Merge "Fall-through for ActionModes with ToolbarActionBar" into lmp-dev
diff --git a/Android.mk b/Android.mk
index 273f5f6..52c2d16 100644
--- a/Android.mk
+++ b/Android.mk
@@ -630,7 +630,7 @@
     -since $(SRC_API_DIR)/18.txt 18 \
     -since $(SRC_API_DIR)/19.txt 19 \
     -since $(SRC_API_DIR)/20.txt 20 \
-		-werror -hide 113 \
+		-werror -hide 111 -hide 113 \
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
 framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
diff --git a/api/current.txt b/api/current.txt
index 4fbebf8..b2a73fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5380,6 +5380,7 @@
 
   public class DevicePolicyManager {
     method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
+    method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
     method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
     method public void addUserRestriction(android.content.ComponentName, java.lang.String);
     method public void clearCrossProfileIntentFilters(android.content.ComponentName);
@@ -5396,6 +5397,7 @@
     method public boolean getBlockUninstall(android.content.ComponentName, java.lang.String);
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+    method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
@@ -5429,6 +5431,7 @@
     method public boolean isProfileOwnerApp(java.lang.String);
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
+    method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
     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);
@@ -5734,6 +5737,7 @@
     method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo);
     method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo);
     method protected void onProvidersChanged();
+    method public final void startAppWidgetConfigureActivityForResult(android.app.Activity, android.content.Intent, int);
     method public void startListening();
     method public void stopListening();
   }
@@ -5756,10 +5760,12 @@
   public class AppWidgetManager {
     method public boolean bindAppWidgetIdIfAllowed(int, android.content.ComponentName);
     method public boolean bindAppWidgetIdIfAllowed(int, android.content.ComponentName, android.os.Bundle);
+    method public boolean bindAppWidgetIdIfAllowed(int, android.os.UserHandle, android.content.ComponentName, android.os.Bundle);
     method public int[] getAppWidgetIds(android.content.ComponentName);
     method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
     method public android.os.Bundle getAppWidgetOptions(int);
     method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();
+    method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProvidersForProfiles(android.os.UserHandle[]);
     method public static android.appwidget.AppWidgetManager getInstance(android.content.Context);
     method public void notifyAppWidgetViewDataChanged(int[], int);
     method public void notifyAppWidgetViewDataChanged(int, int);
@@ -5784,6 +5790,7 @@
     field public static final java.lang.String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds";
     field public static final java.lang.String EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions";
     field public static final java.lang.String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
+    field public static final java.lang.String EXTRA_APPWIDGET_PROVIDER_PROFILE = "appWidgetProviderProfile";
     field public static final java.lang.String EXTRA_CUSTOM_EXTRAS = "customExtras";
     field public static final java.lang.String EXTRA_CUSTOM_INFO = "customInfo";
     field public static final java.lang.String EXTRA_HOST_ID = "hostId";
@@ -5812,6 +5819,10 @@
     ctor public AppWidgetProviderInfo(android.os.Parcel);
     method public android.appwidget.AppWidgetProviderInfo clone();
     method public int describeContents();
+    method public final android.os.UserHandle getProfile();
+    method public final android.graphics.drawable.Drawable loadIcon(android.content.Context, int);
+    method public final java.lang.String loadLabel(android.content.pm.PackageManager);
+    method public final android.graphics.drawable.Drawable loadPreviewImage(android.content.Context, int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int RESIZE_BOTH = 3; // 0x3
@@ -5823,15 +5834,15 @@
     field public static final int WIDGET_CATEGORY_RECENTS = 4; // 0x4
     field public int autoAdvanceViewId;
     field public android.content.ComponentName configure;
-    field public int icon;
+    field public deprecated int icon;
     field public int initialKeyguardLayout;
     field public int initialLayout;
-    field public java.lang.String label;
+    field public deprecated java.lang.String label;
     field public int minHeight;
     field public int minResizeHeight;
     field public int minResizeWidth;
     field public int minWidth;
-    field public int previewImage;
+    field public deprecated int previewImage;
     field public android.content.ComponentName provider;
     field public int resizeMode;
     field public int updatePeriodMillis;
@@ -7277,6 +7288,7 @@
     field public static final java.lang.String ACCOUNT_SERVICE = "account";
     field public static final java.lang.String ACTIVITY_SERVICE = "activity";
     field public static final java.lang.String ALARM_SERVICE = "alarm";
+    field public static final java.lang.String APPWIDGET_SERVICE = "appwidget";
     field public static final java.lang.String APP_OPS_SERVICE = "appops";
     field public static final java.lang.String AUDIO_SERVICE = "audio";
     field public static final java.lang.String BATTERY_SERVICE = "batterymanager";
@@ -13235,6 +13247,7 @@
     method public android.view.Display getDisplay();
     method public android.view.Surface getSurface();
     method public void release();
+    method public void resize(int, int, int);
     method public void setSurface(android.view.Surface);
   }
 
@@ -15118,6 +15131,7 @@
     method public long getLong(java.lang.String);
     method public android.media.Rating getRating(java.lang.String);
     method public java.lang.String getString(java.lang.String);
+    method public java.lang.CharSequence getText(java.lang.String);
     method public java.util.Set<java.lang.String> keySet();
     method public int size();
     method public void writeToParcel(android.os.Parcel, int);
@@ -15134,6 +15148,11 @@
     field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
     field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
     field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION = "android.media.metadata.DISPLAY_DESCRIPTION";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI = "android.media.metadata.DISPLAY_ICON_URI";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE = "android.media.metadata.DISPLAY_SUBTITLE";
+    field public static final java.lang.String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
     field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
     field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
     field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
@@ -15153,6 +15172,7 @@
     method public android.media.MediaMetadata.Builder putLong(java.lang.String, long);
     method public android.media.MediaMetadata.Builder putRating(java.lang.String, android.media.Rating);
     method public android.media.MediaMetadata.Builder putString(java.lang.String, java.lang.String);
+    method public android.media.MediaMetadata.Builder putText(java.lang.String, java.lang.CharSequence);
   }
 
   public abstract deprecated class MediaMetadataEditor {
@@ -16849,9 +16869,8 @@
 package android.media.tv {
 
   public final class TvContentRating {
-    method public static android.media.tv.TvContentRating createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String...);
+    method public static android.media.tv.TvContentRating createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String...);
     method public java.lang.String flattenToString();
-    method public java.lang.String getCountry();
     method public java.lang.String getDomain();
     method public java.lang.String getMainRating();
     method public java.lang.String getRatingSystem();
@@ -17038,22 +17057,6 @@
     method public void onInputStateChanged(java.lang.String, int);
   }
 
-  public abstract class TvInputPassthroughWrapperService extends android.media.tv.TvInputService {
-    ctor public TvInputPassthroughWrapperService();
-    method public abstract java.lang.String getPassthroughInputId(java.lang.String);
-    method public abstract android.media.tv.TvInputPassthroughWrapperService.PassthroughWrapperSession onCreatePassthroughWrapperSession();
-    method public final android.media.tv.TvInputService.Session onCreateSession(java.lang.String);
-  }
-
-  public abstract class TvInputPassthroughWrapperService.PassthroughWrapperSession extends android.media.tv.TvInputService.Session {
-    ctor public TvInputPassthroughWrapperService.PassthroughWrapperSession();
-    method public abstract void onPassthroughSessionCreationFailed();
-    method public abstract void onPassthroughSessionReleased();
-    method public abstract void onPassthroughVideoAvailable();
-    method public abstract void onPassthroughVideoUnavailable(int);
-    method public final boolean onSetSurface(android.view.Surface);
-  }
-
   public abstract class TvInputService extends android.app.Service {
     ctor public TvInputService();
     method public final android.os.IBinder onBind(android.content.Intent);
@@ -17062,13 +17065,21 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
   }
 
+  public abstract class TvInputService.HardwareSession extends android.media.tv.TvInputService.Session {
+    ctor public TvInputService.HardwareSession();
+    method public abstract java.lang.String getHardwareInputId();
+    method public void onHardwareVideoAvailable();
+    method public void onHardwareVideoUnavailable(int);
+    method public final boolean onSetSurface(android.view.Surface);
+  }
+
   public abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session();
     method public void notifyChannelRetuned(android.net.Uri);
     method public void notifyContentAllowed();
     method public void notifyContentBlocked(android.media.tv.TvContentRating);
-    method public void notifyTrackInfoChanged(java.util.List<android.media.tv.TvTrackInfo>);
-    method public void notifyTrackSelectionChanged(java.util.List<android.media.tv.TvTrackInfo>);
+    method public void notifyTrackSelected(int, java.lang.String);
+    method public void notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>);
     method public void notifyVideoAvailable();
     method public void notifyVideoUnavailable(int);
     method public android.view.View onCreateOverlayView();
@@ -17078,7 +17089,7 @@
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
     method public boolean onKeyUp(int, android.view.KeyEvent);
     method public abstract void onRelease();
-    method public boolean onSelectTrack(android.media.tv.TvTrackInfo);
+    method public boolean onSelectTrack(int, java.lang.String);
     method public abstract void onSetCaptionEnabled(boolean);
     method public abstract void onSetStreamVolume(float);
     method public abstract boolean onSetSurface(android.view.Surface);
@@ -17087,7 +17098,6 @@
     method public boolean onTrackballEvent(android.view.MotionEvent);
     method public abstract boolean onTune(android.net.Uri);
     method public void onUnblockContent(android.media.tv.TvContentRating);
-    method public boolean onUnselectTrack(android.media.tv.TvTrackInfo);
     method public void setOverlayViewEnabled(boolean);
   }
 
@@ -17096,6 +17106,7 @@
     method public final int getAudioChannelCount();
     method public final int getAudioSampleRate();
     method public final android.os.Bundle getExtra();
+    method public final java.lang.String getId();
     method public final java.lang.String getLanguage();
     method public final int getType();
     method public final int getVideoHeight();
@@ -17108,7 +17119,7 @@
   }
 
   public static final class TvTrackInfo.Builder {
-    ctor public TvTrackInfo.Builder(int);
+    ctor public TvTrackInfo.Builder(int, java.lang.String);
     method public android.media.tv.TvTrackInfo build();
     method public final android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
     method public final android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
@@ -17123,18 +17134,17 @@
     ctor public TvView(android.content.Context, android.util.AttributeSet);
     ctor public TvView(android.content.Context, android.util.AttributeSet, int);
     method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
-    method public java.util.List<android.media.tv.TvTrackInfo> getSelectedTracks();
-    method public java.util.List<android.media.tv.TvTrackInfo> getTracks();
+    method public java.lang.String getSelectedTrack(int);
+    method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
     method protected void onLayout(boolean, int, int, int, int);
     method public boolean onUnhandledInputEvent(android.view.InputEvent);
     method public void reset();
-    method public void selectTrack(android.media.tv.TvTrackInfo);
+    method public void selectTrack(int, java.lang.String);
     method public void setCaptionEnabled(boolean);
     method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
     method public void setStreamVolume(float);
     method public void setTvInputListener(android.media.tv.TvView.TvInputListener);
     method public void tune(java.lang.String, android.net.Uri);
-    method public void unselectTrack(android.media.tv.TvTrackInfo);
     field public static final int ERROR_INPUT_DISCONNECTED = 1; // 0x1
     field public static final int ERROR_INPUT_NOT_CONNECTED = 0; // 0x0
   }
@@ -17149,8 +17159,8 @@
     method public void onContentAllowed(java.lang.String);
     method public void onContentBlocked(java.lang.String, android.media.tv.TvContentRating);
     method public void onError(java.lang.String, int);
-    method public void onTrackInfoChanged(java.lang.String, java.util.List<android.media.tv.TvTrackInfo>);
-    method public void onTrackSelectionChanged(java.lang.String, java.util.List<android.media.tv.TvTrackInfo>);
+    method public void onTrackSelected(java.lang.String, int, java.lang.String);
+    method public void onTracksChanged(java.lang.String, java.util.List<android.media.tv.TvTrackInfo>);
     method public void onVideoAvailable(java.lang.String);
     method public void onVideoSizeChanged(java.lang.String, int, int);
     method public void onVideoUnavailable(java.lang.String, int);
diff --git a/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java b/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java
index dd45d39..5ea7352 100644
--- a/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java
+++ b/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java
@@ -150,7 +150,7 @@
             IBinder binder = ServiceManager.getService(Context.APPWIDGET_SERVICE);
             IAppWidgetService appWidgetService = IAppWidgetService.Stub.asInterface(binder);
             try {
-                appWidgetService.setBindAppWidgetPermission(mPackageName, mGranted, mUserId);
+                appWidgetService.setBindAppWidgetPermission(mPackageName, mUserId, mGranted);
             } catch (RemoteException re) {
                 re.printStackTrace();
             }
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 7757b91c..d9faf4c 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -191,7 +191,7 @@
         @Override
         public void onMetadataChanged(MediaMetadata metadata) {
             String mmString = metadata == null ? null : "title=" + metadata
-                    .getString(MediaMetadata.METADATA_KEY_TITLE);
+                    .getDescription();
             System.out.println("onMetadataChanged " + mmString);
         }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index caadecb..a52186a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -459,15 +459,15 @@
             null, //POST_NOTIFICATION
             null, //NEIGHBORING_CELLS
             null, //CALL_PHONE
-            null, //READ_SMS
-            null, //WRITE_SMS
-            null, //RECEIVE_SMS
-            null, //RECEIVE_EMERGECY_SMS
-            null, //RECEIVE_MMS
+            UserManager.DISALLOW_SMS, //READ_SMS
+            UserManager.DISALLOW_SMS, //WRITE_SMS
+            UserManager.DISALLOW_SMS, //RECEIVE_SMS
+            null, //RECEIVE_EMERGENCY_SMS
+            UserManager.DISALLOW_SMS, //RECEIVE_MMS
             null, //RECEIVE_WAP_PUSH
-            null, //SEND_SMS
-            null, //READ_ICC_SMS
-            null, //WRITE_ICC_SMS
+            UserManager.DISALLOW_SMS, //SEND_SMS
+            UserManager.DISALLOW_SMS, //READ_ICC_SMS
+            UserManager.DISALLOW_SMS, //WRITE_ICC_SMS
             null, //WRITE_SETTINGS
             UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
             null, //ACCESS_NOTIFICATIONS
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 18ba8c4..c75c8b7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1384,7 +1384,18 @@
     public void replacePreferredActivity(IntentFilter filter,
                                          int match, ComponentName[] set, ComponentName activity) {
         try {
-            mPM.replacePreferredActivity(filter, match, set, activity);
+            mPM.replacePreferredActivity(filter, match, set, activity, UserHandle.myUserId());
+        } catch (RemoteException e) {
+            // Should never happen!
+        }
+    }
+
+    @Override
+    public void replacePreferredActivityAsUser(IntentFilter filter,
+                                         int match, ComponentName[] set, ComponentName activity,
+                                         int userId) {
+        try {
+            mPM.replacePreferredActivity(filter, match, set, activity, userId);
         } catch (RemoteException e) {
             // Should never happen!
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5f606a61..4cf8cb4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -18,10 +18,12 @@
 
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageStatsManager;
+import android.appwidget.AppWidgetManager;
 import android.os.Build;
 
 import android.service.persistentdata.IPersistentDataBlockService;
 import android.service.persistentdata.PersistentDataBlockManager;
+import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.util.Preconditions;
 
@@ -148,7 +150,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
-import com.android.internal.appwidget.IAppWidgetService.Stub;
 import com.android.internal.os.IDropBoxManagerService;
 import com.android.internal.telecomm.ITelecommService;
 
@@ -771,6 +772,12 @@
                     return new MediaProjectionManager(ctx);
                 }
         });
+
+        registerService(APPWIDGET_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(APPWIDGET_SERVICE);
+                return new AppWidgetManager(ctx, IAppWidgetService.Stub.asInterface(b));
+            }});
     }
 
     static ContextImpl getImpl(Context context) {
@@ -2100,6 +2107,25 @@
     }
 
     @Override
+    public Context createApplicationContext(ApplicationInfo application, int flags)
+            throws NameNotFoundException {
+        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
+                flags | CONTEXT_REGISTER_PACKAGE);
+        if (pi != null) {
+            final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
+                    new UserHandle(UserHandle.getUserId(application.uid)), restricted,
+                    mDisplay, mOverrideConfiguration);
+            if (c.mResources != null) {
+                return c;
+            }
+        }
+
+        throw new PackageManager.NameNotFoundException(
+                "Application package " + application.packageName + " not found");
+    }
+
+    @Override
     public Context createPackageContext(String packageName, int flags)
             throws NameNotFoundException {
         return createPackageContextAsUser(packageName, flags,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 21525bc..797a0a0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1707,28 +1707,6 @@
         }
     }
 
-    /** {@hide} */
-    public void setUser(UserHandle user) {
-        if (user.getIdentifier() == UserHandle.USER_ALL) {
-            user = UserHandle.OWNER;
-        }
-        if (tickerView != null) {
-            tickerView.setUser(user);
-        }
-        if (contentView != null) {
-            contentView.setUser(user);
-        }
-        if (bigContentView != null) {
-            bigContentView.setUser(user);
-        }
-        if (headsUpContentView != null) {
-            headsUpContentView.setUser(user);
-        }
-        if (publicVersion != null) {
-            publicVersion.setUser(user);
-        }
-    }
-
     /**
      * @hide
      */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e28f00c..ca6b1e8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -20,6 +20,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.app.Activity;
+import android.app.admin.IDevicePolicyManager;
 import android.content.AbstractRestrictionsProvider;
 import android.content.ComponentName;
 import android.content.Context;
@@ -28,8 +29,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.RestrictionsManager;
-import android.media.AudioService;
 import android.net.ProxyInfo;
 import android.os.Bundle;
 import android.os.Handler;
@@ -40,7 +39,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.service.trust.TrustAgentService;
 import android.util.Log;
 
 import com.android.org.conscrypt.TrustedCertificateStore;
@@ -55,6 +53,7 @@
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -3079,4 +3078,85 @@
         }
         return false;
     }
+
+    /**
+     * Called by the profile owner to enable widget providers from a given package
+     * to be available in the parent profile. As a result the user will be able to
+     * add widgets from the white-listed package running under the profile to a widget
+     * host which runs under the device owner, for example the home screen. Note that
+     * a package may have zero or more provider components, where each component
+     * provides a different widget type.
+     * <p>
+     * <strong>Note:</strong> By default no widget provider package is white-listed.
+     * </p>
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName The package from which widget providers are white-listed.
+     * @return Whether the package was added.
+     *
+     * @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String)
+     * @see #getCrossProfileWidgetProviders(android.content.ComponentName)
+     */
+    public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+        if (mService != null) {
+            try {
+                return mService.addCrossProfileWidgetProvider(admin, packageName);
+            } catch (RemoteException re) {
+                Log.w(TAG, "Error calling addCrossProfileWidgetProvider", re);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called by the profile owner to disable widget providers from a given package
+     * to be available in the parent profile. For this method to take effect the
+     * package should have been added via {@link #addCrossProfileWidgetProvider(
+     * android.content.ComponentName, String)}.
+     * <p>
+     * <strong>Note:</strong> By default no widget provider package is white-listed.
+     * </p>
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName The package from which widget providers are no longer
+     *     white-listed.
+     * @return Whether the package was removed.
+     *
+     * @see #addCrossProfileWidgetProvider(android.content.ComponentName, String)
+     * @see #getCrossProfileWidgetProviders(android.content.ComponentName)
+     */
+    public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+        if (mService != null) {
+            try {
+                return mService.removeCrossProfileWidgetProvider(admin, packageName);
+            } catch (RemoteException re) {
+                Log.w(TAG, "Error calling removeCrossProfileWidgetProvider", re);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called by the profile owner to query providers from which packages are
+     * available in the parent profile.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return The white-listed package list.
+     *
+     * @see #addCrossProfileWidgetProvider(android.content.ComponentName, String)
+     * @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String)
+     */
+    public List<String> getCrossProfileWidgetProviders(ComponentName admin) {
+        if (mService != null) {
+            try {
+                List<String> providers = mService.getCrossProfileWidgetProviders(admin);
+                if (providers != null) {
+                    return providers;
+                }
+            } catch (RemoteException re) {
+                Log.w(TAG, "Error calling getCrossProfileWidgetProviders", re);
+            }
+        }
+        return Collections.emptyList();
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
new file mode 100644
index 0000000..edd8199
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import java.util.List;
+
+/**
+ * Device policy manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class DevicePolicyManagerInternal {
+
+    /**
+     * Gets the packages whose widget providers are white-listed to be
+     * available in the parent user.
+     *
+     * @param profileId The profile id.
+     * @return The list of packages if such or empty list if there are
+     *    no white-listed packages or the profile id is not a managed
+     *    profile.
+     */
+    public abstract List<String> getCrossProfileWidgetProviders(int profileId);
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6ce737a..8954c0d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -175,4 +175,7 @@
     void setTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, in List<String> features, int userId);
     List<String> getTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, int userId);
 
+    boolean addCrossProfileWidgetProvider(in ComponentName admin, String packageName);
+    boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
+    List<String> getCrossProfileWidgetProviders(in ComponentName admin);
 }
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 84d3835..e7b68f5 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -19,7 +19,11 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -61,32 +65,30 @@
     private OnClickHandler mOnClickHandler;
 
     class Callbacks extends IAppWidgetHost.Stub {
-        public void updateAppWidget(int appWidgetId, RemoteViews views, int userId) {
+        public void updateAppWidget(int appWidgetId, RemoteViews views) {
             if (isLocalBinder() && views != null) {
                 views = views.clone();
-                views.setUser(new UserHandle(userId));
             }
-            Message msg = mHandler.obtainMessage(HANDLE_UPDATE, appWidgetId, userId, views);
+            Message msg = mHandler.obtainMessage(HANDLE_UPDATE, appWidgetId, 0, views);
             msg.sendToTarget();
         }
 
-        public void providerChanged(int appWidgetId, AppWidgetProviderInfo info, int userId) {
+        public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) {
             if (isLocalBinder() && info != null) {
                 info = info.clone();
             }
             Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED,
-                    appWidgetId, userId, info);
+                    appWidgetId, 0, info);
             msg.sendToTarget();
         }
 
-        public void providersChanged(int userId) {
-            Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED, userId, 0);
-            msg.sendToTarget();
+        public void providersChanged() {
+            mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED).sendToTarget();
         }
 
-        public void viewDataChanged(int appWidgetId, int viewId, int userId) {
+        public void viewDataChanged(int appWidgetId, int viewId) {
             Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED,
-                    appWidgetId, viewId, userId);
+                    appWidgetId, viewId);
             msg.sendToTarget();
         }
     }
@@ -99,7 +101,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case HANDLE_UPDATE: {
-                    updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj, msg.arg2);
+                    updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
                     break;
                 }
                 case HANDLE_PROVIDER_CHANGED: {
@@ -111,7 +113,7 @@
                     break;
                 }
                 case HANDLE_VIEW_DATA_CHANGED: {
-                    viewDataChanged(msg.arg1, msg.arg2, (Integer) msg.obj);
+                    viewDataChanged(msg.arg1, msg.arg2);
                     break;
                 }
             }
@@ -151,25 +153,20 @@
     public void startListening() {
         int[] updatedIds;
         ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
-
-        final int userId = mContext.getUserId();
         try {
             if (mPackageName == null) {
                 mPackageName = mContext.getPackageName();
             }
-            updatedIds = sService.startListening(
-                    mCallbacks, mPackageName, mHostId, updatedViews, userId);
+            updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId,
+                    updatedViews);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
 
         final int N = updatedIds.length;
-        for (int i=0; i<N; i++) {
-            if (updatedViews.get(i) != null) {
-                updatedViews.get(i).setUser(new UserHandle(userId));
-            }
-            updateAppWidgetView(updatedIds[i], updatedViews.get(i), userId);
+        for (int i = 0; i < N; i++) {
+            updateAppWidgetView(updatedIds[i], updatedViews.get(i));
         }
     }
 
@@ -179,7 +176,7 @@
      */
     public void stopListening() {
         try {
-            sService.stopListening(mHostId, mContext.getUserId());
+            sService.stopListening(mPackageName, mHostId);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -200,7 +197,7 @@
             if (mPackageName == null) {
                 mPackageName = mContext.getPackageName();
             }
-            return sService.allocateAppWidgetId(mPackageName, mHostId, mContext.getUserId());
+            return sService.allocateAppWidgetId(mPackageName, mHostId);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -208,18 +205,39 @@
     }
 
     /**
-     * Get a appWidgetId for a host in the given package.
+     * Starts an app widget provider configure activity for result on behalf of the caller.
+     * Use this method if the provider is in another profile as you are not allowed to start
+     * an activity in another profile. The provided intent must have action {@link
+     * AppWidgetManager#ACTION_APPWIDGET_CONFIGURE}. The widget id must be specified by
+     * the {@link AppWidgetManager#EXTRA_APPWIDGET_ID} extra. The provider configure
+     * activity must be specified as the component name of the intent via {@link
+     * Intent#setComponent(android.content.ComponentName)}. The user profile under which
+     * the provider operates is specified via the {@link
+     * AppWidgetManager#EXTRA_APPWIDGET_PROVIDER_PROFILE} extra. If a user profile is
+     * not provided, the current user is used. Finally, you can optionally provide a
+     * request code that is returned in {@link Activity#onActivityResult(int, int,
+     * android.content.Intent)}.
      *
-     * @return a appWidgetId
-     * @hide
+     * @param activity The activity from which to start the configure one.
+     * @param intent Properly populated intent for launching the configuration activity.
+     * @param requestCode Optional request code retuned with the result.
+     *
+     * @throws android.content.ActivityNotFoundException If the activity is not found.
+     *
+     * @see AppWidgetProviderInfo#getProfile()
      */
-    public static int allocateAppWidgetIdForPackage(int hostId, int userId, String packageName) {
-        checkCallerIsSystem();
+    public final void startAppWidgetConfigureActivityForResult(Activity activity, Intent intent,
+            int requestCode) {
         try {
-            if (sService == null) {
-                bindService();
+            IntentSender intentSender = sService.createAppWidgetConfigIntentSender(
+                    mContext.getPackageName(), intent);
+            if (intentSender != null) {
+                activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0);
+            } else {
+                throw new ActivityNotFoundException();
             }
-            return sService.allocateAppWidgetId(packageName, hostId, userId);
+        } catch (IntentSender.SendIntentException e) {
+            throw new ActivityNotFoundException();
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
@@ -235,20 +253,12 @@
             if (sService == null) {
                 bindService();
             }
-            return sService.getAppWidgetIdsForHost(mHostId, mContext.getUserId());
+            return sService.getAppWidgetIdsForHost(mContext.getPackageName(), mHostId);
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
     }
 
-    private static void checkCallerIsSystem() {
-        int uid = Process.myUid();
-        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
-            return;
-        }
-        throw new SecurityException("Disallowed call for uid " + uid);
-    }
-
     private boolean isLocalBinder() {
         return Process.myPid() == Binder.getCallingPid();
     }
@@ -260,7 +270,7 @@
         synchronized (mViews) {
             mViews.remove(appWidgetId);
             try {
-                sService.deleteAppWidgetId(appWidgetId, mContext.getUserId());
+                sService.deleteAppWidgetId(mContext.getPackageName(), appWidgetId);
             }
             catch (RemoteException e) {
                 throw new RuntimeException("system server dead?", e);
@@ -269,22 +279,6 @@
     }
 
     /**
-     * Stop listening to changes for this AppWidget.
-     * @hide
-     */
-    public static void deleteAppWidgetIdForSystem(int appWidgetId, int userId) {
-        checkCallerIsSystem();
-        try {
-            if (sService == null) {
-                bindService();
-            }
-            sService.deleteAppWidgetId(appWidgetId, userId);
-        } catch (RemoteException e) {
-            throw new RuntimeException("system server dead?", e);
-        }
-    }
-
-    /**
      * Remove all records about this host from the AppWidget manager.
      * <ul>
      *   <li>Call this when initializing your database, as it might be because of a data wipe.</li>
@@ -294,7 +288,7 @@
      */
     public void deleteHost() {
         try {
-            sService.deleteHost(mHostId, mContext.getUserId());
+            sService.deleteHost(mContext.getPackageName(), mHostId);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -310,16 +304,8 @@
      * </ul>
      */
     public static void deleteAllHosts() {
-        deleteAllHosts(UserHandle.myUserId());
-    }
-
-    /**
-     * Private method containing a userId
-     * @hide
-     */
-    public static void deleteAllHosts(int userId) {
         try {
-            sService.deleteAllHosts(userId);
+            sService.deleteAllHosts();
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -332,9 +318,7 @@
      */
     public final AppWidgetHostView createView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        final int userId = mContext.getUserId();
         AppWidgetHostView view = onCreateView(mContext, appWidgetId, appWidget);
-        view.setUserId(userId);
         view.setOnClickHandler(mOnClickHandler);
         view.setAppWidget(appWidgetId, appWidget);
         synchronized (mViews) {
@@ -342,10 +326,7 @@
         }
         RemoteViews views;
         try {
-            views = sService.getAppWidgetViews(appWidgetId, userId);
-            if (views != null) {
-                views.setUser(new UserHandle(mContext.getUserId()));
-            }
+            views = sService.getAppWidgetViews(mPackageName, appWidgetId);
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
@@ -397,7 +378,7 @@
         // Does nothing
     }
 
-    void updateAppWidgetView(int appWidgetId, RemoteViews views, int userId) {
+    void updateAppWidgetView(int appWidgetId, RemoteViews views) {
         AppWidgetHostView v;
         synchronized (mViews) {
             v = mViews.get(appWidgetId);
@@ -407,7 +388,7 @@
         }
     }
 
-    void viewDataChanged(int appWidgetId, int viewId, int userId) {
+    void viewDataChanged(int appWidgetId, int viewId) {
         AppWidgetHostView v;
         synchronized (mViews) {
             v = mViews.get(appWidgetId);
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 700bba8..1ff476e 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -87,7 +87,6 @@
     Bitmap mOld;
     Paint mOldPaint = new Paint();
     private OnClickHandler mOnClickHandler;
-    private UserHandle mUser;
 
     /**
      * Create a host view.  Uses default fade animations.
@@ -115,17 +114,11 @@
     public AppWidgetHostView(Context context, int animationIn, int animationOut) {
         super(context);
         mContext = context;
-        mUser = Process.myUserHandle();
         // We want to segregate the view ids within AppWidgets to prevent
         // problems when those ids collide with view ids in the AppWidgetHost.
         setIsRootNamespace(true);
     }
 
-    /** @hide */
-    public void setUserId(int userId) {
-        mUser = new UserHandle(userId);
-    }
-
     /**
      * Pass the given handler to RemoteViews when updating this widget. Unless this
      * is done immediatly after construction, a call to {@link #updateAppWidget(RemoteViews)}
@@ -380,7 +373,7 @@
         } else {
             // Prepare a local reference to the remote Context so we're ready to
             // inflate any requested LayoutParams.
-            mRemoteContext = getRemoteContext(remoteViews);
+            mRemoteContext = getRemoteContext();
             int layoutId = remoteViews.getLayoutId();
 
             // If our stale view has been prepared to match active, and the new
@@ -466,17 +459,14 @@
      * Build a {@link Context} cloned into another package name, usually for the
      * purposes of reading remote resources.
      */
-    private Context getRemoteContext(RemoteViews views) {
-        // Bail if missing package name
-        final String packageName = views.getPackage();
-        if (packageName == null) return mContext;
-
+    private Context getRemoteContext() {
         try {
             // Return if cloned successfully, otherwise default
-            return mContext.createPackageContextAsUser(packageName, Context.CONTEXT_RESTRICTED,
-                    mUser);
+            return mContext.createApplicationContext(
+                    mInfo.providerInfo.applicationInfo,
+                    Context.CONTEXT_RESTRICTED);
         } catch (NameNotFoundException e) {
-            Log.e(TAG, "Package name " + packageName + " not found");
+            Log.e(TAG, "Package name " +  mInfo.providerInfo.packageName + " not found");
             return mContext;
         }
     }
@@ -548,8 +538,7 @@
 
         try {
             if (mInfo != null) {
-                Context theirContext = mContext.createPackageContextAsUser(
-                        mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED, mUser);
+                Context theirContext = getRemoteContext();
                 mRemoteContext = theirContext;
                 LayoutInflater inflater = (LayoutInflater)
                         theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -572,8 +561,6 @@
             } else {
                 Log.w(TAG, "can't inflate defaultView because mInfo is missing");
             }
-        } catch (PackageManager.NameNotFoundException e) {
-            exception = e;
         } catch (RuntimeException e) {
             exception = e;
         }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index e5bf7d0..e5d1d95 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -21,8 +21,8 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -30,9 +30,8 @@
 
 import com.android.internal.appwidget.IAppWidgetService;
 
-import java.lang.ref.WeakReference;
+import java.util.Collections;
 import java.util.List;
-import java.util.WeakHashMap;
 
 /**
  * Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -45,7 +44,6 @@
  * </div>
  */
 public class AppWidgetManager {
-    static final String TAG = "AppWidgetManager";
 
     /**
      * Activity action to launch from your {@link AppWidgetHost} activity when you want to
@@ -72,9 +70,9 @@
      * <p>
      * When you receive the result from the AppWidget pick activity, if the resultCode is
      * {@link android.app.Activity#RESULT_OK}, an AppWidget has been selected.  You should then
-     * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its configuration
-     * activity.  If {@link android.app.Activity#RESULT_CANCELED} is returned, you should delete
-     * the appWidgetId.
+     * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its
+     * configuration activity.  If {@link android.app.Activity#RESULT_CANCELED} is returned, you
+     * should delete the appWidgetId.
      *
      * @see #ACTION_APPWIDGET_CONFIGURE
      */
@@ -103,6 +101,12 @@
      *     <td>The BroadcastReceiver that will be the AppWidget provider for this AppWidget.
      *     </td>
      *  </tr>
+     *  <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_PROVIDER_PROFILE}</td>
+     *     <td>An optional handle to a user profile under which runs the provider
+     *     for this AppWidget.
+     *     </td>
+     *  </tr>
      * </table>
      *
      * <p>
@@ -119,8 +123,7 @@
      * {@link android.app.Activity#RESULT_OK}, the AppWidget has been bound.  You should then
      * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its
      * configuration activity.  If {@link android.app.Activity#RESULT_CANCELED} is returned, you
-     * should delete
-     * the appWidgetId.
+     * should delete the appWidgetId.
      *
      * @see #ACTION_APPWIDGET_CONFIGURE
      *
@@ -130,7 +133,8 @@
     /**
      * Sent when it is time to configure your AppWidget while it is being added to a host.
      * This action is not sent as a broadcast to the AppWidget provider, but as a startActivity
-     * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo meta-data}.
+     * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo
+     * meta-data}.
      *
      * <p>
      * The intent will contain the following extras:
@@ -145,7 +149,8 @@
      * {@link android.app.Activity#setResult Activity.setResult()}, the AppWidget will be added,
      * and you will receive an {@link #ACTION_APPWIDGET_UPDATE} broadcast for this AppWidget.
      * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add
-     * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED} broadcast.
+     * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED}
+     * broadcast.
      */
     public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
 
@@ -188,7 +193,9 @@
 
     /**
      * An intent extra which points to a bundle of extra information for a particular widget id.
-     * In particular this bundle can contain EXTRA_APPWIDGET_WIDTH and EXTRA_APPWIDGET_HEIGHT.
+     * In particular this bundle can contain {@link #OPTION_APPWIDGET_MIN_WIDTH},
+     * {@link #OPTION_APPWIDGET_MIN_HEIGHT}, {@link #OPTION_APPWIDGET_MAX_WIDTH},
+     * {@link #OPTION_APPWIDGET_MAX_HEIGHT}.
      */
     public static final String EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions";
 
@@ -203,11 +210,19 @@
     /**
      * An intent extra that contains the component name of a AppWidget provider.
      * <p>
-     * The value will be an ComponentName.
+     * The value will be an {@link android.content.ComponentName}.
      */
     public static final String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
 
     /**
+     * An intent extra that contains the user handle of the profile under
+     * which an AppWidget provider is registered.
+     * <p>
+     * The value will be a {@link android.os.UserHandle}.
+     */
+    public static final String EXTRA_APPWIDGET_PROVIDER_PROFILE = "appWidgetProviderProfile";
+
+    /**
      * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
      * {@link AppWidgetProviderInfo} objects to mix in to the list of AppWidgets that are
      * installed.  (This is how the launcher shows the search widget).
@@ -295,12 +310,12 @@
     public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
 
     /**
-     * Sent when an instance of an AppWidget is removed from the last host.
+     * Sent when the last AppWidget of this provider is removed from the last host.
      *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      *
-     * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
+     * @see AppWidgetProvider#onEnabled AppWidgetProvider.onDisabled(Context context)
      */
     public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
 
@@ -403,46 +418,36 @@
      */
     public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
 
-    static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache =
-        new WeakHashMap<Context, WeakReference<AppWidgetManager>>();
-    static IAppWidgetService sService;
+    private final String mPackageName;
 
-    Context mContext;
+    private final IAppWidgetService mService;
 
-    private DisplayMetrics mDisplayMetrics;
+    private final DisplayMetrics mDisplayMetrics;
 
     /**
      * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
      * Context} object.
      */
     public static AppWidgetManager getInstance(Context context) {
-        synchronized (sManagerCache) {
-            if (sService == null) {
-                IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
-                sService = IAppWidgetService.Stub.asInterface(b);
-            }
-
-            WeakReference<AppWidgetManager> ref = sManagerCache.get(context);
-            AppWidgetManager result = null;
-            if (ref != null) {
-                result = ref.get();
-            }
-            if (result == null) {
-                result = new AppWidgetManager(context);
-                sManagerCache.put(context, new WeakReference<AppWidgetManager>(result));
-            }
-            return result;
-        }
+        return (AppWidgetManager) context.getSystemService(Context.APPWIDGET_SERVICE);
     }
 
-    private AppWidgetManager(Context context) {
-        mContext = context;
+    /**
+     * Creates a new instance.
+     *
+     * @param context The current context in which to operate.
+     * @param service The backing system service.
+     * @hide
+     */
+    public AppWidgetManager(Context context, IAppWidgetService service) {
+        mPackageName = context.getPackageName();
+        mService = service;
         mDisplayMetrics = context.getResources().getDisplayMetrics();
     }
 
     /**
      * Set the RemoteViews to use for the specified appWidgetIds.
-     *
+     * <p>
      * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
      * contain a complete representation of the widget. For performing partial widget updates, see
      * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}.
@@ -456,12 +461,15 @@
      * The total Bitmap memory used by the RemoteViews object cannot exceed that required to
      * fill the screen 1.5 times, ie. (screen width x screen height x 4 x 1.5) bytes.
      *
-     * @param appWidgetIds     The AppWidget instances for which to set the RemoteViews.
-     * @param views         The RemoteViews object to show.
+     * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
+     * @param views The RemoteViews object to show.
      */
     public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
+        if (mService == null) {
+            return;
+        }
         try {
-            sService.updateAppWidgetIds(appWidgetIds, views, mContext.getUserId());
+            mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -470,18 +478,21 @@
 
     /**
      * Update the extras for a given widget instance.
-     *
+     * <p>
      * The extras can be used to embed additional information about this widget to be accessed
      * by the associated widget's AppWidgetProvider.
      *
      * @see #getAppWidgetOptions(int)
      *
-     * @param appWidgetId    The AppWidget instances for which to set the RemoteViews.
-     * @param options         The options to associate with this widget
+     * @param appWidgetId The AppWidget instances for which to set the RemoteViews.
+     * @param options The options to associate with this widget
      */
     public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
+        if (mService == null) {
+            return;
+        }
         try {
-            sService.updateAppWidgetOptions(appWidgetId, options, mContext.getUserId());
+            mService.updateAppWidgetOptions(mPackageName, appWidgetId, options);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -490,18 +501,21 @@
 
     /**
      * Get the extras associated with a given widget instance.
-     *
+     * <p>
      * The extras can be used to embed additional information about this widget to be accessed
      * by the associated widget's AppWidgetProvider.
      *
      * @see #updateAppWidgetOptions(int, Bundle)
      *
-     * @param appWidgetId     The AppWidget instances for which to set the RemoteViews.
-     * @return                The options associated with the given widget instance.
+     * @param appWidgetId The AppWidget instances for which to set the RemoteViews.
+     * @return The options associated with the given widget instance.
      */
     public Bundle getAppWidgetOptions(int appWidgetId) {
+        if (mService == null) {
+            return Bundle.EMPTY;
+        }
         try {
-            return sService.getAppWidgetOptions(appWidgetId, mContext.getUserId());
+            return mService.getAppWidgetOptions(mPackageName, appWidgetId);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -510,7 +524,7 @@
 
     /**
      * Set the RemoteViews to use for the specified appWidgetId.
-     *
+     * <p>
      * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
      * contain a complete representation of the widget. For performing partial widget updates, see
      * {@link #partiallyUpdateAppWidget(int, RemoteViews)}.
@@ -528,12 +542,15 @@
      * @param views         The RemoteViews object to show.
      */
     public void updateAppWidget(int appWidgetId, RemoteViews views) {
+        if (mService == null) {
+            return;
+        }
         updateAppWidget(new int[] { appWidgetId }, views);
     }
 
     /**
      * Perform an incremental update or command on the widget(s) specified by appWidgetIds.
-     *
+     * <p>
      * This update  differs from {@link #updateAppWidget(int[], RemoteViews)} in that the
      * RemoteViews object which is passed is understood to be an incomplete representation of the
      * widget, and hence does not replace the cached representation of the widget. As of API
@@ -556,8 +573,11 @@
      * @param views            The RemoteViews object containing the incremental update / command.
      */
     public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) {
+        if (mService == null) {
+            return;
+        }
         try {
-            sService.partiallyUpdateAppWidgetIds(appWidgetIds, views, mContext.getUserId());
+            mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
@@ -565,7 +585,7 @@
 
     /**
      * Perform an incremental update or command on the widget specified by appWidgetId.
-     *
+     * <p>
      * This update  differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews
      * object which is passed is understood to be an incomplete representation of the widget, and
      * hence is not cached by the AppWidgetService. Note that because these updates are not cached,
@@ -588,6 +608,9 @@
      * @param views            The RemoteViews object containing the incremental update / command.
      */
     public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
+        if (mService == null) {
+            return;
+        }
         partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
     }
 
@@ -605,8 +628,11 @@
      * @param views         The RemoteViews object to show.
      */
     public void updateAppWidget(ComponentName provider, RemoteViews views) {
+        if (mService == null) {
+            return;
+        }
         try {
-            sService.updateAppWidgetProvider(provider, views, mContext.getUserId());
+            mService.updateAppWidgetProvider(provider, views);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -621,8 +647,11 @@
      * @param viewId        The collection view id.
      */
     public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+        if (mService == null) {
+            return;
+        }
         try {
-            sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId, mContext.getUserId());
+            mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -637,37 +666,106 @@
      * @param viewId       The collection view id.
      */
     public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
+        if (mService == null) {
+            return;
+        }
         notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId);
     }
 
     /**
+     * Gets the AppWidget providers for the given user profiles. User profiles can only
+     * be the current user or a profile of the current user. For example, the current
+     * user may have a corporate profile. In this case the parent user profile has a
+     * child profile, the corporate one.
+     *
+     * @param profiles The profiles for which to get providers. Passing null is equivaled
+     *         to passing only the current user handle.
+     * @return The intalled providers.
+     *
+     * @see android.os.Process#myUserHandle()
+     * @see android.os.UserManager#getUserProfiles()
+     */
+    public List<AppWidgetProviderInfo> getInstalledProvidersForProfiles(UserHandle[] profiles) {
+        if (mService == null) {
+            return Collections.emptyList();
+        }
+        return getInstalledProvidersForProfiles(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+                profiles);
+    }
+
+    /**
      * Return a list of the AppWidget providers that are currently installed.
      */
     public List<AppWidgetProviderInfo> getInstalledProviders() {
-        return getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+        if (mService == null) {
+            return Collections.emptyList();
+        }
+        return getInstalledProvidersForProfiles(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+                null);
     }
 
     /**
-     * Return a list of the AppWidget providers that are currently installed.
+     * Gets the AppWidget providers for the current user.
      *
      * @param categoryFilter Will only return providers which register as any of the specified
      *        specified categories. See {@link AppWidgetProviderInfo#widgetCategory}.
+     * @return The intalled providers.
+     *
+     * @see android.os.Process#myUserHandle()
+     * @see android.os.UserManager#getUserProfiles()
+     *
      * @hide
      */
     public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
+        if (mService == null) {
+            return Collections.emptyList();
+        }
+        return getInstalledProvidersForProfiles(categoryFilter, null);
+    }
+
+    /**
+     * Gets the AppWidget providers for the given user profiles. User profiles can only
+     * be the current user or a profile of the current user. For example, the current
+     * user may have a corporate profile. In this case the parent user profile has a
+     * child profile, the corporate one.
+     *
+     * @param categoryFilter Will only return providers which register as any of the specified
+     *        specified categories. See {@link AppWidgetProviderInfo#widgetCategory}.
+     * @param profiles Child profiles of the current user which to be queried. The user
+     *        is itself also a profile. If null, the providers only for the current user
+     *        are returned.
+     * @return The intalled providers.
+     *
+     * @see android.os.Process#myUserHandle()
+     * @see android.os.UserManager#getUserProfiles()
+     *
+     * @hide
+     */
+    public List<AppWidgetProviderInfo> getInstalledProvidersForProfiles(int categoryFilter,
+            UserHandle[] profiles) {
+        if (mService == null) {
+            return Collections.emptyList();
+        }
+
+        int[] profileIds = null;
+
+        if (profiles != null) {
+            final int profileCount = profiles.length;
+            profileIds = new int[profileCount];
+            for (int i = 0; i < profileCount; i++) {
+                profileIds[i] = profiles[i].getIdentifier();
+            }
+        }
+
         try {
-            List<AppWidgetProviderInfo> providers = sService.getInstalledProviders(categoryFilter,
-                    mContext.getUserId());
+            List<AppWidgetProviderInfo> providers = mService.getInstalledProviders(categoryFilter,
+                    profileIds);
+            if (providers == null) {
+                return Collections.emptyList();
+            }
             for (AppWidgetProviderInfo info : providers) {
                 // Converting complex to dp.
-                info.minWidth =
-                        TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
-                info.minHeight =
-                        TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
-                info.minResizeWidth =
-                    TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
-                info.minResizeHeight =
-                    TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
+                convertSizesToPixels(info);
             }
             return providers;
         }
@@ -683,19 +781,14 @@
      * you don't have access to that appWidgetId, null is returned.
      */
     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+        if (mService == null) {
+            return null;
+        }
         try {
-            AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId,
-                    mContext.getUserId());
+            AppWidgetProviderInfo info = mService.getAppWidgetInfo(mPackageName, appWidgetId);
             if (info != null) {
                 // Converting complex to dp.
-                info.minWidth =
-                        TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
-                info.minHeight =
-                        TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
-                info.minResizeWidth =
-                    TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
-                info.minResizeHeight =
-                    TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
+                convertSizesToPixels(info);
             }
             return info;
         }
@@ -717,12 +810,10 @@
      * @hide
      */
     public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
-        try {
-            sService.bindAppWidgetId(appWidgetId, provider, null, mContext.getUserId());
+        if (mService == null) {
+            return;
         }
-        catch (RemoteException e) {
-            throw new RuntimeException("system server dead?", e);
-        }
+        bindAppWidgetId(appWidgetId, provider, null);
     }
 
     /**
@@ -741,12 +832,10 @@
      * @hide
      */
     public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
-        try {
-            sService.bindAppWidgetId(appWidgetId, provider, options, mContext.getUserId());
+        if (mService == null) {
+            return;
         }
-        catch (RemoteException e) {
-            throw new RuntimeException("system server dead?", e);
-        }
+        bindAppWidgetIdIfAllowed(appWidgetId, Process.myUserHandle(), provider, options);
     }
 
     /**
@@ -757,22 +846,16 @@
      *         method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to
      *         bind
      *
-     * @param appWidgetId     The AppWidget instance for which to set the RemoteViews.
+     * @param appWidgetId   The AppWidget id under which to bind the provider.
      * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
      *                      provider for this AppWidget.
      * @return true if this component has permission to bind the AppWidget
      */
     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider) {
-        if (mContext == null) {
+        if (mService == null) {
             return false;
         }
-        try {
-            return sService.bindAppWidgetIdIfAllowed(
-                    mContext.getPackageName(), appWidgetId, provider, null, mContext.getUserId());
-        }
-        catch (RemoteException e) {
-            throw new RuntimeException("system server dead?", e);
-        }
+        return bindAppWidgetIdIfAllowed(appWidgetId, UserHandle.myUserId(), provider, null);
     }
 
     /**
@@ -783,7 +866,7 @@
      *         method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to
      *         bind
      *
-     * @param appWidgetId     The AppWidget instance for which to set the RemoteViews.
+     * @param appWidgetId The AppWidget id under which to bind the provider.
      * @param provider      The {@link android.content.BroadcastReceiver} that will be the AppWidget
      *                      provider for this AppWidget.
      * @param options       Bundle containing options for the AppWidget. See also
@@ -793,12 +876,52 @@
      */
     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider,
             Bundle options) {
-        if (mContext == null) {
+        if (mService == null) {
+            return false;
+        }
+        return bindAppWidgetIdIfAllowed(appWidgetId, UserHandle.myUserId(), provider, options);
+    }
+
+    /**
+     * Set the provider for a given appWidgetId if the caller has a permission.
+     * <p>
+     * <strong>Note:</strong> You need the {@link android.Manifest.permission#BIND_APPWIDGET}
+     * permission or the user must have enabled binding widgets always for your component.
+     * Should be used by apps that host widgets. If this method returns false, call {@link
+     * #ACTION_APPWIDGET_BIND} to request permission to bind.
+     * </p>
+     *
+     * @param appWidgetId The AppWidget id under which to bind the provider.
+     * @param user The user id in which the provider resides.
+     * @param provider The component name of the provider.
+     * @param options An optional Bundle containing options for the AppWidget.
+     *
+     * @return true if this component has permission to bind the AppWidget
+     */
+    public boolean bindAppWidgetIdIfAllowed(int appWidgetId, UserHandle user,
+            ComponentName provider, Bundle options) {
+        if (mService == null) {
+            return false;
+        }
+        return bindAppWidgetIdIfAllowed(appWidgetId, user.getIdentifier(), provider, options);
+    }
+
+    /**
+     * Query if a given package was granted permission by the user to bind app widgets
+     *
+     * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
+     *
+     * @param packageName The package for which the permission is being queried
+     * @param userId The user id of the user under which the package runs.
+     * @return true if the package was granted permission by the user to bind app widgets
+     * @hide
+     */
+    public boolean hasBindAppWidgetPermission(String packageName, int userId) {
+        if (mService == null) {
             return false;
         }
         try {
-            return sService.bindAppWidgetIdIfAllowed(mContext.getPackageName(), appWidgetId,
-                    provider, options, mContext.getUserId());
+            return mService.hasBindAppWidgetPermission(packageName, userId);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -815,8 +938,11 @@
      * @hide
      */
     public boolean hasBindAppWidgetPermission(String packageName) {
+        if (mService == null) {
+            return false;
+        }
         try {
-            return sService.hasBindAppWidgetPermission(packageName, mContext.getUserId());
+            return mService.hasBindAppWidgetPermission(packageName, UserHandle.myUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -828,13 +954,35 @@
      *
      * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
      *
-     * @param provider        The package whose permission is being changed
-     * @param permission      Whether to give the package permission to bind widgets
+     * @param packageName The package whose permission is being changed
+     * @param permission Whether to give the package permission to bind widgets
+     *
      * @hide
      */
     public void setBindAppWidgetPermission(String packageName, boolean permission) {
+        if (mService == null) {
+            return;
+        }
+        setBindAppWidgetPermission(packageName, UserHandle.myUserId(), permission);
+    }
+
+    /**
+     * Changes any user-granted permission for the given package to bind app widgets
+     *
+     * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
+     *
+     * @param packageName The package whose permission is being changed
+     * @param userId The user under which the package is running.
+     * @param permission Whether to give the package permission to bind widgets
+     *
+     * @hide
+     */
+    public void setBindAppWidgetPermission(String packageName, int userId, boolean permission) {
+        if (mService == null) {
+            return;
+        }
         try {
-            sService.setBindAppWidgetPermission(packageName, permission, mContext.getUserId());
+            mService.setBindAppWidgetPermission(packageName, userId, permission);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -847,18 +995,20 @@
      * The appWidgetId specified must already be bound to the calling AppWidgetHost via
      * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
      *
+     * @param packageName   The package from which the binding is requested.
      * @param appWidgetId   The AppWidget instance for which to bind the RemoteViewsService.
      * @param intent        The intent of the service which will be providing the data to the
      *                      RemoteViewsAdapter.
      * @param connection    The callback interface to be notified when a connection is made or lost.
-     * @param userHandle    The user to bind to.
      * @hide
      */
-    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection,
-            UserHandle userHandle) {
+    public void bindRemoteViewsService(String packageName, int appWidgetId, Intent intent,
+            IBinder connection) {
+        if (mService == null) {
+            return;
+        }
         try {
-            sService.bindRemoteViewsService(appWidgetId, intent, connection,
-                    userHandle.getIdentifier());
+            mService.bindRemoteViewsService(packageName, appWidgetId, intent, connection);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -871,15 +1021,18 @@
      * The appWidgetId specified muse already be bound to the calling AppWidgetHost via
      * {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
      *
+     * @param packageName   The package from which the binding is requested.
      * @param appWidgetId   The AppWidget instance for which to bind the RemoteViewsService.
      * @param intent        The intent of the service which will be providing the data to the
      *                      RemoteViewsAdapter.
-     * @param userHandle    The user to unbind from.
      * @hide
      */
-    public void unbindRemoteViewsService(int appWidgetId, Intent intent, UserHandle userHandle) {
+    public void unbindRemoteViewsService(String packageName, int appWidgetId, Intent intent) {
+        if (mService == null) {
+            return;
+        }
         try {
-            sService.unbindRemoteViewsService(appWidgetId, intent, userHandle.getIdentifier());
+            mService.unbindRemoteViewsService(packageName, appWidgetId, intent);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -894,12 +1047,40 @@
      *            AppWidget provider to find appWidgetIds for.
      */
     public int[] getAppWidgetIds(ComponentName provider) {
+        if (mService == null) {
+            return new int[0];
+        }
         try {
-            return sService.getAppWidgetIds(provider, mContext.getUserId());
+            return mService.getAppWidgetIds(provider);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
     }
-}
 
+    private boolean bindAppWidgetIdIfAllowed(int appWidgetId, int profileId,
+            ComponentName provider, Bundle options) {
+        if (mService == null) {
+            return false;
+        }
+        try {
+            return mService.bindAppWidgetId(mPackageName, appWidgetId,
+                    profileId, provider, options);
+        }
+        catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+    }
+
+    private void convertSizesToPixels(AppWidgetProviderInfo info) {
+        // Converting complex to dp.
+        info.minWidth = TypedValue.complexToDimensionPixelSize(info.minWidth,
+                mDisplayMetrics);
+        info.minHeight = TypedValue.complexToDimensionPixelSize(info.minHeight,
+                mDisplayMetrics);
+        info.minResizeWidth = TypedValue.complexToDimensionPixelSize(info.minResizeWidth,
+                mDisplayMetrics);
+        info.minResizeHeight = TypedValue.complexToDimensionPixelSize(info.minResizeHeight,
+                mDisplayMetrics);
+    }
+}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 8b9c7f0..e4dad5a 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -16,9 +16,17 @@
 
 package android.appwidget;
 
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.content.ComponentName;
+import android.os.UserHandle;
+import android.os.UserManager;
 
 /**
  * Describes the meta data for an installed AppWidget provider.  The fields in this class
@@ -145,21 +153,23 @@
     public ComponentName configure;
 
     /**
-     * The label to display to the user in the AppWidget picker.  If not supplied in the
-     * xml, the application label will be used.
+     * The label to display to the user in the AppWidget picker.
      *
-     * <p>This field corresponds to the <code>android:label</code> attribute in
-     * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+     * @deprecated Use {@link #loadLabel(android.content.pm.PackageManager)}.
      */
+    @Deprecated
     public String label;
 
     /**
-     * The icon to display for this AppWidget in the AppWidget picker.  If not supplied in the
+     * The icon to display for this AppWidget in the AppWidget picker. If not supplied in the
      * xml, the application icon will be used.
      *
      * <p>This field corresponds to the <code>android:icon</code> attribute in
      * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+     *
+     * @deprecated Use {@link #loadIcon(android.content.Context, int)}.
      */
+    @Deprecated
     public int icon;
 
     /**
@@ -176,7 +186,10 @@
      *
      * <p>This field corresponds to the <code>android:previewImage</code> attribute in
      * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+     *
+     * @deprecated User {@link #loadPreviewImage(android.content.Context, int)}.
      */
+    @Deprecated
     public int previewImage;
 
     /**
@@ -200,12 +213,17 @@
      */
     public int widgetCategory;
 
+    /** @hide */
+    public ActivityInfo providerInfo;
+
     public AppWidgetProviderInfo() {
+
     }
 
     /**
      * Unflatten the AppWidgetProviderInfo from a parcel.
      */
+    @SuppressWarnings("deprecation")
     public AppWidgetProviderInfo(Parcel in) {
         if (0 != in.readInt()) {
             this.provider = new ComponentName(in);
@@ -226,8 +244,86 @@
         this.autoAdvanceViewId = in.readInt();
         this.resizeMode = in.readInt();
         this.widgetCategory = in.readInt();
+        this.providerInfo = in.readParcelable(null);
     }
 
+    /**
+     * Loads the localized label to display to the user in the AppWidget picker.
+     *
+     * @param packageManager Package manager instance for loading resources.
+     * @return The label for the current locale.
+     */
+    public final String loadLabel(PackageManager packageManager) {
+        CharSequence label = providerInfo.loadLabel(packageManager);
+        if (label != null) {
+            return label.toString().trim();
+        }
+        return null;
+    }
+
+    /**
+     * Loads the icon to display for this AppWidget in the AppWidget picker. If not
+     * supplied in the xml, the application icon will be used. A client can optionally
+     * provide a desired density such as {@link android.util.DisplayMetrics#DENSITY_LOW}
+     * {@link android.util.DisplayMetrics#DENSITY_MEDIUM}, etc. If no density is
+     * provided, the density of the current display will be used.
+     * <p>
+     * The loaded icon corresponds to the <code>android:icon</code> attribute in
+     * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> If you care about widgets from different profiles, you
+     * should use this method to load the icon as the system will apply the correct
+     * badging when applicable, so the user knows which profile a widget comes from.
+     * </p>
+     *
+     * @param context Context for accessing resources.
+     * @param density The optional desired density as per
+     *         {@link android.util.DisplayMetrics#densityDpi}.
+     * @return The potentially badged provider icon.
+     */
+    public final Drawable loadIcon(Context context, int density) {
+        return loadDrawable(context, density, providerInfo.getIconResource());
+    }
+
+    /**
+     * Loads a preview of what the AppWidget will look like after it's configured.
+     * If not supplied, the AppWidget's icon will be used. A client can optionally
+     * provide a desired deinsity such as {@link android.util.DisplayMetrics#DENSITY_LOW}
+     * {@link android.util.DisplayMetrics#DENSITY_MEDIUM}, etc. If no density is
+     * provided, the density of the current display will be used.
+     * <p>
+     * The loaded image corresponds to the <code>android:previewImage</code> attribute
+     * in the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> If you care about widgets from different profiles, you
+     * should use this method to load the preview image as the system will apply the
+     * correct badging when applicable, so the user knows which profile a previewed
+     * widget comes from.
+     * </p>
+     *
+     * @param context Context for accessing resources.
+     * @param density The optional desired density as per
+     *         {@link android.util.DisplayMetrics#densityDpi}.
+     * @return The potentially badged widget preview image.
+     */
+    @SuppressWarnings("deprecation")
+    public final Drawable loadPreviewImage(Context context, int density) {
+        return loadDrawable(context, density, previewImage);
+    }
+
+    /**
+     * Gets the user profile in which the provider resides.
+     *
+     * @return The hosting user profile.
+     */
+    public final UserHandle getProfile() {
+        return new UserHandle(UserHandle.getUserId(providerInfo.applicationInfo.uid));
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
     public void writeToParcel(android.os.Parcel out, int flags) {
         if (this.provider != null) {
             out.writeInt(1);
@@ -254,9 +350,11 @@
         out.writeInt(this.autoAdvanceViewId);
         out.writeInt(this.resizeMode);
         out.writeInt(this.widgetCategory);
+        out.writeParcelable(this.providerInfo, flags);
     }
 
     @Override
+    @SuppressWarnings("deprecation")
     public AppWidgetProviderInfo clone() {
         AppWidgetProviderInfo that = new AppWidgetProviderInfo();
         that.provider = this.provider == null ? null : this.provider.clone();
@@ -273,7 +371,8 @@
         that.previewImage = this.previewImage;
         that.autoAdvanceViewId = this.autoAdvanceViewId;
         that.resizeMode = this.resizeMode;
-        that.widgetCategory  = this.widgetCategory;
+        that.widgetCategory = this.widgetCategory;
+        that.providerInfo = this.providerInfo;
         return that;
     }
 
@@ -281,6 +380,33 @@
         return 0;
     }
 
+    private Drawable loadDrawable(Context context, int density, int resourceId) {
+        try {
+            Resources resources = context.getPackageManager().getResourcesForApplication(
+                    providerInfo.applicationInfo);
+
+            final Drawable drawable;
+            if (resourceId > 0) {
+                if (density <= 0) {
+                    density = context.getResources().getDisplayMetrics().densityDpi;
+                }
+                drawable = resources.getDrawableForDensity(resourceId, density);
+            } else {
+                drawable = providerInfo.loadIcon(context.getPackageManager());
+            }
+
+            if (drawable instanceof BitmapDrawable) {
+                UserManager userManager = (UserManager) context.getSystemService(
+                        Context.USER_SERVICE);
+                return userManager.getBadgedDrawableForUser(drawable, getProfile());
+            }
+        } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
+            /* ignore */
+        }
+
+        return null;
+    }
+
     /**
      * Parcelable.Creator that instantiates AppWidgetProviderInfo objects
      */
@@ -299,6 +425,6 @@
     };
 
     public String toString() {
-        return "AppWidgetProviderInfo(provider=" + this.provider + ")";
+        return "AppWidgetProviderInfo(" + getProfile() + '/' + provider + ')';
     }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f3a7b1c1..7417208 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2076,7 +2076,7 @@
             CLIPBOARD_SERVICE,
             INPUT_METHOD_SERVICE,
             TEXT_SERVICES_MANAGER_SERVICE,
-            //@hide: APPWIDGET_SERVICE,
+            APPWIDGET_SERVICE,
             //@hide: BACKUP_SERVICE,
             DROPBOX_SERVICE,
             DEVICE_POLICY_SERVICE,
@@ -2596,7 +2596,6 @@
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.appwidget.AppWidgetManager} for accessing AppWidgets.
      *
-     * @hide
      * @see #getSystemService
      */
     public static final String APPWIDGET_SERVICE = "appwidget";
@@ -3306,6 +3305,14 @@
             throws PackageManager.NameNotFoundException;
 
     /**
+     * Creates a context given an {@link android.content.pm.ApplicationInfo}.
+     *
+     * @hide
+     */
+    public abstract Context createApplicationContext(ApplicationInfo application,
+            int flags) throws PackageManager.NameNotFoundException;
+
+    /**
      * Get the userId associated with this context
      * @return user id
      *
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 4e1c4a7..ad7c350 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -666,6 +666,12 @@
     }
 
     /** @hide */
+    public Context createApplicationContext(ApplicationInfo application,
+            int flags) throws PackageManager.NameNotFoundException {
+        return mBase.createApplicationContext(application, flags);
+    }
+
+    /** @hide */
     @Override
     public int getUserId() {
         return mBase.getUserId();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 7196372..e27ad7d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -233,7 +233,7 @@
             in ComponentName[] set, in ComponentName activity, int userId);
 
     void replacePreferredActivity(in IntentFilter filter, int match,
-            in ComponentName[] set, in ComponentName activity);
+            in ComponentName[] set, in ComponentName activity, int userId);
 
     void clearPackagePreferredActivities(String packageName);
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c5dcd8e..e482bb0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3523,6 +3523,15 @@
             ComponentName[] set, ComponentName activity);
 
     /**
+     * @hide
+     */
+    @Deprecated
+    public void replacePreferredActivityAsUser(IntentFilter filter, int match,
+           ComponentName[] set, ComponentName activity, int userId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Remove all preferred activity mappings, previously added with
      * {@link #addPreferredActivity}, from the
      * system whose activities are implemented in the given package name.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1b9a0c5..8b44f3b 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -416,6 +416,15 @@
         }
     }
 
+    public void resizeVirtualDisplay(IVirtualDisplayCallbacks token,
+            int width, int height, int densityDpi) {
+        try {
+            mDm.resizeVirtualDisplay(token, width, height, densityDpi);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Failed to resize virtual display.", ex);
+        }
+    }
+
     public void releaseVirtualDisplay(IVirtualDisplayCallbacks token) {
         try {
             mDm.releaseVirtualDisplay(token);
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 44ffbc4..cfaa5a0 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -65,6 +65,10 @@
             in IMediaProjection projectionToken, String packageName, String name,
             int width, int height, int densityDpi, in Surface surface, int flags);
 
+    // No permissions required, but must be same Uid as the creator.
+    void resizeVirtualDisplay(in IVirtualDisplayCallbacks token,
+            int width, int height, int densityDpi);
+
     // No permissions required but must be same Uid as the creator.
     void setVirtualDisplaySurface(in IVirtualDisplayCallbacks token, in Surface surface);
 
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index df6116b..1dd6978 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -80,6 +80,18 @@
     }
 
     /**
+     * Asks the virtual display to resize.
+     *<p>
+     * This is really just a convenience to allow applications using
+     * virtual displays to adapt to changing conditions without having
+     * to tear down and recreate the display.
+     * </p>
+     */
+    public void resize(int width, int height, int densityDpi) {
+        mGlobal.resizeVirtualDisplay(mToken, width, height, densityDpi);
+    }
+
+    /**
      * Releases the virtual display and destroys its underlying surface.
      * <p>
      * All remaining windows on the virtual display will be forcibly removed
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index adee740..7a49eb5 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,6 +16,7 @@
 
 package android.hardware.soundtrigger;
 
+import android.media.AudioFormat;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -86,11 +87,15 @@
         /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */
         public final int powerConsumptionMw;
 
+        /** Returns the trigger (key phrase) capture in the binary data of the
+         * recognition callback event */
+        public final boolean returnsTriggerInEvent;
+
         ModuleProperties(int id, String implementor, String description,
                 String uuid, int version, int maxSoundModels, int maxKeyphrases,
                 int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
                 int maxBufferMs, boolean supportsConcurrentCapture,
-                int powerConsumptionMw) {
+                int powerConsumptionMw, boolean returnsTriggerInEvent) {
             this.id = id;
             this.implementor = implementor;
             this.description = description;
@@ -104,6 +109,7 @@
             this.maxBufferMs = maxBufferMs;
             this.supportsConcurrentCapture = supportsConcurrentCapture;
             this.powerConsumptionMw = powerConsumptionMw;
+            this.returnsTriggerInEvent = returnsTriggerInEvent;
         }
 
         public static final Parcelable.Creator<ModuleProperties> CREATOR
@@ -131,10 +137,11 @@
             int maxBufferMs = in.readInt();
             boolean supportsConcurrentCapture = in.readByte() == 1;
             int powerConsumptionMw = in.readInt();
+            boolean returnsTriggerInEvent = in.readByte() == 1;
             return new ModuleProperties(id, implementor, description, uuid, version,
                     maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
                     supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
-                    powerConsumptionMw);
+                    powerConsumptionMw, returnsTriggerInEvent);
         }
 
         @Override
@@ -152,6 +159,7 @@
             dest.writeInt(maxBufferMs);
             dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0));
             dest.writeInt(powerConsumptionMw);
+            dest.writeByte((byte) (returnsTriggerInEvent ? 1 : 0));
         }
 
         @Override
@@ -167,7 +175,8 @@
                     + maxUsers + ", recognitionModes=" + recognitionModes
                     + ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
                     + maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture
-                    + ", powerConsumptionMw=" + powerConsumptionMw + "]";
+                    + ", powerConsumptionMw=" + powerConsumptionMw
+                    + ", returnsTriggerInEvent=" + returnsTriggerInEvent + "]";
         }
     }
 
@@ -190,11 +199,15 @@
         /** Sound model type (e.g. TYPE_KEYPHRASE); */
         public final int type;
 
+        /** Unique sound model vendor identifier */
+        public final UUID vendorUuid;
+
         /** Opaque data. For use by vendor implementation and enrollment application */
         public final byte[] data;
 
-        public SoundModel(UUID uuid, int type, byte[] data) {
+        public SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data) {
             this.uuid = uuid;
+            this.vendorUuid = vendorUuid;
             this.type = type;
             this.data = data;
         }
@@ -329,8 +342,9 @@
         /** Key phrases in this sound model */
         public final Keyphrase[] keyphrases; // keyword phrases in model
 
-        public KeyphraseSoundModel(UUID id, byte[] data, Keyphrase[] keyphrases) {
-            super(id, TYPE_KEYPHRASE, data);
+        public KeyphraseSoundModel(
+                UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases) {
+            super(uuid, vendorUuid, TYPE_KEYPHRASE, data);
             this.keyphrases = keyphrases;
         }
 
@@ -347,9 +361,14 @@
 
         private static KeyphraseSoundModel fromParcel(Parcel in) {
             UUID uuid = UUID.fromString(in.readString());
+            UUID vendorUuid = null;
+            int length = in.readInt();
+            if (length >= 0) {
+                vendorUuid = UUID.fromString(in.readString());
+            }
             byte[] data = in.readBlob();
             Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
-            return new KeyphraseSoundModel(uuid, data, keyphrases);
+            return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases);
         }
 
         @Override
@@ -360,14 +379,21 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeString(uuid.toString());
+            if (vendorUuid == null) {
+                dest.writeInt(-1);
+            } else {
+                dest.writeInt(vendorUuid.toString().length());
+                dest.writeString(vendorUuid.toString());
+            }
             dest.writeBlob(data);
             dest.writeTypedArray(keyphrases, flags);
         }
 
         @Override
         public String toString() {
-            return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases) + ", uuid="
-                    + uuid + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
+            return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases)
+                    + ", uuid=" + uuid + ", vendorUuid=" + vendorUuid
+                    + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
         }
     }
 
@@ -411,18 +437,26 @@
         public final int captureDelayMs;
         /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
         public final int capturePreambleMs;
+        /** True if  the trigger (key phrase capture is present in binary data */
+        public final boolean triggerInData;
+        /** Audio format of either the trigger in event data or to use for capture of the
+          * rest of the utterance */
+        public AudioFormat captureFormat;
         /** Opaque data for use by system applications who know about voice engine internals,
          * typically during enrollment. */
         public final byte[] data;
 
         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
-                int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data) {
+                int captureSession, int captureDelayMs, int capturePreambleMs,
+                boolean triggerInData, AudioFormat captureFormat, byte[] data) {
             this.status = status;
             this.soundModelHandle = soundModelHandle;
             this.captureAvailable = captureAvailable;
             this.captureSession = captureSession;
             this.captureDelayMs = captureDelayMs;
             this.capturePreambleMs = capturePreambleMs;
+            this.triggerInData = triggerInData;
+            this.captureFormat = captureFormat;
             this.data = data;
         }
 
@@ -444,9 +478,21 @@
             int captureSession = in.readInt();
             int captureDelayMs = in.readInt();
             int capturePreambleMs = in.readInt();
+            boolean triggerInData = in.readByte() == 1;
+            AudioFormat captureFormat = null;
+            if (triggerInData) {
+                int sampleRate = in.readInt();
+                int encoding = in.readInt();
+                int channelMask = in.readInt();
+                captureFormat = (new AudioFormat.Builder())
+                        .setChannelMask(channelMask)
+                        .setEncoding(encoding)
+                        .setSampleRate(sampleRate)
+                        .build();
+            }
             byte[] data = in.readBlob();
             return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession,
-                    captureDelayMs, capturePreambleMs, data);
+                    captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data);
         }
 
         @Override
@@ -462,6 +508,14 @@
             dest.writeInt(captureSession);
             dest.writeInt(captureDelayMs);
             dest.writeInt(capturePreambleMs);
+            if (triggerInData && (captureFormat != null)) {
+                dest.writeByte((byte)1);
+                dest.writeInt(captureFormat.getSampleRate());
+                dest.writeInt(captureFormat.getEncoding());
+                dest.writeInt(captureFormat.getChannelMask());
+            } else {
+                dest.writeByte((byte)0);
+            }
             dest.writeBlob(data);
         }
 
@@ -473,6 +527,12 @@
             result = prime * result + captureDelayMs;
             result = prime * result + capturePreambleMs;
             result = prime * result + captureSession;
+            result = prime * result + (triggerInData ? 1231 : 1237);
+            if (captureFormat != null) {
+                result = prime * result + captureFormat.getSampleRate();
+                result = prime * result + captureFormat.getEncoding();
+                result = prime * result + captureFormat.getChannelMask();
+            }
             result = prime * result + Arrays.hashCode(data);
             result = prime * result + soundModelHandle;
             result = prime * result + status;
@@ -502,6 +562,14 @@
                 return false;
             if (status != other.status)
                 return false;
+            if (triggerInData != other.triggerInData)
+                return false;
+            if (captureFormat.getSampleRate() != other.captureFormat.getSampleRate())
+                return false;
+            if (captureFormat.getEncoding() != other.captureFormat.getEncoding())
+                return false;
+            if (captureFormat.getChannelMask() != other.captureFormat.getChannelMask())
+                return false;
             return true;
         }
 
@@ -511,6 +579,13 @@
                     + ", captureAvailable=" + captureAvailable + ", captureSession="
                     + captureSession + ", captureDelayMs=" + captureDelayMs
                     + ", capturePreambleMs=" + capturePreambleMs
+                    + ", triggerInData=" + triggerInData
+                    + ((captureFormat == null) ? "" :
+                        (", sampleRate=" + captureFormat.getSampleRate()))
+                    + ((captureFormat == null) ? "" :
+                        (", encoding=" + captureFormat.getEncoding()))
+                    + ((captureFormat == null) ? "" :
+                        (", channelMask=" + captureFormat.getChannelMask()))
                     + ", data=" + (data == null ? 0 : data.length) + "]";
         }
     }
@@ -673,14 +748,19 @@
         /** Recognition modes matched for this event */
         public final int recognitionModes;
 
+        /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
+         * is not performed */
+        public final int coarseConfidenceLevel;
+
         /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
          * be recognized (RecognitionConfig) */
         public final ConfidenceLevel[] confidenceLevels;
 
-        public KeyphraseRecognitionExtra(int id, int recognitionModes,
-                                  ConfidenceLevel[] confidenceLevels) {
+        public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
+                ConfidenceLevel[] confidenceLevels) {
             this.id = id;
             this.recognitionModes = recognitionModes;
+            this.coarseConfidenceLevel = coarseConfidenceLevel;
             this.confidenceLevels = confidenceLevels;
         }
 
@@ -698,14 +778,17 @@
         private static KeyphraseRecognitionExtra fromParcel(Parcel in) {
             int id = in.readInt();
             int recognitionModes = in.readInt();
+            int coarseConfidenceLevel = in.readInt();
             ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR);
-            return new KeyphraseRecognitionExtra(id, recognitionModes, confidenceLevels);
+            return new KeyphraseRecognitionExtra(id, recognitionModes, coarseConfidenceLevel,
+                    confidenceLevels);
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(id);
             dest.writeInt(recognitionModes);
+            dest.writeInt(coarseConfidenceLevel);
             dest.writeTypedArray(confidenceLevels, flags);
         }
 
@@ -721,6 +804,7 @@
             result = prime * result + Arrays.hashCode(confidenceLevels);
             result = prime * result + id;
             result = prime * result + recognitionModes;
+            result = prime * result + coarseConfidenceLevel;
             return result;
         }
 
@@ -739,12 +823,15 @@
                 return false;
             if (recognitionModes != other.recognitionModes)
                 return false;
+            if (coarseConfidenceLevel != other.coarseConfidenceLevel)
+                return false;
             return true;
         }
 
         @Override
         public String toString() {
             return "KeyphraseRecognitionExtra [id=" + id + ", recognitionModes=" + recognitionModes
+                    + ", coarseConfidenceLevel=" + coarseConfidenceLevel
                     + ", confidenceLevels=" + Arrays.toString(confidenceLevels) + "]";
         }
     }
@@ -756,15 +843,12 @@
         /** Indicates if the key phrase is present in the buffered audio available for capture */
         public final KeyphraseRecognitionExtra[] keyphraseExtras;
 
-        /** Additional data available for each recognized key phrases in the model */
-        public final boolean keyphraseInCapture;
-
         public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
-               int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data,
-               boolean keyphraseInCapture, KeyphraseRecognitionExtra[] keyphraseExtras) {
+               int captureSession, int captureDelayMs, int capturePreambleMs,
+               boolean triggerInData, AudioFormat captureFormat, byte[] data,
+               KeyphraseRecognitionExtra[] keyphraseExtras) {
             super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
-                  capturePreambleMs, data);
-            this.keyphraseInCapture = keyphraseInCapture;
+                  capturePreambleMs, triggerInData, captureFormat, data);
             this.keyphraseExtras = keyphraseExtras;
         }
 
@@ -786,13 +870,24 @@
             int captureSession = in.readInt();
             int captureDelayMs = in.readInt();
             int capturePreambleMs = in.readInt();
+            boolean triggerInData = in.readByte() == 1;
+            AudioFormat captureFormat = null;
+            if (triggerInData) {
+                int sampleRate = in.readInt();
+                int encoding = in.readInt();
+                int channelMask = in.readInt();
+                captureFormat = (new AudioFormat.Builder())
+                        .setChannelMask(channelMask)
+                        .setEncoding(encoding)
+                        .setSampleRate(sampleRate)
+                        .build();
+            }
             byte[] data = in.readBlob();
-            boolean keyphraseInCapture = in.readByte() == 1;
             KeyphraseRecognitionExtra[] keyphraseExtras =
                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
             return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable,
-                    captureSession, captureDelayMs, capturePreambleMs, data, keyphraseInCapture,
-                    keyphraseExtras);
+                    captureSession, captureDelayMs, capturePreambleMs, triggerInData,
+                    captureFormat, data, keyphraseExtras);
         }
 
         @Override
@@ -803,8 +898,15 @@
             dest.writeInt(captureSession);
             dest.writeInt(captureDelayMs);
             dest.writeInt(capturePreambleMs);
+            if (triggerInData && (captureFormat != null)) {
+                dest.writeByte((byte)1);
+                dest.writeInt(captureFormat.getSampleRate());
+                dest.writeInt(captureFormat.getEncoding());
+                dest.writeInt(captureFormat.getChannelMask());
+            } else {
+                dest.writeByte((byte)0);
+            }
             dest.writeBlob(data);
-            dest.writeByte((byte) (keyphraseInCapture ? 1 : 0));
             dest.writeTypedArray(keyphraseExtras, flags);
         }
 
@@ -818,7 +920,6 @@
             final int prime = 31;
             int result = super.hashCode();
             result = prime * result + Arrays.hashCode(keyphraseExtras);
-            result = prime * result + (keyphraseInCapture ? 1231 : 1237);
             return result;
         }
 
@@ -833,23 +934,127 @@
             KeyphraseRecognitionEvent other = (KeyphraseRecognitionEvent) obj;
             if (!Arrays.equals(keyphraseExtras, other.keyphraseExtras))
                 return false;
-            if (keyphraseInCapture != other.keyphraseInCapture)
-                return false;
             return true;
         }
 
         @Override
         public String toString() {
             return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras)
-                    + ", keyphraseInCapture=" + keyphraseInCapture + ", status=" + status
+                    + ", status=" + status
                     + ", soundModelHandle=" + soundModelHandle + ", captureAvailable="
                     + captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs="
                     + captureDelayMs + ", capturePreambleMs=" + capturePreambleMs
+                    + ", triggerInData=" + triggerInData
+                    + ((captureFormat == null) ? "" :
+                        (", sampleRate=" + captureFormat.getSampleRate()))
+                    + ((captureFormat == null) ? "" :
+                        (", encoding=" + captureFormat.getEncoding()))
+                    + ((captureFormat == null) ? "" :
+                        (", channelMask=" + captureFormat.getChannelMask()))
                     + ", data=" + (data == null ? 0 : data.length) + "]";
         }
     }
 
     /**
+     *  Status codes for {@link SoundModelEvent}
+     */
+    /** Sound Model was updated */
+    public static final int SOUNDMODEL_STATUS_UPDATED = 0;
+
+    /**
+     *  A SoundModelEvent is provided by the
+     *  {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
+     *  callback when a sound model has been updated by the implementation
+     */
+    public static class SoundModelEvent implements Parcelable {
+        /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
+        public final int status;
+        /** The updated sound model handle */
+        public final int soundModelHandle;
+        /** New sound model data */
+        public final byte[] data;
+
+        SoundModelEvent(int status, int soundModelHandle, byte[] data) {
+            this.status = status;
+            this.soundModelHandle = soundModelHandle;
+            this.data = data;
+        }
+
+        public static final Parcelable.Creator<SoundModelEvent> CREATOR
+                = new Parcelable.Creator<SoundModelEvent>() {
+            public SoundModelEvent createFromParcel(Parcel in) {
+                return SoundModelEvent.fromParcel(in);
+            }
+
+            public SoundModelEvent[] newArray(int size) {
+                return new SoundModelEvent[size];
+            }
+        };
+
+        private static SoundModelEvent fromParcel(Parcel in) {
+            int status = in.readInt();
+            int soundModelHandle = in.readInt();
+            byte[] data = in.readBlob();
+            return new SoundModelEvent(status, soundModelHandle, data);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(status);
+            dest.writeInt(soundModelHandle);
+            dest.writeBlob(data);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + Arrays.hashCode(data);
+            result = prime * result + soundModelHandle;
+            result = prime * result + status;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            SoundModelEvent other = (SoundModelEvent) obj;
+            if (!Arrays.equals(data, other.data))
+                return false;
+            if (soundModelHandle != other.soundModelHandle)
+                return false;
+            if (status != other.status)
+                return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
+                    + ", data=" + (data == null ? 0 : data.length) + "]";
+        }
+    }
+
+    /**
+     *  Native service state. {@link StatusListener#onServiceStateChange(int)}
+     */
+    // Keep in sync with system/core/include/system/sound_trigger.h
+    /** Sound trigger service is enabled */
+    public static final int SERVICE_STATE_ENABLED = 0;
+    /** Sound trigger service is disabled */
+    public static final int SERVICE_STATE_DISABLED = 1;
+
+    /**
      * Returns a list of descriptors for all harware modules loaded.
      * @param modules A ModuleProperties array where the list will be returned.
      * @return - {@link #STATUS_OK} in case of success
@@ -891,6 +1096,18 @@
         public abstract void onRecognition(RecognitionEvent event);
 
         /**
+         * Called when a sound model has been updated
+         */
+        public abstract void onSoundModelUpdate(SoundModelEvent event);
+
+        /**
+         * Called when the sound trigger native service state changes.
+         * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED},
+         * {@link SoundTrigger#SERVICE_STATE_DISABLED}
+         */
+        public abstract void onServiceStateChange(int state);
+
+        /**
          * Called when the sound trigger native service dies
          */
         public abstract void onServiceDied();
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 4a54fd8..1a8723d 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -36,8 +36,11 @@
     private int mId;
     private NativeEventHandlerDelegate mEventHandlerDelegate;
 
+    // to be kept in sync with core/jni/android_hardware_SoundTrigger.cpp
     private static final int EVENT_RECOGNITION = 1;
     private static final int EVENT_SERVICE_DIED = 2;
+    private static final int EVENT_SOUNDMODEL = 3;
+    private static final int EVENT_SERVICE_STATE_CHANGE = 4;
 
     SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) {
         mId = moduleId;
@@ -133,10 +136,7 @@
             if (handler != null) {
                 looper = handler.getLooper();
             } else {
-                looper = Looper.myLooper();
-                if (looper == null) {
-                    looper = Looper.getMainLooper();
-                }
+                looper = Looper.getMainLooper();
             }
 
             // construct the event handler with this looper
@@ -152,6 +152,17 @@
                                         (SoundTrigger.RecognitionEvent)msg.obj);
                             }
                             break;
+                        case EVENT_SOUNDMODEL:
+                            if (listener != null) {
+                                listener.onSoundModelUpdate(
+                                        (SoundTrigger.SoundModelEvent)msg.obj);
+                            }
+                            break;
+                        case EVENT_SERVICE_STATE_CHANGE:
+                            if (listener != null) {
+                                listener.onServiceStateChange(msg.arg1);
+                            }
+                            break;
                         case EVENT_SERVICE_DIED:
                             if (listener != null) {
                                 listener.onServiceDied();
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index e5a5292..0eda692 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -63,7 +63,6 @@
         this.score = score;
         this.notification = notification;
         this.user = user;
-        this.notification.setUser(user);
         this.postTime = postTime;
         this.key = key();
         this.groupKey = groupKey();
@@ -83,7 +82,6 @@
         this.score = in.readInt();
         this.notification = new Notification(in);
         this.user = UserHandle.readFromParcel(in);
-        this.notification.setUser(this.user);
         this.postTime = in.readLong();
         this.key = key();
         this.groupKey = groupKey();
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 279bf40..b0ff947 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -82,6 +82,15 @@
     private static final int STATE_NOT_READY = 0;
 
     // Keyphrase management actions. Used in getManageIntent() ----//
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+                MANAGE_ACTION_ENROLL,
+                MANAGE_ACTION_RE_ENROLL,
+                MANAGE_ACTION_UN_ENROLL
+            })
+    public @interface ManageActions {}
+
     /** Indicates that we need to enroll. */
     public static final int MANAGE_ACTION_ENROLL = 0;
     /** Indicates that we need to re-enroll. */
@@ -360,7 +369,7 @@
      *         This may happen if another detector has been instantiated or the
      *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
-    public Intent getManageIntent(int action) {
+    public Intent getManageIntent(@ManageActions int action) {
         if (DBG) Slog.d(TAG, "getManageIntent(" + action + ")");
         synchronized (mLock) {
             return getManageIntentLocked(action);
@@ -426,7 +435,7 @@
         KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1];
         // TODO: Do we need to do something about the confidence level here?
         recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.id,
-                mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]);
+                mKeyphraseMetadata.recognitionModeFlags, 0, new ConfidenceLevel[0]);
         boolean captureTriggerAudio =
                 (recognitionFlags&RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0;
         boolean allowMultipleTriggers =
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 8b2ec7a..681717c 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1722,6 +1722,11 @@
         return false;
     }
 
+    /** @hide */
+    public static final boolean isMetaKey(int keyCode) {
+        return keyCode == KeyEvent.KEYCODE_META_LEFT || keyCode == KeyEvent.KEYCODE_META_RIGHT;
+    }
+
     /** {@inheritDoc} */
     @Override
     public final int getDeviceId() {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 191ad64..1e28e33 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -74,6 +74,7 @@
             IBinder displayToken, int orientation,
             int l, int t, int r, int b,
             int L, int T, int R, int B);
+    private static native void nativeSetDisplaySize(IBinder displayToken, int width, int height);
     private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
             IBinder displayToken);
     private static native int nativeGetActiveConfig(IBinder displayToken);
@@ -588,6 +589,17 @@
         }
     }
 
+    public static void setDisplaySize(IBinder displayToken, int width, int height) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("width and height must be positive");
+        }
+
+        nativeSetDisplaySize(displayToken, width, height);
+    }
+
     public static IBinder createDisplay(String name, boolean secure) {
         if (name == null) {
             throw new IllegalArgumentException("name must not be null");
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 5b80648..1716dbd 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -111,7 +111,15 @@
             filter.addAction(Intent.ACTION_TIME_CHANGED);
             filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
 
-            getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
+            // OK, this is gross but needed. This class is supported by the
+            // remote views machanism and as a part of that the remote views
+            // can be inflated by a context for another user without the app
+            // having interact users permission - just for loading resources.
+            // For exmaple, when adding widgets from a user profile to the
+            // home screen. Therefore, we register the receiver as the current
+            // user not the one the context is for.
+            getContext().registerReceiverAsUser(mIntentReceiver,
+                    android.os.Process.myUserHandle(), filter, null, mHandler);
         }
 
         // NOTE: It's safe to do these after registering the receiver since the receiver always runs
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5c7a43b..1098fa2 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.app.ActivityOptions;
+import android.app.ActivityThread;
 import android.app.PendingIntent;
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
@@ -73,11 +74,11 @@
     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
 
     /**
-     * User that these views should be applied as. Requires
-     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} when
-     * crossing user boundaries.
+     * Application that hosts the remote views.
+     *
+     * @hide
      */
-    private UserHandle mUser = android.os.Process.myUserHandle();
+    private ApplicationInfo mApplication;
 
     /**
      * The package name of the package containing the layout
@@ -275,9 +276,9 @@
     /**
      * Merges the passed RemoteViews actions with this RemoteViews actions according to
      * action-specific merge rules.
-     * 
+     *
      * @param newRv
-     * 
+     *
      * @hide
      */
     public void mergeRemoteViews(RemoteViews newRv) {
@@ -1608,16 +1609,16 @@
             int bpp = 4;
             if (c != null) {
                 switch (c) {
-                case ALPHA_8:
-                    bpp = 1;
-                    break;
-                case RGB_565:
-                case ARGB_4444:
-                    bpp = 2;
-                    break;
-                case ARGB_8888:
-                    bpp = 4;
-                    break;
+                    case ALPHA_8:
+                        bpp = 1;
+                        break;
+                    case RGB_565:
+                    case ARGB_4444:
+                        bpp = 2;
+                        break;
+                    case ARGB_8888:
+                        bpp = 4;
+                        break;
                 }
             }
             increment(b.getWidth() * b.getHeight() * bpp);
@@ -1637,17 +1638,13 @@
         mPackage = packageName;
         mLayoutId = layoutId;
         mBitmapCache = new BitmapCache();
+        mApplication = ActivityThread.currentApplication().getApplicationInfo();
 
         // setup the memory usage statistics
         mMemoryUsageCounter = new MemoryUsageCounter();
         recalculateMemoryUsage();
     }
 
-    /** {@hide} */
-    public void setUser(UserHandle user) {
-        mUser = user;
-    }
-
     private boolean hasLandscapeAndPortraitLayouts() {
         return (mLandscape != null) && (mPortrait != null);
     }
@@ -1713,53 +1710,53 @@
                 for (int i=0; i<count; i++) {
                     int tag = parcel.readInt();
                     switch (tag) {
-                    case SetOnClickPendingIntent.TAG:
-                        mActions.add(new SetOnClickPendingIntent(parcel));
-                        break;
-                    case SetDrawableParameters.TAG:
-                        mActions.add(new SetDrawableParameters(parcel));
-                        break;
-                    case ReflectionAction.TAG:
-                        mActions.add(new ReflectionAction(parcel));
-                        break;
-                    case ViewGroupAction.TAG:
-                        mActions.add(new ViewGroupAction(parcel, mBitmapCache));
-                        break;
-                    case ReflectionActionWithoutParams.TAG:
-                        mActions.add(new ReflectionActionWithoutParams(parcel));
-                        break;
-                    case SetEmptyView.TAG:
-                        mActions.add(new SetEmptyView(parcel));
-                        break;
-                    case SetPendingIntentTemplate.TAG:
-                        mActions.add(new SetPendingIntentTemplate(parcel));
-                        break;
-                    case SetOnClickFillInIntent.TAG:
-                        mActions.add(new SetOnClickFillInIntent(parcel));
-                        break;
-                    case SetRemoteViewsAdapterIntent.TAG:
-                        mActions.add(new SetRemoteViewsAdapterIntent(parcel));
-                        break;
-                    case TextViewDrawableAction.TAG:
-                        mActions.add(new TextViewDrawableAction(parcel));
-                        break;
-                    case TextViewSizeAction.TAG:
-                        mActions.add(new TextViewSizeAction(parcel));
-                        break;
-                    case ViewPaddingAction.TAG:
-                        mActions.add(new ViewPaddingAction(parcel));
-                        break;
-                    case BitmapReflectionAction.TAG:
-                        mActions.add(new BitmapReflectionAction(parcel));
-                        break;
-                    case SetRemoteViewsAdapterList.TAG:
-                        mActions.add(new SetRemoteViewsAdapterList(parcel));
-                        break;
-                    case TextViewDrawableColorFilterAction.TAG:
-                        mActions.add(new TextViewDrawableColorFilterAction(parcel));
-                        break;
-                    default:
-                        throw new ActionException("Tag " + tag + " not found");
+                        case SetOnClickPendingIntent.TAG:
+                            mActions.add(new SetOnClickPendingIntent(parcel));
+                            break;
+                        case SetDrawableParameters.TAG:
+                            mActions.add(new SetDrawableParameters(parcel));
+                            break;
+                        case ReflectionAction.TAG:
+                            mActions.add(new ReflectionAction(parcel));
+                            break;
+                        case ViewGroupAction.TAG:
+                            mActions.add(new ViewGroupAction(parcel, mBitmapCache));
+                            break;
+                        case ReflectionActionWithoutParams.TAG:
+                            mActions.add(new ReflectionActionWithoutParams(parcel));
+                            break;
+                        case SetEmptyView.TAG:
+                            mActions.add(new SetEmptyView(parcel));
+                            break;
+                        case SetPendingIntentTemplate.TAG:
+                            mActions.add(new SetPendingIntentTemplate(parcel));
+                            break;
+                        case SetOnClickFillInIntent.TAG:
+                            mActions.add(new SetOnClickFillInIntent(parcel));
+                            break;
+                        case SetRemoteViewsAdapterIntent.TAG:
+                            mActions.add(new SetRemoteViewsAdapterIntent(parcel));
+                            break;
+                        case TextViewDrawableAction.TAG:
+                            mActions.add(new TextViewDrawableAction(parcel));
+                            break;
+                        case TextViewSizeAction.TAG:
+                            mActions.add(new TextViewSizeAction(parcel));
+                            break;
+                        case ViewPaddingAction.TAG:
+                            mActions.add(new ViewPaddingAction(parcel));
+                            break;
+                        case BitmapReflectionAction.TAG:
+                            mActions.add(new BitmapReflectionAction(parcel));
+                            break;
+                        case SetRemoteViewsAdapterList.TAG:
+                            mActions.add(new SetRemoteViewsAdapterList(parcel));
+                            break;
+                        case TextViewDrawableColorFilterAction.TAG:
+                            mActions.add(new TextViewDrawableColorFilterAction(parcel));
+                            break;
+                        default:
+                            throw new ActionException("Tag " + tag + " not found");
                     }
                 }
             }
@@ -1771,6 +1768,8 @@
             mLayoutId = mPortrait.getLayoutId();
         }
 
+        mApplication = parcel.readParcelable(null);
+
         // setup the memory usage statistics
         mMemoryUsageCounter = new MemoryUsageCounter();
         recalculateMemoryUsage();
@@ -2557,22 +2556,32 @@
     }
 
     private Context prepareContext(Context context) {
-        Context c;
-        String packageName = mPackage;
-
-        if (packageName != null) {
-            try {
-                c = context.createPackageContextAsUser(
-                        packageName, Context.CONTEXT_RESTRICTED, mUser);
-            } catch (NameNotFoundException e) {
-                Log.e(LOG_TAG, "Package name " + packageName + " not found");
-                c = context;
+        if (mApplication != null) {
+            if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
+                    && context.getPackageName().equals(mApplication.packageName)) {
+                return context;
             }
-        } else {
-            c = context;
+            try {
+                return context.createApplicationContext(mApplication,
+                        Context.CONTEXT_RESTRICTED);
+            } catch (NameNotFoundException e) {
+                Log.e(LOG_TAG, "Package name " + mPackage + " not found");
+            }
         }
 
-        return c;
+        if (mPackage != null) {
+            if (context.getPackageName().equals(mPackage)) {
+                return context;
+            }
+            try {
+                return context.createPackageContext(
+                        mPackage, Context.CONTEXT_RESTRICTED);
+            } catch (NameNotFoundException e) {
+                Log.e(LOG_TAG, "Package name " + mPackage + " not found");
+            }
+        }
+
+        return context;
     }
 
     /**
@@ -2629,6 +2638,8 @@
             mLandscape.writeToParcel(dest, flags);
             mPortrait.writeToParcel(dest, flags);
         }
+
+        dest.writeParcelable(mApplication, 0);
     }
 
     /**
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index bbe6f9e..5d21e0b 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -114,8 +114,6 @@
     // construction (happens when we have a cached FixedSizeRemoteViewsCache).
     private boolean mDataReady = false;
 
-    int mUserId;
-
     /**
      * An interface for the RemoteAdapter to notify other classes when adapters
      * are actually connected to/disconnected from their actual services.
@@ -159,9 +157,8 @@
                     RemoteViewsAdapter adapter;
                     final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
                     if ((adapter = mAdapter.get()) != null) {
-                        checkInteractAcrossUsersPermission(context, adapter.mUserId);
-                        mgr.bindRemoteViewsService(appWidgetId, intent, asBinder(),
-                                new UserHandle(adapter.mUserId));
+                        mgr.bindRemoteViewsService(context.getPackageName(), appWidgetId,
+                                intent, asBinder());
                     } else {
                         Slog.w(TAG, "bind: adapter was null");
                     }
@@ -179,9 +176,7 @@
                 RemoteViewsAdapter adapter;
                 final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
                 if ((adapter = mAdapter.get()) != null) {
-                    checkInteractAcrossUsersPermission(context, adapter.mUserId);
-                    mgr.unbindRemoteViewsService(appWidgetId, intent,
-                            new UserHandle(adapter.mUserId));
+                    mgr.unbindRemoteViewsService(context.getPackageName(), appWidgetId, intent);
                 } else {
                     Slog.w(TAG, "unbind: adapter was null");
                 }
@@ -796,12 +791,10 @@
     static class RemoteViewsCacheKey {
         final Intent.FilterComparison filter;
         final int widgetId;
-        final int userId;
 
-        RemoteViewsCacheKey(Intent.FilterComparison filter, int widgetId, int userId) {
+        RemoteViewsCacheKey(Intent.FilterComparison filter, int widgetId) {
             this.filter = filter;
             this.widgetId = widgetId;
-            this.userId = userId;
         }
 
         @Override
@@ -810,29 +803,28 @@
                 return false;
             }
             RemoteViewsCacheKey other = (RemoteViewsCacheKey) o;
-            return other.filter.equals(filter) && other.widgetId == widgetId
-                    && other.userId == userId;
+            return other.filter.equals(filter) && other.widgetId == widgetId;
         }
 
         @Override
         public int hashCode() {
-            return (filter == null ? 0 : filter.hashCode()) ^ (widgetId << 2) ^ (userId << 10);
+            return (filter == null ? 0 : filter.hashCode()) ^ (widgetId << 2);
         }
     }
 
-    public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
+    public RemoteViewsAdapter(Context context, Intent intent,
+            RemoteAdapterConnectionCallback callback) {
         mContext = context;
         mIntent = intent;
+
         mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
+
         mLayoutInflater = LayoutInflater.from(context);
         if (mIntent == null) {
             throw new IllegalArgumentException("Non-null Intent must be specified.");
         }
         mRequestedViews = new RemoteViewsFrameLayoutRefSet();
 
-        checkInteractAcrossUsersPermission(context, UserHandle.myUserId());
-        mUserId = context.getUserId();
-
         // Strip the previously injected app widget id from service intent
         if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
             intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
@@ -855,7 +847,7 @@
         mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
 
         RemoteViewsCacheKey key = new RemoteViewsCacheKey(new Intent.FilterComparison(mIntent),
-                mAppWidgetId, mUserId);
+                mAppWidgetId);
 
         synchronized(sCachedRemoteViewsCaches) {
             if (sCachedRemoteViewsCaches.containsKey(key)) {
@@ -876,15 +868,6 @@
         }
     }
 
-    private static void checkInteractAcrossUsersPermission(Context context, int userId) {
-        if (context.getUserId() != userId
-                && context.checkCallingOrSelfPermission(MULTI_USER_PERM)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Must have permission " + MULTI_USER_PERM
-                    + " to inflate another user's widget");
-        }
-    }
-
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -906,7 +889,7 @@
 
     public void saveRemoteViewsCache() {
         final RemoteViewsCacheKey key = new RemoteViewsCacheKey(
-                new Intent.FilterComparison(mIntent), mAppWidgetId, mUserId);
+                new Intent.FilterComparison(mIntent), mAppWidgetId);
 
         synchronized(sCachedRemoteViewsCaches) {
             // If we already have a remove runnable posted for this key, remove it.
@@ -1028,7 +1011,6 @@
         long itemId = 0;
         try {
             remoteViews = factory.getViewAt(position);
-            remoteViews.setUser(new UserHandle(mUserId));
             itemId = factory.getItemId(position);
         } catch (RemoteException e) {
             Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index b152297..cf1f554 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -21,8 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.TypedArray;
-import android.os.Handler;
-import android.os.Message;
+import android.os.*;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
@@ -90,7 +89,16 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(Intent.ACTION_USER_PRESENT);
-        getContext().registerReceiver(mReceiver, filter, null, mHandler);
+
+        // OK, this is gross but needed. This class is supported by the
+        // remote views machanism and as a part of that the remote views
+        // can be inflated by a context for another user without the app
+        // having interact users permission - just for loading resources.
+        // For exmaple, when adding widgets from a user profile to the
+        // home screen. Therefore, we register the receiver as the current
+        // user not the one the context is for.
+        getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
+                filter, null, mHandler);
 
         if (mAutoStart) {
             // Automatically start when requested
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f0e7215..59891d6 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -229,7 +229,7 @@
         }
         mAlwaysUseOption = alwaysUseOption;
 
-        int count = mAdapter.getCount();
+        int count = mAdapter.mList.size();
         if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
             // Gulp!
             finish();
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
index 6d51d38..a7f7fe1 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
@@ -16,15 +16,16 @@
 
 package com.android.internal.appwidget;
 
+import android.content.pm.ApplicationInfo;
 import android.content.ComponentName;
 import android.appwidget.AppWidgetProviderInfo;
 import android.widget.RemoteViews;
 
 /** {@hide} */
 oneway interface IAppWidgetHost {
-    void updateAppWidget(int appWidgetId, in RemoteViews views, int userId);
-    void providerChanged(int appWidgetId, in AppWidgetProviderInfo info, int userId);
-    void providersChanged(int userId);
-    void viewDataChanged(int appWidgetId, int viewId, int userId);
+    void updateAppWidget(int appWidgetId, in RemoteViews views);
+    void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
+    void providersChanged();
+    void viewDataChanged(int appWidgetId, int viewId);
 }
 
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 5214dd9..9da1c9d 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -18,6 +18,8 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
 import android.appwidget.AppWidgetProviderInfo;
 import com.android.internal.appwidget.IAppWidgetHost;
 import android.os.Bundle;
@@ -30,34 +32,37 @@
     //
     // for AppWidgetHost
     //
-    int[] startListening(IAppWidgetHost host, String packageName, int hostId,
-            out List<RemoteViews> updatedViews, int userId);
-    void stopListening(int hostId, int userId);
-    int allocateAppWidgetId(String packageName, int hostId, int userId);
-    void deleteAppWidgetId(int appWidgetId, int userId);
-    void deleteHost(int hostId, int userId);
-    void deleteAllHosts(int userId);
-    RemoteViews getAppWidgetViews(int appWidgetId, int userId);
-    int[] getAppWidgetIdsForHost(int hostId, int userId);
+    int[] startListening(IAppWidgetHost host, String callingPackage, int hostId,
+            out List<RemoteViews> updatedViews);
+    void stopListening(String callingPackage, int hostId);
+    int allocateAppWidgetId(String callingPackage, int hostId);
+    void deleteAppWidgetId(String callingPackage, int appWidgetId);
+    void deleteHost(String packageName, int hostId);
+    void deleteAllHosts();
+    RemoteViews getAppWidgetViews(String callingPackage, int appWidgetId);
+    int[] getAppWidgetIdsForHost(String callingPackage, int hostId);
+    IntentSender createAppWidgetConfigIntentSender(String callingPackage, in Intent intent);
 
     //
     // for AppWidgetManager
     //
-    void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views, int userId);
-    void updateAppWidgetOptions(int appWidgetId, in Bundle extras, int userId);
-    Bundle getAppWidgetOptions(int appWidgetId, int userId);
-    void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views, int userId);
-    void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views, int userId);
-    void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId, int userId);
-    List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int userId);
-    AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId, int userId);
+    void updateAppWidgetIds(String callingPackage, in int[] appWidgetIds, in RemoteViews views);
+    void updateAppWidgetOptions(String callingPackage, int appWidgetId, in Bundle extras);
+    Bundle getAppWidgetOptions(String callingPackage, int appWidgetId);
+    void partiallyUpdateAppWidgetIds(String callingPackage, in int[] appWidgetIds,
+            in RemoteViews views);
+    void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
+    void notifyAppWidgetViewDataChanged(String packageName, in int[] appWidgetIds, int viewId);
+    List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter,
+            in int[] profileIds);
+    AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId);
     boolean hasBindAppWidgetPermission(in String packageName, int userId);
-    void setBindAppWidgetPermission(in String packageName, in boolean permission, int userId);
-    void bindAppWidgetId(int appWidgetId, in ComponentName provider, in Bundle options, int userId);
-    boolean bindAppWidgetIdIfAllowed(in String packageName, int appWidgetId,
-            in ComponentName provider, in Bundle options, int userId);
-    void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection, int userId);
-    void unbindRemoteViewsService(int appWidgetId, in Intent intent, int userId);
-    int[] getAppWidgetIds(in ComponentName provider, int userId);
+    void setBindAppWidgetPermission(in String packageName, int userId, in boolean permission);
+    boolean bindAppWidgetId(in String callingPackage, int appWidgetId,
+            int providerProfileId, in ComponentName providerComponent, in Bundle options);
+    void bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
+            in IBinder connection);
+    void unbindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent);
+    int[] getAppWidgetIds(in ComponentName providerComponent);
 }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 7901379..69ffbfe 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -39,7 +39,7 @@
     void notificationLightPulse(int argb, int millisOn, int millisOff);
 
     void showRecentApps(boolean triggeredFromAltTab);
-    void hideRecentApps(boolean triggeredFromAltTab);
+    void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
     void toggleRecentApps();
     void preloadRecentApps();
     void cancelPreloadRecentApps();
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 243ce97..50c82bb 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -55,7 +55,7 @@
     void setWindowState(int window, int state);
 
     void showRecentApps(boolean triggeredFromAltTab);
-    void hideRecentApps(boolean triggeredFromAltTab);
+    void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
     void toggleRecentApps();
     void preloadRecentApps();
     void cancelPreloadRecentApps();
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index c9a0b1e..f0d7a35 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -29,6 +29,7 @@
 #include <utils/Vector.h>
 #include <binder/IMemory.h>
 #include <binder/MemoryDealer.h>
+#include "android_media_AudioFormat.h"
 
 using namespace android;
 
@@ -63,6 +64,7 @@
 static jclass gSoundModelClass;
 static struct {
     jfieldID    uuid;
+    jfieldID    vendorUuid;
     jfieldID    data;
 } gSoundModelFields;
 
@@ -110,6 +112,7 @@
 static struct {
     jfieldID id;
     jfieldID recognitionModes;
+    jfieldID coarseConfidenceLevel;
     jfieldID confidenceLevels;
 } gKeyphraseRecognitionExtraFields;
 
@@ -122,6 +125,16 @@
     jfieldID confidenceLevel;
 } gConfidenceLevelFields;
 
+static const char* const kAudioFormatClassPathName =
+                             "android/media/AudioFormat";
+static jclass gAudioFormatClass;
+static jmethodID gAudioFormatCstor;
+
+static const char* const kSoundModelEventClassPathName =
+                                     "android/hardware/soundtrigger/SoundTrigger$SoundModelEvent";
+static jclass gSoundModelEventClass;
+static jmethodID   gSoundModelEventCstor;
+
 static Mutex gLock;
 
 enum {
@@ -137,6 +150,8 @@
 enum  {
     SOUNDTRIGGER_EVENT_RECOGNITION = 1,
     SOUNDTRIGGER_EVENT_SERVICE_DIED = 2,
+    SOUNDTRIGGER_EVENT_SOUNDMODEL = 3,
+    SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE = 4,
 };
 
 // ----------------------------------------------------------------------------
@@ -148,6 +163,8 @@
     ~JNISoundTriggerCallback();
 
     virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event);
+    virtual void onSoundModelEvent(struct sound_trigger_model_event *event);
+    virtual void onServiceStateChange(sound_trigger_service_state_t state);
     virtual void onServiceDied();
 
 private:
@@ -183,10 +200,9 @@
 void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognition_event *event)
 {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-
-    jobject jEvent;
-
+    jobject jEvent = NULL;
     jbyteArray jData = NULL;
+
     if (event->data_size) {
         jData = env->NewByteArray(event->data_size);
         jbyte *nData = env->GetByteArrayElements(jData, NULL);
@@ -194,6 +210,15 @@
         env->ReleaseByteArrayElements(jData, nData, 0);
     }
 
+    jobject jAudioFormat = NULL;
+    if (event->trigger_in_data) {
+        jAudioFormat = env->NewObject(gAudioFormatClass,
+                                    gAudioFormatCstor,
+                                    audioFormatFromNative(event->audio_config.format),
+                                    event->audio_config.sample_rate,
+                                    inChannelMaskFromNative(event->audio_config.channel_mask));
+
+    }
     if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) {
         struct sound_trigger_phrase_recognition_event *phraseEvent =
                 (struct sound_trigger_phrase_recognition_event *)event;
@@ -225,6 +250,7 @@
                                                gKeyphraseRecognitionExtraCstor,
                                                phraseEvent->phrase_extras[i].id,
                                                phraseEvent->phrase_extras[i].recognition_modes,
+                                               phraseEvent->phrase_extras[i].confidence_level,
                                                jConfidenceLevels);
 
             if (jNewExtra == NULL) {
@@ -236,19 +262,63 @@
         }
         jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor,
                                 event->status, event->model, event->capture_available,
-                               event->capture_session, event->capture_delay_ms,
-                               event->capture_preamble_ms, jData,
-                               phraseEvent->key_phrase_in_capture, jExtras);
+                                event->capture_session, event->capture_delay_ms,
+                                event->capture_preamble_ms, event->trigger_in_data,
+                                jAudioFormat, jData, jExtras);
+        env->DeleteLocalRef(jAudioFormat);
+        env->DeleteLocalRef(jData);
     } else {
         jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor,
                                 event->status, event->model, event->capture_available,
                                 event->capture_session, event->capture_delay_ms,
-                                event->capture_preamble_ms, jData);
+                                event->capture_preamble_ms, event->trigger_in_data,
+                                jAudioFormat, jData);
+        env->DeleteLocalRef(jAudioFormat);
+        env->DeleteLocalRef(jData);
     }
 
 
     env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
                               SOUNDTRIGGER_EVENT_RECOGNITION, 0, 0, jEvent);
+
+    env->DeleteLocalRef(jEvent);
+    if (env->ExceptionCheck()) {
+        ALOGW("An exception occurred while notifying an event.");
+        env->ExceptionClear();
+    }
+}
+
+void JNISoundTriggerCallback::onSoundModelEvent(struct sound_trigger_model_event *event)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jobject jEvent = NULL;
+    jbyteArray jData = NULL;
+
+    if (event->data_size) {
+        jData = env->NewByteArray(event->data_size);
+        jbyte *nData = env->GetByteArrayElements(jData, NULL);
+        memcpy(nData, (char *)event + event->data_offset, event->data_size);
+        env->ReleaseByteArrayElements(jData, nData, 0);
+    }
+
+    jEvent = env->NewObject(gSoundModelEventClass, gSoundModelEventCstor,
+                            event->status, event->model, jData);
+
+    env->DeleteLocalRef(jData);
+    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+                              SOUNDTRIGGER_EVENT_SOUNDMODEL, 0, 0, jEvent);
+    env->DeleteLocalRef(jEvent);
+    if (env->ExceptionCheck()) {
+        ALOGW("An exception occurred while notifying an event.");
+        env->ExceptionClear();
+    }
+}
+
+void JNISoundTriggerCallback::onServiceStateChange(sound_trigger_service_state_t state)
+{
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+                                        SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE, state, 0, NULL);
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred while notifying an event.");
         env->ExceptionClear();
@@ -336,7 +406,7 @@
                                    SOUND_TRIGGER_MAX_STRING_LEN);
         jstring uuid = env->NewStringUTF(str);
 
-        ALOGV("listModules module %d id %d description %s maxSoundModels %d",
+        ALOGV("listModules module %zu id %d description %s maxSoundModels %d",
               i, nModules[i].handle, nModules[i].properties.description,
               nModules[i].properties.max_sound_models);
 
@@ -351,7 +421,8 @@
                                                nModules[i].properties.capture_transition,
                                                nModules[i].properties.max_buffer_ms,
                                                nModules[i].properties.concurrent_capture,
-                                               nModules[i].properties.power_consumption_mw);
+                                               nModules[i].properties.power_consumption_mw,
+                                               nModules[i].properties.trigger_in_event);
 
         env->DeleteLocalRef(implementor);
         env->DeleteLocalRef(description);
@@ -463,6 +534,18 @@
     env->ReleaseStringUTFChars(jUuidString, nUuidString);
     env->DeleteLocalRef(jUuidString);
 
+    sound_trigger_uuid_t nVendorUuid;
+    jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.vendorUuid);
+    if (jUuid != NULL) {
+        jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString);
+        nUuidString = env->GetStringUTFChars(jUuidString, NULL);
+        SoundTrigger::stringToGuid(nUuidString, &nVendorUuid);
+        env->ReleaseStringUTFChars(jUuidString, nUuidString);
+        env->DeleteLocalRef(jUuidString);
+    } else {
+        SoundTrigger::stringToGuid("00000000-0000-0000-0000-000000000000", &nVendorUuid);
+    }
+
     jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data);
     if (jData == NULL) {
         status = SOUNDTRIGGER_STATUS_BAD_VALUE;
@@ -491,6 +574,7 @@
 
     nSoundModel->type = type;
     nSoundModel->uuid = nUuid;
+    nSoundModel->vendor_uuid = nVendorUuid;
     nSoundModel->data_size = size;
     nSoundModel->data_offset = offset;
     memcpy((char *)nSoundModel + offset, nData, size);
@@ -507,7 +591,7 @@
 
         size_t numPhrases = env->GetArrayLength(jPhrases);
         phraseModel->num_phrases = numPhrases;
-        ALOGV("loadSoundModel numPhrases %d", numPhrases);
+        ALOGV("loadSoundModel numPhrases %zu", numPhrases);
         for (size_t i = 0; i < numPhrases; i++) {
             jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
             phraseModel->phrases[i].id =
@@ -539,7 +623,7 @@
             env->DeleteLocalRef(jLocale);
             env->ReleaseStringUTFChars(jText, nText);
             env->DeleteLocalRef(jText);
-            ALOGV("loadSoundModel phrases %d text %s locale %s",
+            ALOGV("loadSoundModel phrases %zu text %s locale %s",
                   i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale);
             env->DeleteLocalRef(jPhrase);
         }
@@ -640,13 +724,15 @@
                                                 gKeyphraseRecognitionExtraFields.id);
         config->phrases[i].recognition_modes = env->GetIntField(jPhrase,
                                                 gKeyphraseRecognitionExtraFields.recognitionModes);
+        config->phrases[i].confidence_level = env->GetIntField(jPhrase,
+                                            gKeyphraseRecognitionExtraFields.coarseConfidenceLevel);
         config->phrases[i].num_levels = 0;
         jobjectArray jConfidenceLevels = (jobjectArray)env->GetObjectField(jPhrase,
                                                 gKeyphraseRecognitionExtraFields.confidenceLevels);
         if (jConfidenceLevels != NULL) {
             config->phrases[i].num_levels = env->GetArrayLength(jConfidenceLevels);
         }
-        ALOGV("startRecognition phrase %d num_levels %d", i, config->phrases[i].num_levels);
+        ALOGV("startRecognition phrase %zu num_levels %d", i, config->phrases[i].num_levels);
         for (size_t j = 0; j < config->phrases[i].num_levels; j++) {
             jobject jConfidenceLevel = env->GetObjectArrayElement(jConfidenceLevels, j);
             config->phrases[i].levels[j].user_id = env->GetIntField(jConfidenceLevel,
@@ -655,7 +741,7 @@
                                                           gConfidenceLevelFields.confidenceLevel);
             env->DeleteLocalRef(jConfidenceLevel);
         }
-        ALOGV("startRecognition phrases %d", i);
+        ALOGV("startRecognition phrases %zu", i);
         env->DeleteLocalRef(jConfidenceLevels);
         env->DeleteLocalRef(jPhrase);
     }
@@ -734,11 +820,12 @@
     jclass modulePropertiesClass = env->FindClass(kModulePropertiesClassPathName);
     gModulePropertiesClass = (jclass) env->NewGlobalRef(modulePropertiesClass);
     gModulePropertiesCstor = env->GetMethodID(modulePropertiesClass, "<init>",
-                              "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZI)V");
+                              "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZIZ)V");
 
     jclass soundModelClass = env->FindClass(kSoundModelClassPathName);
     gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass);
     gSoundModelFields.uuid = env->GetFieldID(soundModelClass, "uuid", "Ljava/util/UUID;");
+    gSoundModelFields.vendorUuid = env->GetFieldID(soundModelClass, "vendorUuid", "Ljava/util/UUID;");
     gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B");
 
     jclass keyphraseClass = env->FindClass(kKeyphraseClassPathName);
@@ -759,12 +846,12 @@
     jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName);
     gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass);
     gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "<init>",
-                                              "(IIZIII[B)V");
+                                              "(IIZIIIZLandroid/media/AudioFormat;[B)V");
 
     jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName);
     gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass);
     gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "<init>",
-              "(IIZIII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V");
+              "(IIZIIIZLandroid/media/AudioFormat;[B[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V");
 
 
     jclass keyRecognitionConfigClass = env->FindClass(kRecognitionConfigClassPathName);
@@ -782,9 +869,12 @@
     jclass keyphraseRecognitionExtraClass = env->FindClass(kKeyphraseRecognitionExtraClassPathName);
     gKeyphraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyphraseRecognitionExtraClass);
     gKeyphraseRecognitionExtraCstor = env->GetMethodID(keyphraseRecognitionExtraClass, "<init>",
-                           "(II[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V");
+                           "(III[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V");
     gKeyphraseRecognitionExtraFields.id = env->GetFieldID(gKeyphraseRecognitionExtraClass, "id", "I");
-    gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass, "recognitionModes", "I");
+    gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass,
+                                                                        "recognitionModes", "I");
+    gKeyphraseRecognitionExtraFields.coarseConfidenceLevel = env->GetFieldID(gKeyphraseRecognitionExtraClass,
+                                                                        "coarseConfidenceLevel", "I");
     gKeyphraseRecognitionExtraFields.confidenceLevels = env->GetFieldID(gKeyphraseRecognitionExtraClass,
                                              "confidenceLevels",
                                              "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;");
@@ -796,6 +886,16 @@
     gConfidenceLevelFields.confidenceLevel = env->GetFieldID(confidenceLevelClass,
                                                              "confidenceLevel", "I");
 
+    jclass audioFormatClass = env->FindClass(kAudioFormatClassPathName);
+    gAudioFormatClass = (jclass) env->NewGlobalRef(audioFormatClass);
+    gAudioFormatCstor = env->GetMethodID(audioFormatClass, "<init>", "(III)V");
+
+    jclass soundModelEventClass = env->FindClass(kSoundModelEventClassPathName);
+    gSoundModelEventClass = (jclass) env->NewGlobalRef(soundModelEventClass);
+    gSoundModelEventCstor = env->GetMethodID(soundModelEventClass, "<init>",
+                                              "(II[B)V");
+
+
     int status = AndroidRuntime::registerNativeMethods(env,
                 kSoundTriggerClassPathName, gMethods, NELEM(gMethods));
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9783e91..3fb084a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -369,6 +369,13 @@
     SurfaceComposerClient::setDisplayProjection(token, orientation, layerStackRect, displayRect);
 }
 
+static void nativeSetDisplaySize(JNIEnv* env, jclass clazz,
+        jobject tokenObj, jint width, jint height) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    if (token == NULL) return;
+    SurfaceComposerClient::setDisplaySize(token, width, height);
+}
+
 static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz,
         jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
@@ -620,6 +627,8 @@
             (void*)nativeSetDisplayLayerStack },
     {"nativeSetDisplayProjection", "(Landroid/os/IBinder;IIIIIIIII)V",
             (void*)nativeSetDisplayProjection },
+    {"nativeSetDisplaySize", "(Landroid/os/IBinder;II)V",
+            (void*)nativeSetDisplaySize },
     {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$PhysicalDisplayInfo;",
             (void*)nativeGetDisplayConfigs },
     {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect1_scale.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect1_scale.xml
new file mode 100644
index 0000000..7c782a6
--- /dev/null
+++ b/core/res/res/anim/progress_indeterminate_horizontal_rect1_scale.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="2016"
+        android:pathData="M 0.1 1 l 0 0 l 0.00882427215576 0 l 0.00982859611511 0
+l 0.01086503982544 0 l 0.01193084716797 0 l 0.0130220413208 0 l 0.01413340568542 0
+l 0.01525821685791 0 l 0.01638801574707 0 l 0.01751272201538 0 l 0.01862035751343 0
+l 0.01969732284546 0 l 0.02072854995728 0 l 0.02169786453247 0 l 0.02258871078491 0
+l 0.02338474273682 0 l 0.02407070159912 0 l 0.02463348388672 0 l 0.0250626373291 0
+l 0.02535140991211 0 l 0.02549694061279 0 l 0.02550048828125 0 l 0.02536708831787 0
+l 0.02510528564453 0 l 0.02472625732422 0 l 0.0242431640625 0 l 0.02367015838623 0
+l 0.02302188873291 0 l 0.02231246948242 0 l 0.02155555725098 0 l 0.02076324462891 0
+l 0.01994682312012 0 l 0.01911575317383 0 l 0.01827827453613 0 l 0.01732414245605 0
+l 0.01522109985352 0 l 0.01262580871582 0 l 0.00973388671875 0 l 0.00647575378418 0
+l 0.0027661895752 0 l -0.00149223327637 0 l -0.00639404296875 0 l -0.01199066162109 0
+l -0.01820671081543 0 l -0.02470901489258 0 l -0.03080444335937 0 l -0.0355574798584 0
+l -0.03823974609375 0 l -0.03876884460449 0 l -0.03766212463379 0 l -0.03562252044678 0
+l -0.03321434020996 0 l -0.03078151702881 0 l -0.02849582672119 0 l -0.02642543792725 0
+l -0.02458423614502 0 l -0.02296115875244 0 l -0.02153518676758 0 l -0.02028285980225 0
+l -0.01918155670166 0 l -0.01821084976196 0 l -0.01735286712646 0 l -0.01659231185913 0
+l -0.01591604232788 0 l -0.0153129196167 0 l -0.01477350234985 0 l -0.01413362503052 0
+l -0.01339265823364 0 l -0.01270362854004 0 l -0.01206108093262 0 l -0.01146033287048 0
+l -0.01089729309082 0 l -0.01036835670471 0 l -0.00987038612366 0 l -0.00940062522888 0
+l -0.00895661354065 0 l -0.00853617668152 0"
+        android:propertyXName="scaleX"
+        android:repeatCount="infinite" />
+
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect1_translate.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect1_translate.xml
new file mode 100644
index 0000000..c26bb5d
--- /dev/null
+++ b/core/res/res/anim/progress_indeterminate_horizontal_rect1_translate.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="2016"
+        android:pathData="M -522.599975585938 0 l 0 0 l 0.12939453125 0
+l 0.33831787109375 0 l 0.55450439453125 0 l 0.7708740234375 0 l 0.98065185546875 0
+l 1.1964111328125 0 l 1.41351318359375 0 l 1.63153076171875 0 l 1.85052490234375 0
+l 2.07052612304688 0 l 2.29080200195312 0 l 2.51150512695312 0 l 2.73260498046875 0
+l 2.95355224609375 0 l 3.17404174804688 0 l 3.39422607421875 0 l 3.61355590820312 0
+l 3.83163452148438 0 l 4.04849243164062 0 l 4.263671875 0 l 5.74725341796875 0
+l 6.1026611328125 0 l 6.45980834960938 0 l 6.81781005859375 0 l 7.17654418945312 0
+l 7.53366088867188 0 l 7.88861083984375 0 l 8.23974609375 0 l 8.58447265625 0
+l 8.92156982421875 0 l 9.24810791015625 0 l 9.56137084960938 0 l 9.85906982421875 0
+l 10.1377868652344 0 l 10.3955688476562 0 l 10.6287536621094 0 l 10.8357238769531 0
+l 11.0149230957031 0 l 11.1639709472656 0 l 11.2832336425781 0 l 11.3713989257812 0
+l 11.4301147460938 0 l 11.4596557617188 0 l 11.4611053466797 0 l 11.4369049072266 0
+l 11.3887786865234 0 l 11.3183441162109 0 l 11.2276000976562 0 l 11.1185607910156 0
+l 10.9933776855469 0 l 10.8534698486328 0 l 10.6995391845703 0 l 10.533935546875 0
+l 10.3744659423828 0 l 10.3707733154297 0 l 10.4309463500977 0 l 10.5275726318359 0
+l 10.671501159668 0 l 10.8763961791992 0 l 11.1566543579102 0 l 11.5270767211914 0
+l 11.9947967529297 0 l 12.5502433776855 0 l 13.1453399658203 0 l 13.680793762207 0
+l 14.0223298072815 0 l 14.0650296211243 0 l 13.798041343689 0 l 13.2949924468994 0
+l 12.6584892272949 0 l 11.9693031311035 0 l 11.2772979736328 0 l 10.607666015625 0
+l 9.97052764892578 0 l 9.36723327636719 0 l 8.79751586914062 0 l 8.25792694091797 0
+l 7.74495697021484 0 l 7.25632476806641 0 l 6.78855895996094 0 l 6.33934020996094 0
+l 5.9071044921875 0 l 5.48941040039062 0 l 5.08502197265625 0 l 4.69291687011719 0
+l 4.33430480957031 0 l 4.00733947753906 0 l 3.68829345703125 0 l 3.37684631347656 0
+l 3.07246398925781 0 l 2.77439880371094 0 l 2.48252868652344 0 l 2.20101928710938 0
+l 1.91748046875 0 l 1.63726806640625 0 l 1.36772155761719 0"
+        android:propertyXName="translateX"
+        android:repeatCount="infinite" />
+
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect2_scale.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect2_scale.xml
new file mode 100644
index 0000000..ef1677d
--- /dev/null
+++ b/core/res/res/anim/progress_indeterminate_horizontal_rect2_scale.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="2016"
+        android:pathData="M 0.1 1 l 0.00930031776428 0 l 0.01123028755188 0
+l 0.01313143730164 0 l 0.01497107505798 0 l 0.01671510696411 0 l 0.01833034515381 0
+l 0.01978672027588 0 l 0.02105976104736 0 l 0.02213228225708 0 l 0.02299520492554 0
+l 0.02364795684814 0 l 0.02409727096558 0 l 0.02435619354248 0 l 0.02444213867188 0
+l 0.02437515258789 0 l 0.02417644500732 0 l 0.02386695861816 0 l 0.02346652984619 0
+l 0.02299335479736 0 l 0.0224634552002 0 l 0.02189086914062 0 l 0.02128746032715 0
+l 0.02066318511963 0 l 0.02002624511719 0 l 0.01938335418701 0 l 0.01873977661133 0
+l 0.01809989929199 0 l 0.01746696472168 0 l 0.01684349060059 0 l 0.01623161315918 0
+l 0.0156324005127 0 l 0.0150471496582 0 l 0.01447631835938 0 l 0.01392051696777 0
+l 0.01337966918945 0 l 0.0128540802002 0 l 0.01234344482422 0 l 0.01184753417969 0
+l 0.0113663482666 0 l 0.01089920043945 0 l 0.01044593811035 0 l 0.00998542785645 0
+l 0.00933837890625 0 l 0.00863349914551 0 l 0.00791206359863 0 l 0.00717010498047 0
+l 0.00640274047852 0 l 0.00560478210449 0 l 0.00477012634277 0 l 0.00389221191406 0
+l 0.00296325683594 0 l 0.0019751739502 0 l 0.00091903686523 0 l -0.00021408081055 0
+l -0.00143287658691 0 l -0.00274444580078 0 l -0.00415267944336 0 l -0.00565589904785 0
+l -0.00724327087402 0 l -0.00889205932617 0 l -0.01056480407715 0 l -0.01220878601074 0
+l -0.01376045227051 0 l -0.01515449523926 0 l -0.01633560180664 0 l -0.01726905822754 0
+l -0.01794639587402 0 l -0.0183829498291 0 l -0.01861137390137 0 l -0.01867179870605 0
+l -0.01860504150391 0 l -0.01844764709473 0 l -0.01822959899902 0 l -0.01797431945801 0
+l -0.0176993560791 0 l -0.0174169921875 0 l -0.01713603973389 0 l -0.01686214447021 0
+l -0.01651359558105 0 l -0.01609485626221 0 l -0.01569358825684 0 l -0.01531024932861 0
+l -0.0149446105957 0 l -0.01459632873535 0 l -0.01426464080811 0 l -0.0139489364624 0
+l -0.01364833831787 0 l -0.01336200714111 0 l -0.01308917999268 0 l -0.01282897949219 0
+l -0.01258075714111 0 l -0.01234363555908 0 l -0.01211700439453 0 l -0.01190029144287 0
+l -0.01169273376465 0 l -0.01149394989014 0 l -0.01130325317383 0 l -0.01112024307251 0
+l -0.01094444274902 0 l -0.01077545166016 0 l -0.0106128692627 0 l -0.01045631408691 0
+l -0.01030544281006 0 l -0.01016000747681 0 l -0.01001962661743 0 l -0.0098840713501 0
+l -0.00975311279297 0 l -0.00962644577026 0 l -0.00950393676758 0 l -0.00938529968262 0
+l -0.00927038192749 0 l -0.00915899276733 0 l -0.00905097961426 0 l -0.00894614219666 0
+l -0.00884438514709 0 l -0.00874552726746 0 l -0.00864946365356 0 l -0.00855606079102 0
+l -0.00846519470215 0 l -0.00837676048279 0 "
+        android:propertyXName="scaleX"
+        android:repeatCount="infinite" />
+
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/progress_indeterminate_horizontal_rect2_translate.xml b/core/res/res/anim/progress_indeterminate_horizontal_rect2_translate.xml
new file mode 100644
index 0000000..f4cf83d
--- /dev/null
+++ b/core/res/res/anim/progress_indeterminate_horizontal_rect2_translate.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="2016"
+        android:pathData="M -197.600006103516 0 l 1.42625427246094 0
+l 1.80754089355469 0 l 2.18778991699219 0 l 2.56109619140625 0 l 2.91810607910156 0
+l 3.25482177734375 0 l 3.57159423828125 0 l 3.862548828125 0 l 4.12493896484375 0
+l 4.35758972167969 0 l 4.56034851074219 0 l 4.73426818847656 0 l 4.88090515136719 0
+l 5.00271606445312 0 l 5.10273742675781 0 l 5.18400573730469 0 l 5.24911499023438 0
+l 5.30097961425781 0 l 5.34226226806641 0 l 5.37535095214844 0 l 5.40180206298828 0
+l 5.42322540283203 0 l 5.44123077392578 0 l 5.45704650878906 0 l 5.47099304199219 0
+l 5.48395538330078 0 l 5.4967041015625 0 l 5.50949859619141 0 l 5.52214813232422 0
+l 5.53528594970703 0 l 5.54912567138672 0 l 5.56306457519531 0 l 5.57742691040039 0
+l 5.59244155883789 0 l 5.60744094848633 0 l 5.62243270874023 0 l 5.6376781463623 0
+l 5.65262794494629 0 l 5.66689777374268 0 l 5.68069934844971 0 l 5.69401162862778 0
+l 5.70898681879044 0 l 5.75169992446899 0 l 5.80327129364014 0 l 5.85710144042969 0
+l 5.91399765014648 0 l 5.97450065612793 0 l 6.03849411010742 0 l 6.10729217529297 0
+l 6.18125534057617 0 l 6.26116561889648 0 l 6.34840393066406 0 l 6.44406127929688 0
+l 6.54866790771484 0 l 6.66371917724609 0 l 6.79020690917969 0 l 6.92859649658203 0
+l 7.07807159423828 0 l 7.23712158203125 0 l 7.40253448486328 0 l 7.56884765625 0
+l 7.72840881347656 0 l 7.87199401855469 0 l 7.98992919921875 0 l 8.07417297363281 0
+l 8.12013244628906 0 l 8.12655639648438 0 l 8.09510803222656 0 l 8.03091430664062 0
+l 7.93995666503906 0 l 7.827880859375 0 l 7.69976806640625 0 l 7.56065368652344 0
+l 7.41322326660156 0 l 7.26063537597656 0 l 7.10470581054688 0 l 6.94624328613281 0
+l 6.78694152832031 0 l 6.6390380859375 0 l 6.50302124023438 0 l 6.36688232421875 0
+l 6.23043823242188 0 l 6.09356689453125 0 l 5.95706176757812 0 l 5.82064819335938 0
+l 5.6839599609375 0 l 5.5477294921875 0 l 5.41143798828125 0 l 5.27532958984375 0
+l 5.13922119140625 0 l 5.00347900390625 0 l 4.8680419921875 0 l 4.73251342773438 0
+l 4.59732055664062 0 l 4.46258544921875 0 l 4.328125 0 l 4.1937255859375 0
+l 4.0599365234375 0 l 3.92672729492188 0 l 3.79376220703125 0 l 3.66119384765625 0
+l 3.52935791015625 0 l 3.398193359375 0 l 3.26748657226562 0 l 3.13726806640625 0
+l 3.00796508789062 0 l 2.87939453125 0 l 2.7515869140625 0 l 2.62445068359375 0
+l 2.49810791015625 0 l 2.3726806640625 0 l 2.2481689453125 0 l 2.12457275390625 0
+l 2.00173950195312 0 l 1.87997436523438 0 l 1.7618408203125 0 l 1.64154052734375 0
+l 1.51962280273438 0 l 1.40017700195312 0 l 1.28421020507812 0 "
+        android:propertyXName="translateX"
+        android:repeatCount="infinite" />
+
+</set>
\ No newline at end of file
diff --git a/core/res/res/drawable/progress_indeterminate_horizontal_material.xml b/core/res/res/drawable/progress_indeterminate_horizontal_material.xml
new file mode 100644
index 0000000..4fc68ce
--- /dev/null
+++ b/core/res/res/drawable/progress_indeterminate_horizontal_material.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal" >
+
+    <target
+        android:name="path1"
+        android:animation="@anim/progress_indeterminate_horizontal_rect1_translate" />
+    <target
+        android:name="path1"
+        android:animation="@anim/progress_indeterminate_horizontal_rect1_scale" />
+
+    <target
+        android:name="path2"
+        android:animation="@anim/progress_indeterminate_horizontal_rect2_translate" />
+    <target
+        android:name="path2"
+        android:animation="@anim/progress_indeterminate_horizontal_rect2_scale" />
+</animated-vector>
diff --git a/core/res/res/drawable/vector_drawable_progress_indeterminate_horizontal.xml b/core/res/res/drawable/vector_drawable_progress_indeterminate_horizontal.xml
new file mode 100644
index 0000000..0cc7202
--- /dev/null
+++ b/core/res/res/drawable/vector_drawable_progress_indeterminate_horizontal.xml
@@ -0,0 +1,60 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="4dp"
+    android:viewportHeight="4"
+    android:viewportWidth="360"
+    android:width="360dp" >
+
+    <group
+        android:name="linear_indeterminate"
+        android:translateX="180.0"
+        android:translateY="0.0" >
+        <group
+            android:name="path1"
+            android:scaleX="0.1"
+            android:translateX="-522.59" >
+            <path
+                android:name="rect1"
+                android:fillColor="?attr/colorControlActivated"
+                android:pathData="m 0 1.6 l 288 0 l 0 0.8 l -288 0 z" />
+        </group>
+        <group
+            android:name="path2"
+            android:scaleX="0.1"
+            android:translateX="-197.6" >
+            <path
+                android:name="rect2"
+                android:fillColor="?attr/colorControlActivated"
+                android:pathData="m 0 1.6 l 288 0 l 0 0.8 l -288 0 z" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e94a046..2311e67 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4865,6 +4865,15 @@
     <!-- TV content rating system strings for CO TV -->
 
     <!-- TV content rating system strings for DE TV -->
+    <string name="display_name_detv" translatable="false">DE-TV</string>
+    <string name="display_name_detv_all" translatable="false">ab 0 Jahren</string>
+    <string name="display_name_detv_12" translatable="false">ab 12 Jahren</string>
+    <string name="display_name_detv_16" translatable="false">ab 16 Jahren</string>
+    <string name="display_name_detv_18" translatable="false">ab 18 Jahren</string>
+    <string name="description_detv_all">Die nachfolgende Sendung ist für alle alters geeignet.</string>
+    <string name="description_detv_12">Die nachfolgende Sendung ist für Zuschauer unter 12 Jahren nicht geeignet.</string>
+    <string name="description_detv_16">Die nachfolgende Sendung ist für Zuschauer unter 16 Jahren nicht geeignet.</string>
+    <string name="description_detv_18">Die nachfolgende Sendung ist für Zuschauer unter 18 Jahren nicht geeignet.</string>
 
     <!-- TV content rating system strings for DK TV -->
 
@@ -4873,6 +4882,17 @@
     <!-- TV content rating system strings for FI TV -->
 
     <!-- TV content rating system strings for FR TV -->
+    <string name="display_name_frtv" translatable="false">FR-TV</string>
+    <string name="display_name_frtv_all" translatable="false">Les programmes tous publics</string>
+    <string name="display_name_frtv_10" translatable="false">Déconseillé aux -10 ans</string>
+    <string name="display_name_frtv_12" translatable="false">Déconseillé aux -12 ans</string>
+    <string name="display_name_frtv_16" translatable="false">Déconseillé aux -16 ans</string>
+    <string name="display_name_frtv_18" translatable="false">Déconseillé aux -18 ans</string>
+    <string name="description_frtv_all">Les programmes tous publics</string>
+    <string name="description_frtv_10">Programmes comportant certaines scènes susceptibles de heurter les -10 ans.</string>
+    <string name="description_frtv_12">Programmes pouvant troubler les -12 ans, notamment lorsque le scénario recourt de façon systématique et répétée à la violence physique ou psychologique.</string>
+    <string name="description_frtv_16">Programmes à caractère érotique ou de grande violence, susceptibles de nuire à l’épanouissement physique, mental ou moral des -16 ans.</string>
+    <string name="description_frtv_18">Programmes pornographiques ou de très grande violence, réservés à un public adulte averti et susceptibles de nuire à l’épanouissement physique, mental ou moral des -18 ans.</string>
 
     <!-- TV content rating system strings for GR TV -->
 
@@ -4914,6 +4934,29 @@
     <!-- TV content rating system strings for MY TV -->
 
     <!-- TV content rating system strings for NL TV -->
+    <string name="display_name_nltv" translatable="false">NL-TV</string>
+    <string name="display_name_nltv_v" translatable="false">Geweld</string>
+    <string name="display_name_nltv_f" translatable="false">Angst</string>
+    <string name="display_name_nltv_s" translatable="false">Seks</string>
+    <string name="display_name_nltv_d" translatable="false">Discriminatie</string>
+    <string name="display_name_nltv_da" translatable="false">Drugs- en/of alcoholmisbruik</string>
+    <string name="display_name_nltv_l" translatable="false">Grof taalgebruik</string>
+    <string name="display_name_nltv_al" translatable="false">Alle leeftijden</string>
+    <string name="display_name_nltv_6" translatable="false">Let op met kinderen tot 6 jaar</string>
+    <string name="display_name_nltv_9" translatable="false">Let op met kinderen tot 9 jaar</string>
+    <string name="display_name_nltv_12" translatable="false">Let op met kinderen tot 12 jaar</string>
+    <string name="display_name_nltv_16" translatable="false">Let op met kinderen tot 16 jaar</string>
+    <string name="description_nltv_v">Geweld</string>
+    <string name="description_nltv_f">Angst</string>
+    <string name="description_nltv_s">Seks</string>
+    <string name="description_nltv_d">Discriminatie</string>
+    <string name="description_nltv_da">Drugs- en/of alcoholmisbruik</string>
+    <string name="description_nltv_l">Grof taalgebruik</string>
+    <string name="description_nltv_al">De leeftijdscategorie Alle Leeftijden geeft aan dat een mediaproductie geen schadelijke elementen bevat.</string>
+    <string name="description_nltv_6">Mogelijk schadelijk voor kinderen onder de 6 jaar.</string>
+    <string name="description_nltv_9">Mogelijk schadelijk voor kinderen onder de 9 jaar.</string>
+    <string name="description_nltv_12">Mogelijk schadelijk voor kinderen onder de 12 jaar.</string>
+    <string name="description_nltv_16">Mogelijk schadelijk voor kinderen onder de 16 jaar.</string>
 
     <!-- TV content rating system strings for NZ TV -->
 
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 9ee377f..2dc0438 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -652,7 +652,7 @@
 
     <style name="Widget.Material.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal">
         <item name="progressDrawable">@drawable/progress_horizontal_material</item>
-        <item name="indeterminateDrawable">@drawable/progress_indeterminate_horizontal_holo</item>
+        <item name="indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material</item>
         <item name="minHeight">16dip</item>
         <item name="maxHeight">16dip</item>
     </style>
diff --git a/core/res/res/xml/tv_content_rating_systems.xml b/core/res/res/xml/tv_content_rating_systems.xml
index 57fd2ad..2df091d 100644
--- a/core/res/res/xml/tv_content_rating_systems.xml
+++ b/core/res/res/xml/tv_content_rating_systems.xml
@@ -36,6 +36,32 @@
     <!-- TV content rating system for CO TV -->
 
     <!-- TV content rating system for DE TV -->
+    <rating-system-definition id="DE_TV"
+        displayName="@string/display_name_detv"
+        country="DE">
+        <rating-definition id="DE_TV_ALL"
+            displayName="@string/display_name_detv_all"
+            description="@string/description_detv_all"
+            ageHint="0" />
+        <rating-definition id="DE_TV_12"
+            displayName="@string/display_name_detv_12"
+            description="@string/description_detv_12"
+            ageHint="12" />
+        <rating-definition id="DE_TV_16"
+            displayName="@string/display_name_detv_16"
+            description="@string/description_detv_16"
+            ageHint="16" />
+        <rating-definition id="DE_TV_18"
+            displayName="@string/display_name_detv_18"
+            description="@string/description_detv_18"
+            ageHint="18" />
+        <order>
+            <rating id="DE_TV_ALL" />
+            <rating id="DE_TV_12" />
+            <rating id="DE_TV_16" />
+            <rating id="DE_TV_18" />
+        </order>
+    </rating-system-definition>
 
     <!-- TV content rating system for DK TV -->
 
@@ -44,6 +70,41 @@
     <!-- TV content rating system for FI TV -->
 
     <!-- TV content rating system for FR TV -->
+    <rating-system-definition id="FR_TV"
+        displayName="@string/display_name_frtv"
+        country="FR">
+        <rating-definition id="FR_TV_ALL"
+            displayName="@string/display_name_frtv_all"
+            description="@string/description_frtv_all"
+            ageHint="0" />
+        <rating-definition id="FR_TV_10"
+            displayName="@string/display_name_frtv_10"
+            description="@string/description_frtv_10"
+            ageHint="10">
+        </rating-definition>
+        <rating-definition id="FR_TV_12"
+            displayName="@string/display_name_frtv_12"
+            description="@string/description_frtv_12"
+            ageHint="12">
+        </rating-definition>
+        <rating-definition id="FR_TV_16"
+            displayName="@string/display_name_frtv_16"
+            description="@string/description_frtv_16"
+            ageHint="16">
+        </rating-definition>
+        <rating-definition id="FR_TV_18"
+            displayName="@string/display_name_frtv_18"
+            description="@string/description_frtv_18"
+            ageHint="18">
+        </rating-definition>
+        <order>
+            <rating id="FR_TV_ALL" />
+            <rating id="FR_TV_10" />
+            <rating id="FR_TV_12" />
+            <rating id="FR_TV_16" />
+            <rating id="FR_TV_18" />
+        </order>
+    </rating-system-definition>
 
     <!-- TV content rating system for GR TV -->
 
@@ -105,6 +166,92 @@
     <!-- TV content rating system for MY TV -->
 
     <!-- TV content rating system for NL TV -->
+    <rating-system-definition id="NL_TV"
+        displayName="@string/display_name_nltv"
+        country="NL">
+        <sub-rating-definition id="NL_TV_V"
+            displayName="@string/display_name_nltv_v"
+            description="@string/description_nltv_v" />
+        <sub-rating-definition id="NL_TV_F"
+            displayName="@string/display_name_nltv_f"
+            description="@string/description_nltv_f" />
+        <sub-rating-definition id="NL_TV_S"
+            displayName="@string/display_name_nltv_s"
+            description="@string/description_nltv_s" />
+        <sub-rating-definition id="NL_TV_D"
+            displayName="@string/display_name_nltv_d"
+            description="@string/description_nltv_d" />
+        <sub-rating-definition id="NL_TV_DA"
+            displayName="@string/display_name_nltv_da"
+            description="@string/description_nltv_da" />
+        <sub-rating-definition id="NL_TV_L"
+            displayName="@string/display_name_nltv_l"
+            description="@string/description_nltv_l" />
+
+        <rating-definition id="NL_TV_AL"
+            displayName="@string/display_name_nltv_al"
+            description="@string/description_nltv_al"
+            ageHint="0">
+            <sub-rating id="NL_TV_V" />
+            <sub-rating id="NL_TV_F" />
+            <sub-rating id="NL_TV_S" />
+            <sub-rating id="NL_TV_D" />
+            <sub-rating id="NL_TV_DA" />
+            <sub-rating id="NL_TV_L" />
+        </rating-definition>
+        <rating-definition id="NL_TV_6"
+            displayName="@string/display_name_nltv_6"
+            description="@string/description_nltv_6"
+            ageHint="6">
+            <sub-rating id="NL_TV_V" />
+            <sub-rating id="NL_TV_F" />
+            <sub-rating id="NL_TV_S" />
+            <sub-rating id="NL_TV_D" />
+            <sub-rating id="NL_TV_DA" />
+            <sub-rating id="NL_TV_L" />
+        </rating-definition>
+        <rating-definition id="NL_TV_9"
+            displayName="@string/display_name_nltv_9"
+            description="@string/description_nltv_9"
+            ageHint="9">
+            <sub-rating id="NL_TV_V" />
+            <sub-rating id="NL_TV_F" />
+            <sub-rating id="NL_TV_S" />
+            <sub-rating id="NL_TV_D" />
+            <sub-rating id="NL_TV_DA" />
+            <sub-rating id="NL_TV_L" />
+        </rating-definition>
+        <rating-definition id="NL_TV_12"
+            displayName="@string/display_name_nltv_12"
+            description="@string/description_nltv_12"
+            ageHint="12">
+            <sub-rating id="NL_TV_V" />
+            <sub-rating id="NL_TV_F" />
+            <sub-rating id="NL_TV_S" />
+            <sub-rating id="NL_TV_D" />
+            <sub-rating id="NL_TV_DA" />
+            <sub-rating id="NL_TV_L" />
+        </rating-definition>
+        <rating-definition id="NL_TV_16"
+            displayName="@string/display_name_nltv_16"
+            description="@string/description_nltv_16"
+            ageHint="16">
+            <sub-rating id="NL_TV_V" />
+            <sub-rating id="NL_TV_F" />
+            <sub-rating id="NL_TV_S" />
+            <sub-rating id="NL_TV_D" />
+            <sub-rating id="NL_TV_DA" />
+            <sub-rating id="NL_TV_L" />
+        </rating-definition>
+
+        <order>
+            <rating id="NL_TV_AL" />
+            <rating id="NL_TV_6" />
+            <rating id="NL_TV_9" />
+            <rating id="NL_TV_12" />
+            <rating id="NL_TV_16" />
+        </order>
+    </rating-system-definition>
 
     <!-- TV content rating system for NZ TV -->
 
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 4b00e22..4d0bb75 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Trace;
 import android.util.DisplayMetrics;
 
 import java.io.OutputStream;
@@ -1004,8 +1005,11 @@
         if (quality < 0 || quality > 100) {
             throw new IllegalArgumentException("quality must be 0..100");
         }
-        return nativeCompress(mNativeBitmap, format.nativeInt, quality,
+        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
+        boolean result = nativeCompress(mNativeBitmap, format.nativeInt, quality,
                               stream, new byte[WORKING_COMPRESS_STORAGE]);
+        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+        return result;
     }
 
     /**
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index f451690..3ef2a71 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -334,8 +334,10 @@
 const char* gFS_Main_FragColor_HasRoundRectClip =
         "    mediump vec2 fragToLT = roundRectInnerRectLTRB.xy - roundRectPos;\n"
         "    mediump vec2 fragFromRB = roundRectPos - roundRectInnerRectLTRB.zw;\n"
-        "    mediump vec2 dist = max(max(fragToLT, fragFromRB), vec2(0.0, 0.0));\n"
-        "    mediump float linearDist = roundRectRadius - length(dist);\n"
+
+        // divide + multiply by 128 to avoid falling out of range in length() function
+        "    mediump vec2 dist = max(max(fragToLT, fragFromRB), vec2(0.0, 0.0)) / 128.0;\n"
+        "    mediump float linearDist = roundRectRadius - (length(dist) * 128.0);\n"
         "    gl_FragColor *= clamp(linearDist, 0.0, 1.0);\n";
 
 const char* gFS_Main_DebugHighlight =
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index fa1b21d..237d500 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -184,7 +184,9 @@
         return;
     }
 
-    if (!dirty.isEmpty()) {
+
+    if (dirty.intersect(0, 0, getWidth(), getHeight())) {
+        dirty.roundOut();
         mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
     }
     // This is not inside the above if because we may have called
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 8d99d6a1..d93d81b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -249,6 +249,20 @@
     private AudioFormat(int ignoredArgument) {
     }
 
+    /**
+     * Constructor used by the JNI
+     */
+    // Update sound trigger JNI in core/jni/android_hardware_SoundTrigger.cpp when modifying this
+    // constructor
+    private AudioFormat(int encoding, int sampleRate, int channelMask) {
+        mEncoding = encoding;
+        mSampleRate = sampleRate;
+        mChannelMask = channelMask;
+        mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_ENCODING |
+                AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE |
+                AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK;
+    }
+
     /** @hide */
     public final static int AUDIO_FORMAT_HAS_PROPERTY_NONE = 0x0;
     /** @hide */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index ac63ea6..bd50142 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -834,6 +834,7 @@
     /**
      * Get the stream type whose volume is driving the UI sounds volume.
      * UI sounds are screen lock/unlock, camera shutter, key clicks...
+     * It is assumed that this stream type is also tied to ringer mode changes.
      * @hide
      */
     public int getMasterStreamType() {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index e1c6e75..705d9c0 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -291,14 +291,14 @@
     };
     private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
         AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
-        AudioSystem.STREAM_MUSIC,           // STREAM_SYSTEM
+        AudioSystem.STREAM_RING,            // STREAM_SYSTEM
         AudioSystem.STREAM_RING,            // STREAM_RING
         AudioSystem.STREAM_MUSIC,           // STREAM_MUSIC
         AudioSystem.STREAM_ALARM,           // STREAM_ALARM
         AudioSystem.STREAM_RING,            // STREAM_NOTIFICATION
         AudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCO
-        AudioSystem.STREAM_MUSIC,           // STREAM_SYSTEM_ENFORCED
-        AudioSystem.STREAM_MUSIC,           // STREAM_DTMF
+        AudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCED
+        AudioSystem.STREAM_RING,            // STREAM_DTMF
         AudioSystem.STREAM_MUSIC            // STREAM_TTS
     };
     private int[] mStreamVolumeAlias;
@@ -1572,15 +1572,7 @@
 
     /** @see AudioManager#getMasterStreamType()  */
     public int getMasterStreamType() {
-        switch (mPlatformType) {
-            case PLATFORM_VOICE:
-                return AudioSystem.STREAM_RING;
-            case PLATFORM_TELEVISION:
-                return AudioSystem.STREAM_MUSIC;
-            default:
-                break;
-        }
-        return AudioSystem.STREAM_NOTIFICATION;
+        return mStreamVolumeAlias[AudioSystem.STREAM_SYSTEM];
     }
 
     /** @see AudioManager#setMicrophoneMute(boolean) */
@@ -4340,7 +4332,7 @@
             AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
             AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_HDMI |
             AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
-            AudioSystem.DEVICE_OUT_ALL_USB;
+            AudioSystem.DEVICE_OUT_ALL_USB | AudioSystem.DEVICE_OUT_LINE;
 
     // must be called before removing the device from mConnectedDevices
     private int checkSendBecomingNoisyIntent(int device, int state) {
@@ -4386,7 +4378,9 @@
             connType = AudioRoutesInfo.MAIN_HEADSET;
             intent.setAction(Intent.ACTION_HEADSET_PLUG);
             intent.putExtra("microphone", 1);
-        } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
+        } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE ||
+                   device == AudioSystem.DEVICE_OUT_LINE) {
+            /*do apps care about line-out vs headphones?*/
             connType = AudioRoutesInfo.MAIN_HEADPHONES;
             intent.setAction(Intent.ACTION_HEADSET_PLUG);
             intent.putExtra("microphone", 0);
@@ -4429,7 +4423,8 @@
     {
         synchronized (mConnectedDevices) {
             if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
-                    (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
+                    (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
+                    (device == AudioSystem.DEVICE_OUT_LINE))) {
                 setBluetoothA2dpOnInt(true);
             }
             boolean isUsb = ((device & ~AudioSystem.DEVICE_OUT_ALL_USB) == 0) ||
@@ -4438,7 +4433,8 @@
             handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
             if (state != 0) {
                 if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
-                    (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)) {
+                    (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
+                    (device == AudioSystem.DEVICE_OUT_LINE)) {
                     setBluetoothA2dpOnInt(false);
                 }
                 if ((device & mSafeMediaVolumeDevices) != 0) {
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 3f7ebce..bbf6a3b 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -15,10 +15,14 @@
  */
 package android.media;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Bitmap;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.text.format.Time;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -115,7 +119,7 @@
     public static final String METADATA_KEY_ART = "android.media.metadata.ART";
 
     /**
-     * The artwork for the media as a Uri style String.
+     * The artwork for the media as a Uri.
      */
     public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
 
@@ -126,8 +130,7 @@
     public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
 
     /**
-     * The artwork for the album of the media's original source as a Uri style
-     * String.
+     * The artwork for the album of the media's original source as a Uri.
      */
     public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
 
@@ -145,36 +148,104 @@
      */
     public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
 
+    /**
+     * A title that is suitable for display to the user. This will generally be
+     * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
+     * When displaying media described by this metadata this should be preferred
+     * if present.
+     */
+    public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
+
+    /**
+     * A subtitle that is suitable for display to the user. When displaying a
+     * second line for media described by this metadata this should be preferred
+     * to other fields if present.
+     */
+    public static final String METADATA_KEY_DISPLAY_SUBTITLE
+            = "android.media.metadata.DISPLAY_SUBTITLE";
+
+    /**
+     * A description that is suitable for display to the user. When displaying
+     * more information for media described by this metadata this should be
+     * preferred to other fields if present.
+     */
+    public static final String METADATA_KEY_DISPLAY_DESCRIPTION
+            = "android.media.metadata.DISPLAY_DESCRIPTION";
+
+    /**
+     * An icon or thumbnail that is suitable for display to the user. When
+     * displaying an icon for media described by this metadata this should be
+     * preferred to other fields if present. This must be a {@link Bitmap}.
+     */
+    public static final String METADATA_KEY_DISPLAY_ICON
+            = "android.media.metadata.DISPLAY_ICON";
+
+    /**
+     * An icon or thumbnail that is suitable for display to the user. When
+     * displaying more information for media described by this metadata the
+     * display description should be preferred to other fields when present.
+     */
+    public static final String METADATA_KEY_DISPLAY_ICON_URI
+            = "android.media.metadata.DISPLAY_ICON_URI";
+
+    private static final String[] PREFERRED_DESCRIPTION_ORDER = {
+            METADATA_KEY_TITLE,
+            METADATA_KEY_ALBUM,
+            METADATA_KEY_ARTIST,
+            METADATA_KEY_ALBUM_ARTIST,
+            METADATA_KEY_WRITER,
+            METADATA_KEY_AUTHOR,
+            METADATA_KEY_COMPOSER
+    };
+
+    private static final String[] PREFERRED_BITMAP_ORDER = {
+            METADATA_KEY_DISPLAY_ICON,
+            METADATA_KEY_ART,
+            METADATA_KEY_ALBUM_ART
+    };
+
+    private static final String[] PREFERRED_URI_ORDER = {
+            METADATA_KEY_DISPLAY_ICON_URI,
+            METADATA_KEY_ART_URI,
+            METADATA_KEY_ALBUM_ART_URI
+    };
+
     private static final int METADATA_TYPE_INVALID = -1;
     private static final int METADATA_TYPE_LONG = 0;
-    private static final int METADATA_TYPE_STRING = 1;
+    private static final int METADATA_TYPE_TEXT = 1;
     private static final int METADATA_TYPE_BITMAP = 2;
     private static final int METADATA_TYPE_RATING = 3;
+    private static final int METADATA_TYPE_URI = 4;
     private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
 
     static {
         METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
-        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_STRING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
         METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_STRING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_STRING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_STRING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_STRING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_STRING);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
         METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
         METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
         METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
         METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
         METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_URI);
         METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
-        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_STRING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_URI);
         METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
         METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
+        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_URI);
     }
 
     private static final SparseArray<String> EDITOR_KEY_MAPPING;
@@ -207,6 +278,7 @@
     }
 
     private final Bundle mBundle;
+    private Description mDescription;
 
     private MediaMetadata(Bundle bundle) {
         mBundle = new Bundle(bundle);
@@ -232,10 +304,27 @@
      * associated with the key.
      *
      * @param key The key the value is stored under
+     * @return a CharSequence value, or null
+     */
+    public CharSequence getText(String key) {
+        return mBundle.getCharSequence(key);
+    }
+
+    /**
+     * Returns the text value associated with the given key as a String, or null
+     * if no mapping of the desired type exists for the given key or a null
+     * value is explicitly associated with the key. This is equivalent to
+     * calling {@link #getText getText().toString()} if the value is not null.
+     *
+     * @param key The key the value is stored under
      * @return a String value, or null
      */
     public String getString(String key) {
-        return mBundle.getString(key);
+        CharSequence text = getText(key);
+        if (text != null) {
+            return text.toString();
+        }
+        return null;
     }
 
     /**
@@ -250,8 +339,8 @@
     }
 
     /**
-     * Return a {@link Rating} for the given key or null if no rating exists for
-     * the given key.
+     * Returns a {@link Rating} for the given key or null if no rating exists
+     * for the given key.
      *
      * @param key The key the value is stored under
      * @return A {@link Rating} or null
@@ -268,8 +357,8 @@
     }
 
     /**
-     * Return a {@link Bitmap} for the given key or null if no bitmap exists for
-     * the given key.
+     * Returns a {@link Bitmap} for the given key or null if no bitmap exists
+     * for the given key.
      *
      * @param key The key the value is stored under
      * @return A {@link Bitmap} or null
@@ -296,7 +385,7 @@
     }
 
     /**
-     * Get the number of fields in this metadata.
+     * Returns the number of fields in this metadata.
      *
      * @return The number of fields in the metadata.
      */
@@ -314,6 +403,64 @@
     }
 
     /**
+     * Returns a simple description of this metadata for display purposes.
+     *
+     * @return A simple description of this metadata.
+     * @hide
+     */
+    public @NonNull Description getDescription() {
+        if (mDescription != null) {
+            return mDescription;
+        }
+
+        CharSequence[] text = new CharSequence[3];
+        Bitmap icon = null;
+        Uri iconUri = null;
+
+        // First handle the case where display data is set already
+        CharSequence displayText = getText(METADATA_KEY_DISPLAY_TITLE);
+        if (!TextUtils.isEmpty(displayText)) {
+            // If they have a display title use only display data, otherwise use
+            // our best bets
+            text[0] = displayText;
+            text[1] = getText(METADATA_KEY_DISPLAY_SUBTITLE);
+            text[2] = getText(METADATA_KEY_DISPLAY_DESCRIPTION);
+        } else {
+            // Use whatever fields we can
+            int textIndex = 0;
+            int keyIndex = 0;
+            while (textIndex < text.length && keyIndex < PREFERRED_DESCRIPTION_ORDER.length) {
+                CharSequence next = getText(PREFERRED_DESCRIPTION_ORDER[keyIndex++]);
+                if (!TextUtils.isEmpty(next)) {
+                    // Fill in the next empty bit of text
+                    text[textIndex++] = next;
+                }
+            }
+        }
+
+        // Get the best art bitmap we can find
+        for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
+            Bitmap next = getBitmap(PREFERRED_BITMAP_ORDER[i]);
+            if (next != null) {
+                icon = next;
+                break;
+            }
+        }
+
+        // Get the best Uri we can find
+        for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) {
+            String next = getString(PREFERRED_URI_ORDER[i]);
+            if (!TextUtils.isEmpty(next)) {
+                iconUri = Uri.parse(next);
+                break;
+            }
+        }
+
+        mDescription = new Description(text[0], text[1], text[2], icon, iconUri);
+        return mDescription;
+    }
+
+    /**
      * Helper for getting the String key used by {@link MediaMetadata} from the
      * integer key that {@link MediaMetadataEditor} uses.
      *
@@ -365,6 +512,43 @@
         }
 
         /**
+         * Put a CharSequence value into the metadata. Custom keys may be used,
+         * but if the METADATA_KEYs defined in this class are used they may only
+         * be one of the following:
+         * <ul>
+         * <li>{@link #METADATA_KEY_TITLE}</li>
+         * <li>{@link #METADATA_KEY_ARTIST}</li>
+         * <li>{@link #METADATA_KEY_ALBUM}</li>
+         * <li>{@link #METADATA_KEY_AUTHOR}</li>
+         * <li>{@link #METADATA_KEY_WRITER}</li>
+         * <li>{@link #METADATA_KEY_COMPOSER}</li>
+         * <li>{@link #METADATA_KEY_DATE}</li>
+         * <li>{@link #METADATA_KEY_GENRE}</li>
+         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
+         * <li>{@link #METADATA_KEY_ART_URI}</li>
+         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
+         * </ul>
+         *
+         * @param key The key for referencing this value
+         * @param value The CharSequence value to store
+         * @return The Builder to allow chaining
+         */
+        public Builder putText(String key, CharSequence value) {
+            if (METADATA_KEYS_TYPE.containsKey(key)) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
+                    throw new IllegalArgumentException("The " + key
+                            + " key cannot be used to put a CharSequence");
+                }
+            }
+            mBundle.putCharSequence(key, value);
+            return this;
+        }
+
+        /**
          * Put a String value into the metadata. Custom keys may be used, but if
          * the METADATA_KEYs defined in this class are used they may only be one
          * of the following:
@@ -380,6 +564,10 @@
          * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
          * <li>{@link #METADATA_KEY_ART_URI}</li>
          * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
          * </ul>
          *
          * @param key The key for referencing this value
@@ -388,12 +576,12 @@
          */
         public Builder putString(String key, String value) {
             if (METADATA_KEYS_TYPE.containsKey(key)) {
-                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_STRING) {
+                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
                     throw new IllegalArgumentException("The " + key
                             + " key cannot be used to put a String");
                 }
             }
-            mBundle.putString(key, value);
+            mBundle.putCharSequence(key, value);
             return this;
         }
 
@@ -410,7 +598,7 @@
          * </ul>
          *
          * @param key The key for referencing this value
-         * @param value The String value to store
+         * @param value The long value to store
          * @return The Builder to allow chaining
          */
         public Builder putLong(String key, long value) {
@@ -434,7 +622,7 @@
          * </ul>
          *
          * @param key The key for referencing this value
-         * @param value The String value to store
+         * @param value The Rating value to store
          * @return The Builder to allow chaining
          */
         public Builder putRating(String key, Rating value) {
@@ -455,6 +643,7 @@
          * <ul>
          * <li>{@link #METADATA_KEY_ART}</li>
          * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
+         * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
          * </ul>
          *
          * @param key The key for referencing this value
@@ -482,4 +671,46 @@
         }
     }
 
+    /**
+     * A simple form of the metadata that can be used for display.
+     *
+     * @hide
+     */
+    public final class Description {
+        /**
+         * A primary title suitable for display or null.
+         */
+        public final CharSequence title;
+        /**
+         * A subtitle suitable for display or null.
+         */
+        public final CharSequence subtitle;
+        /**
+         * A description suitable for display or null.
+         */
+        public final CharSequence description;
+        /**
+         * A bitmap icon suitable for display or null.
+         */
+        public final Bitmap icon;
+        /**
+         * A Uri for an icon suitable for display or null.
+         */
+        public final Uri iconUri;
+
+        private Description(CharSequence title, CharSequence subtitle, CharSequence description,
+                Bitmap icon, Uri iconUri) {
+            this.title = title;
+            this.subtitle = subtitle;
+            this.description = description;
+            this.icon = icon;
+            this.iconUri = iconUri;
+        }
+
+        @Override
+        public String toString() {
+            return title + ", " + subtitle + ", " + description;
+        }
+    }
+
 }
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index db6315a..96c66c5 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -473,7 +473,7 @@
                 String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key);
                 // But just in case, don't add things we don't understand
                 if (metadataKey != null) {
-                    mMetadataBuilder.putString(metadataKey, value);
+                    mMetadataBuilder.putText(metadataKey, value);
                 }
             }
 
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 2c39afa..37c7553 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -33,8 +33,8 @@
     void onSessionReleased(int seq);
     void onSessionEvent(in String name, in Bundle args, int seq);
     void onChannelRetuned(in Uri channelUri, int seq);
-    void onTrackInfoChanged(in List<TvTrackInfo> tracks, int seq);
-    void onTrackSelectionChanged(in List<TvTrackInfo> selectedTracks, int seq);
+    void onTracksChanged(in List<TvTrackInfo> tracks, int seq);
+    void onTrackSelected(int type, in String trackId, int seq);
     void onVideoAvailable(int seq);
     void onVideoUnavailable(int reason, int seq);
     void onContentAllowed(int seq);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index a2b7d6b..d5719c8 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -60,8 +60,7 @@
     void setVolume(in IBinder sessionToken, float volume, int userId);
     void tune(in IBinder sessionToken, in Uri channelUri, in Bundle params, int userId);
     void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
-    void selectTrack(in IBinder sessionToken, in TvTrackInfo track, int userId);
-    void unselectTrack(in IBinder sessionToken, in TvTrackInfo track, int userId);
+    void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);
 
     void sendAppPrivateCommand(in IBinder sessionToken, in String action, in Bundle data,
             int userId);
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index 26a0d20..df648e7 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -27,5 +27,4 @@
     void addHardwareTvInput(in int deviceID, in TvInputInfo inputInfo);
     void addHdmiCecTvInput(in int logicalAddress, in TvInputInfo inputInfo);
     void removeTvInput(in String inputId);
-    void setWrappedInputId(in String inputId, in String wrappedInputId);
 }
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 9a0be25..99fb911 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -37,8 +37,7 @@
     void setVolume(float volume);
     void tune(in Uri channelUri, in Bundle params);
     void setCaptionEnabled(boolean enabled);
-    void selectTrack(in TvTrackInfo track);
-    void unselectTrack(in TvTrackInfo track);
+    void selectTrack(int type, in String trackId);
 
     void appPrivateCommand(in String action, in Bundle data);
 
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 3773987..8a918e1 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -27,11 +27,11 @@
  * @hide
  */
 oneway interface ITvInputSessionCallback {
-    void onSessionCreated(ITvInputSession session);
+    void onSessionCreated(ITvInputSession session, in IBinder hardwareSessionToken);
     void onSessionEvent(in String name, in Bundle args);
     void onChannelRetuned(in Uri channelUri);
-    void onTrackInfoChanged(in List<TvTrackInfo> tracks);
-    void onTrackSelectionChanged(in List<TvTrackInfo> selectedTracks);
+    void onTracksChanged(in List<TvTrackInfo> tracks);
+    void onTrackSelected(int type, in String trackId);
     void onVideoAvailable();
     void onVideoUnavailable(int reason);
     void onContentAllowed();
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index a809da9..5022cc1 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -49,12 +49,11 @@
     private static final int DO_TUNE = 6;
     private static final int DO_SET_CAPTION_ENABLED = 7;
     private static final int DO_SELECT_TRACK = 8;
-    private static final int DO_UNSELECT_TRACK = 9;
-    private static final int DO_APP_PRIVATE_COMMAND = 10;
-    private static final int DO_CREATE_OVERLAY_VIEW = 11;
-    private static final int DO_RELAYOUT_OVERLAY_VIEW = 12;
-    private static final int DO_REMOVE_OVERLAY_VIEW = 13;
-    private static final int DO_REQUEST_UNBLOCK_CONTENT = 14;
+    private static final int DO_APP_PRIVATE_COMMAND = 9;
+    private static final int DO_CREATE_OVERLAY_VIEW = 10;
+    private static final int DO_RELAYOUT_OVERLAY_VIEW = 11;
+    private static final int DO_REMOVE_OVERLAY_VIEW = 12;
+    private static final int DO_REQUEST_UNBLOCK_CONTENT = 13;
 
     private final HandlerCaller mCaller;
 
@@ -121,11 +120,9 @@
                 return;
             }
             case DO_SELECT_TRACK: {
-                mTvInputSessionImpl.selectTrack((TvTrackInfo) msg.obj);
-                return;
-            }
-            case DO_UNSELECT_TRACK: {
-                mTvInputSessionImpl.unselectTrack((TvTrackInfo) msg.obj);
+                SomeArgs args = (SomeArgs) msg.obj;
+                mTvInputSessionImpl.selectTrack((Integer) args.arg1, (String) args.arg2);
+                args.recycle();
                 return;
             }
             case DO_APP_PRIVATE_COMMAND: {
@@ -196,13 +193,8 @@
     }
 
     @Override
-    public void selectTrack(TvTrackInfo track) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SELECT_TRACK, track));
-    }
-
-    @Override
-    public void unselectTrack(TvTrackInfo track) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UNSELECT_TRACK, track));
+    public void selectTrack(int type, String trackId) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_SELECT_TRACK, type, trackId));
     }
 
     @Override
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index a1711e5..fc3ff81 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -124,10 +124,10 @@
  *         <td>CO_TV</td>
  *         <td></td>
  *     </tr-->
- *     <!--tr>
+ *     <tr>
  *         <td>DE_TV</td>
- *         <td></td>
- *     </tr-->
+ *         <td>The Germany television rating system</td>
+ *     </tr>
  *     <!--tr>
  *         <td>DK_TV</td>
  *         <td></td>
@@ -140,10 +140,10 @@
  *         <td>FI_TV</td>
  *         <td></td>
  *     </tr-->
- *     <!--tr>
+ *     <tr>
  *         <td>FR_TV</td>
- *         <td></td>
- *     </tr-->
+ *         <td>The content rating system in French</td>
+ *     </tr>
  *     <!--tr>
  *         <td>GR_TV</td>
  *         <td></td>
@@ -200,10 +200,10 @@
  *         <td>MY_TV</td>
  *         <td></td>
  *     </tr-->
- *     <!--tr>
+ *     <tr>
  *         <td>NL_TV</td>
- *         <td></td>
- *     </tr-->
+ *         <td>The television rating system in the Netherlands</td>
+ *     </tr>
  *     <!--tr>
  *         <td>NZ_TV</td>
  *         <td></td>
@@ -316,10 +316,31 @@
  *         <td>CO_TV_ALL</td>
  *         <td></td>
  *     </tr-->
- *     <!--tr>
+ *     <tr>
  *         <td>DE_TV_ALL</td>
- *         <td></td>
- *     </tr-->
+ *         <td>Without restriction. There are time schedules and certain age groups which have to be
+ *         considered. {@code DE_TV_ALL} is scheduled in daytime (6:00AM – 8:00PM). However, cinema
+ *         films classified with "12" may be shown during the daytime, if they are not considered
+ *         harmful to younger children.</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DE_TV_12</td>
+ *         <td>Suitable for 12 years and above. There are time schedules and certain age groups
+ *         which have to be considered. {@code DE_TV_12} is scheduled in primetime (from 8:00PM
+ *         – 10.00 p.m.).</td>
+ *     </tr>
+ *     <tr>
+ *         <td>DE_TV_16</td>
+ *         <td>Suitable for 16 years and above. There are time schedules and certain age groups
+ *         which have to be considered. {@code DE_TV_16} is scheduled in late evening (from 10:00PM
+ *         - 11:00PM). </td>
+ *     </tr>
+ *     <tr>
+ *         <td>DE_TV_18</td>
+ *         <td>Suitable for 18 years and above. There are time schedules and certain age groups
+ *         which have to be considered. {@code DE_TV_18} is scheduled in late night (from 11:00PM
+ *         - 6:00AM). </td>
+ *     </tr>
  *     <!--tr>
  *         <td>DK_TV_ALL</td>
  *         <td></td>
@@ -332,10 +353,36 @@
  *         <td>FI_TV_ALL</td>
  *         <td></td>
  *     </tr-->
- *     <!--tr>
+ *     <tr>
  *         <td>FR_TV_ALL</td>
- *         <td></td>
- *     </tr-->
+ *         <td>A rating string for {@code FR_TV}. According to CSA in France, if no rating appears,
+ *         the program is most likely appropriate for all ages. In Android TV, however,
+ *         {@code RATING_FR_ALL} is used for handling that case.</td>
+ *     </tr>
+ *     <tr>
+ *         <td>FR_TV_10</td>
+ *         <td>A rating string for {@code FR_TV}. This rating is for programs that are not
+ *         recommended for children under 10.</td>
+ *     </tr>
+ *     <tr>
+ *         <td>FR_TV_12</td>
+ *         <td>A rating string for {@code FR_TV}. This rating is for programs that are not
+ *         recommended for children under 12. Programs rated this are not allowed to air before
+ *         10:00 pm (Some channels and programs are subject to exception). </td>
+ *     </tr>
+ *     <tr>
+ *         <td>FR_TV_16</td>
+ *         <td>A rating string for {@code FR_TV}. This rating is for programs that are not
+ *         recommended for children under 16. Programs rated this are not allowed to air before
+ *         10:30 pm (Some channels and programs are subject to exception). </td>
+ *     </tr>
+ *     <tr>
+ *         <td>FR_TV_18</td>
+ *         <td>A rating string for {@code FR_TV}.  This rating is for programs that are not
+ *         recommended for persons under 18. Programs rated this are allowed between midnight and
+ *         5 am and only on some channels. The access to these programs is locked by a personal
+ *         password.</td>
+ *     </tr>
  *     <!--tr>
  *         <td>GR_TV_ALL</td>
  *         <td></td>
@@ -416,10 +463,31 @@
  *         <td>MY_TV_ALL</td>
  *         <td></td>
  *     </tr-->
- *     <!--tr>
- *         <td>NL_TV_ALL</td>
- *         <td></td>
- *     </tr-->
+ *     <tr>
+ *         <td>NL_TV_AL</td>
+ *         <td>A rating string for {@code NL_TV}. This rating is for programs that are appropriate
+ *         for all ages.</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_6</td>
+ *         <td>A rating string for {@code NL_TV}. This rating is for programs that require parental
+ *         advisory for children under 6.</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_9</td>
+ *         <td>A rating string for {@code NL_TV}. This rating is for programs that require parental
+ *         advisory for children under 9.</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_12</td>
+ *         <td>A rating string for {@code NL_TV}. This rating is for programs that require parental
+ *         advisory for children under 12.</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_16</td>
+ *         <td>A rating string for {@code NL_TV}. This rating is for programs that require parental
+ *         advisory for children under 16.</td>
+ *     </tr>
  *     <!--tr>
  *         <td>NZ_TV_ALL</td>
  *         <td></td>
@@ -582,10 +650,6 @@
  *         <td></td>
  *     </tr-->
  *     <!--tr>
- *         <td>DE_TV_</td>
- *         <td></td>
- *     </tr-->
- *     <!--tr>
  *         <td>DK_TV_</td>
  *         <td></td>
  *     </tr-->
@@ -598,10 +662,6 @@
  *         <td></td>
  *     </tr-->
  *     <!--tr>
- *         <td>FR_TV_</td>
- *         <td></td>
- *     </tr-->
- *     <!--tr>
  *         <td>GR_TV_</td>
  *         <td></td>
  *     </tr-->
@@ -653,10 +713,30 @@
  *         <td>MY_TV_</td>
  *         <td></td>
  *     </tr-->
- *     <!--tr>
- *         <td>NL_TV_</td>
- *         <td></td>
- *     </tr-->
+ *     <tr>
+ *         <td>NL_TV_V</td>
+ *         <td>Violence</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_F</td>
+ *         <td>Fear</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_S</td>
+ *         <td>Sex</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_D</td>
+ *         <td>Discrimination</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_DA</td>
+ *         <td>Drugs- and alcoholabuse</td>
+ *     </tr>
+ *     <tr>
+ *         <td>NL_TV_L</td>
+ *         <td>Coarse Language</td>
+ *     </tr>
  *     <!--tr>
  *         <td>NZ_TV_</td>
  *         <td></td>
@@ -755,7 +835,6 @@
     private static final String DELIMITER = "/";
 
     private final String mDomain;
-    private final String mCountryCode;
     private final String mRatingSystem;
     private final String mRating;
     private final String[] mSubRatings;
@@ -764,21 +843,20 @@
      * Creates a TvContentRating object.
      *
      * @param domain The domain name.
-     * @param countryCode The country code in ISO 3166-2 format or {@code null}.
      * @param ratingSystem The rating system id.
      * @param rating The content rating string.
      * @param subRatings The string array of sub-ratings.
      * @return A TvContentRating object, or null if creation failed.
      */
-    public static TvContentRating createRating(String domain, String countryCode,
-            String ratingSystem, String rating, String... subRatings) {
+    public static TvContentRating createRating(String domain, String ratingSystem,
+            String rating, String... subRatings) {
         if (TextUtils.isEmpty(domain)) {
             throw new IllegalArgumentException("domain cannot be empty");
         }
         if (TextUtils.isEmpty(rating)) {
             throw new IllegalArgumentException("rating cannot be empty");
         }
-        return new TvContentRating(domain, countryCode, ratingSystem, rating, subRatings);
+        return new TvContentRating(domain, ratingSystem, rating, subRatings);
     }
 
     /**
@@ -786,7 +864,7 @@
      * {@link #flattenToString}.
      *
      * @param ratingString The String that was returned by flattenToString().
-     * @return a new TvContentRating containing the domain, countryCode, rating system, rating and
+     * @return a new TvContentRating containing the domain, rating system, rating and
      *         sub-ratings information was encoded in {@code ratingString}.
      * @see #flattenToString
      */
@@ -795,27 +873,28 @@
             throw new IllegalArgumentException("ratingString cannot be empty");
         }
         String[] strs = ratingString.split(DELIMITER);
-        if (strs.length < 4) {
+        if (strs.length < 3) {
             throw new IllegalArgumentException("Invalid rating string: " + ratingString);
         }
-        if (strs.length > 4) {
-            String[] subRatings = new String[strs.length - 4];
-            System.arraycopy(strs, 4, subRatings, 0, subRatings.length);
-            return new TvContentRating(strs[0], strs[1], strs[2], strs[3], subRatings);
+        if (strs.length > 3) {
+            String[] subRatings = new String[strs.length - 3];
+            System.arraycopy(strs, 3, subRatings, 0, subRatings.length);
+            return new TvContentRating(strs[0], strs[1], strs[2], subRatings);
         }
-        return new TvContentRating(strs[0], strs[1], strs[2], strs[3], null);
+        return new TvContentRating(strs[0], strs[1], strs[2], null);
     }
 
     /**
      * Constructs a TvContentRating object from a given rating and sub-rating constants.
      *
-     * @param rating The rating constant defined in this class.
+     * @param domain The domain name.
+     * @param ratingSystem The rating system id.
+     * @param rating The content rating string.
      * @param subRatings The String array of sub-rating constants defined in this class.
      */
-    private TvContentRating(String domain, String countryCode,
-            String ratingSystem, String rating, String[] subRatings) {
+    private TvContentRating(
+            String domain, String ratingSystem, String rating, String[] subRatings) {
         mDomain = domain;
-        mCountryCode = countryCode;
         mRatingSystem = ratingSystem;
         mRating = rating;
         mSubRatings = subRatings;
@@ -829,13 +908,6 @@
     }
 
     /**
-     * Returns the country code in ISO 3166-2 format or {@code null}.
-     */
-    public String getCountry() {
-        return mCountryCode;
-    }
-
-    /**
      * Returns the rating system id.
      */
     public String getRatingSystem() {
@@ -872,8 +944,6 @@
         StringBuilder builder = new StringBuilder();
         builder.append(mDomain);
         builder.append(DELIMITER);
-        builder.append(mCountryCode);
-        builder.append(DELIMITER);
         builder.append(mRatingSystem);
         builder.append(DELIMITER);
         builder.append(mRating);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index a555454..fdf0d9c 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -176,16 +176,20 @@
          * @param session A {@link TvInputManager.Session} associated with this callback.
          * @param tracks A list which includes track information.
          */
-        public void onTrackInfoChanged(Session session, List<TvTrackInfo> tracks) {
+        public void onTracksChanged(Session session, List<TvTrackInfo> tracks) {
         }
 
         /**
-         * This is called when there is a change on the selected tracks in this session.
+         * This is called when a track for a given type is selected.
          *
          * @param session A {@link TvInputManager.Session} associated with this callback
-         * @param selectedTracks A list of selected tracks.
+         * @param type The type of the selected track. The type can be
+         *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
+         *            {@link TvTrackInfo#TYPE_SUBTITLE}.
+         * @param trackId The ID of the selected track. When {@code null} the currently selected
+         *            track for a given type should be unselected.
          */
-        public void onTrackSelectionChanged(Session session, List<TvTrackInfo> selectedTracks) {
+        public void onTrackSelected(Session session, int type, String trackId) {
         }
 
         /**
@@ -282,22 +286,44 @@
             });
         }
 
-        public void postTrackInfoChanged(final List<TvTrackInfo> tracks) {
+        public void postTracksChanged(final List<TvTrackInfo> tracks) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mSession.mTracks = tracks;
-                    mSessionCallback.onTrackInfoChanged(mSession, tracks);
+                    mSession.mAudioTracks.clear();
+                    mSession.mVideoTracks.clear();
+                    mSession.mSubtitleTracks.clear();
+                    for (TvTrackInfo track : tracks) {
+                        if (track.getType() == TvTrackInfo.TYPE_AUDIO) {
+                            mSession.mAudioTracks.add(track);
+                        } else if (track.getType() == TvTrackInfo.TYPE_VIDEO) {
+                            mSession.mVideoTracks.add(track);
+                        } else if (track.getType() == TvTrackInfo.TYPE_SUBTITLE) {
+                            mSession.mSubtitleTracks.add(track);
+                        } else {
+                            // Silently ignore.
+                        }
+                    }
+                    mSessionCallback.onTracksChanged(mSession, tracks);
                 }
             });
         }
 
-        public void postTrackSelectionChanged(final List<TvTrackInfo> selectedTracks) {
+        public void postTrackSelected(final int type, final String trackId) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mSession.mSelectedTracks = selectedTracks;
-                    mSessionCallback.onTrackSelectionChanged(mSession, selectedTracks);
+                    if (type == TvTrackInfo.TYPE_AUDIO) {
+                        mSession.mSelectedAudioTrackId = trackId;
+                    } else if (type == TvTrackInfo.TYPE_VIDEO) {
+                        mSession.mSelectedVideoTrackId = trackId;
+                    } else if (type == TvTrackInfo.TYPE_SUBTITLE) {
+                        mSession.mSelectedSubtitleTrackId = trackId;
+                    } else {
+                        // Silently ignore.
+                        return;
+                    }
+                    mSessionCallback.onTrackSelected(mSession, type, trackId);
                 }
             });
         }
@@ -476,26 +502,26 @@
             }
 
             @Override
-            public void onTrackInfoChanged(List<TvTrackInfo> tracks, int seq) {
+            public void onTracksChanged(List<TvTrackInfo> tracks, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                     if (record == null) {
                         Log.e(TAG, "Callback not found for seq " + seq);
                         return;
                     }
-                    record.postTrackInfoChanged(tracks);
+                    record.postTracksChanged(tracks);
                 }
             }
 
             @Override
-            public void onTrackSelectionChanged(List<TvTrackInfo> selectedTracks, int seq) {
+            public void onTrackSelected(int type, String trackId, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                     if (record == null) {
                         Log.e(TAG, "Callback not found for seq " + seq);
                         return;
                     }
-                    record.postTrackSelectionChanged(selectedTracks);
+                    record.postTrackSelected(type, trackId);
                 }
             }
 
@@ -911,8 +937,12 @@
         private IBinder mToken;
         private TvInputEventSender mSender;
         private InputChannel mChannel;
-        private List<TvTrackInfo> mTracks;
-        private List<TvTrackInfo> mSelectedTracks;
+        private final List<TvTrackInfo> mAudioTracks = new ArrayList<TvTrackInfo>();
+        private final List<TvTrackInfo> mVideoTracks = new ArrayList<TvTrackInfo>();
+        private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<TvTrackInfo>();
+        private String mSelectedAudioTrackId;
+        private String mSelectedVideoTrackId;
+        private String mSelectedSubtitleTrackId;
 
         private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
                 int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
@@ -1045,7 +1075,12 @@
                 Log.w(TAG, "The session has been already released");
                 return;
             }
-            mTracks = null;
+            mAudioTracks.clear();
+            mVideoTracks.clear();
+            mSubtitleTracks.clear();
+            mSelectedAudioTrackId = null;
+            mSelectedVideoTrackId = null;
+            mSelectedSubtitleTrackId = null;
             try {
                 mService.tune(mToken, channelUri, params, mUserId);
             } catch (RemoteException e) {
@@ -1073,69 +1108,84 @@
         /**
          * Selects a track.
          *
-         * @param track The track to be selected.
+         * @param type The type of the track to select. The type can be
+         *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
+         *            {@link TvTrackInfo#TYPE_SUBTITLE}.
+         * @param trackId The ID of the track to select. When {@code null}, the currently selected
+         *            track of the given type will be unselected.
          * @see #getTracks()
          */
-        public void selectTrack(TvTrackInfo track) {
-            if (track == null) {
-                throw new IllegalArgumentException("track cannot be null");
+        public void selectTrack(int type, String trackId) {
+            if (type == TvTrackInfo.TYPE_AUDIO) {
+                if (trackId != null && !mAudioTracks.contains(trackId)) {
+                    Log.w(TAG, "Invalid audio trackId: " + trackId);
+                }
+            } else if (type == TvTrackInfo.TYPE_VIDEO) {
+                if (trackId != null && !mVideoTracks.contains(trackId)) {
+                    Log.w(TAG, "Invalid video trackId: " + trackId);
+                }
+            } else if (type == TvTrackInfo.TYPE_SUBTITLE) {
+                if (trackId != null && !mSubtitleTracks.contains(trackId)) {
+                    Log.w(TAG, "Invalid subtitle trackId: " + trackId);
+                }
+            } else {
+                throw new IllegalArgumentException("invalid type: " + type);
             }
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.selectTrack(mToken, track, mUserId);
+                mService.selectTrack(mToken, type, trackId, mUserId);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
         }
 
         /**
-         * Unselects a track.
+         * Returns the list of tracks for a given type. Returns {@code null} if the information is
+         * not available.
          *
-         * @param track The track to be selected.
-         * @see #getTracks()
+         * @param type The type of the tracks. The type can be {@link TvTrackInfo#TYPE_AUDIO},
+         *            {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
+         * @return the list of tracks for the given type.
          */
-        public void unselectTrack(TvTrackInfo track) {
-            if (track == null) {
-                throw new IllegalArgumentException("track cannot be null");
+        public List<TvTrackInfo> getTracks(int type) {
+            if (type == TvTrackInfo.TYPE_AUDIO) {
+                if (mAudioTracks == null) {
+                    return null;
+                }
+                return mAudioTracks;
+            } else if (type == TvTrackInfo.TYPE_VIDEO) {
+                if (mVideoTracks == null) {
+                    return null;
+                }
+                return mVideoTracks;
+            } else if (type == TvTrackInfo.TYPE_SUBTITLE) {
+                if (mSubtitleTracks == null) {
+                    return null;
+                }
+                return mSubtitleTracks;
             }
-            if (mToken == null) {
-                Log.w(TAG, "The session has been already released");
-                return;
-            }
-            try {
-                mService.unselectTrack(mToken, track, mUserId);
-            } catch (RemoteException e) {
-                throw new RuntimeException(e);
-            }
+            throw new IllegalArgumentException("invalid type: " + type);
         }
 
         /**
-         * Returns a list which includes track information. May return {@code null} if the
-         * information is not available.
-         * @see #selectTrack(TvTrackInfo)
-         * @see #unselectTrack(TvTrackInfo)
+         * Returns the selected track for a given type. Returns {@code null} if the information is
+         * not available or any of the tracks for the given type is not selected.
+         *
+         * @return the ID of the selected track.
+         * @see #selectTrack
          */
-        public List<TvTrackInfo> getTracks() {
-            if (mTracks == null) {
-                return null;
+        public String getSelectedTrack(int type) {
+            if (type == TvTrackInfo.TYPE_AUDIO) {
+                return mSelectedAudioTrackId;
+            } else if (type == TvTrackInfo.TYPE_VIDEO) {
+                return mSelectedVideoTrackId;
+            } else if (type == TvTrackInfo.TYPE_SUBTITLE) {
+                return mSelectedSubtitleTrackId;
             }
-            return new ArrayList<TvTrackInfo>(mTracks);
-        }
-
-        /**
-         * Returns a list of selected tracks May return {@code null} if the information is not
-         * available.
-         * @see #selectTrack(TvTrackInfo)
-         * @see #unselectTrack(TvTrackInfo)
-         */
-        public List<TvTrackInfo> getSelectedTracks() {
-            if (mSelectedTracks == null) {
-                return null;
-            }
-            return new ArrayList<TvTrackInfo>(mSelectedTracks);
+            throw new IllegalArgumentException("invalid type: " + type);
         }
 
         /**
@@ -1404,6 +1454,10 @@
             mPendingEventPool.release(p);
         }
 
+        IBinder getToken() {
+            return mToken;
+        }
+
         private void releaseInternal() {
             mToken = null;
             synchronized (mHandler) {
diff --git a/media/java/android/media/tv/TvInputPassthroughWrapperService.java b/media/java/android/media/tv/TvInputPassthroughWrapperService.java
deleted file mode 100644
index 08c802f6..0000000
--- a/media/java/android/media/tv/TvInputPassthroughWrapperService.java
+++ /dev/null
@@ -1,259 +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 android.media.tv;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-import android.view.Surface;
-
-/**
- * TvInputPassthroughWrapperService represents a TV input which controls an external device
- * connected to a pass-through TV input (e.g. HDMI 1).
- * <p>
- * This service wraps around a pass-through TV input and delegates the {@link Surface} to the
- * connected TV input so that the application can show the pass-through TV input while
- * TvInputPassthroughWrapperService controls the underlying external device via a separate
- * connection. In the setup activity, the TV input should get the pass-through TV input ID, around
- * which this service will wrap. The service implementation should pass the ID via
- * {@link TvInputPassthroughWrapperService#getPassthroughInputId(String)}. In addition,
- * it needs to implement {@link TvInputPassthroughWrapperService#onCreatePassthroughWrapperSession}
- * to handle requests from the application.
- * </p>
- */
-public abstract class TvInputPassthroughWrapperService extends TvInputService {
-    private static final String TAG = "TvInputPassthroughWrapperService";
-    // STOPSHIP: Turn debugging off.
-    private static final boolean DEBUG = true;
-    private TvInputManager mTvInputManager;
-    private Handler mHandler;
-
-    @Override
-    public void onCreate() {
-        if (DEBUG) Log.d(TAG, "onCreate()");
-        super.onCreate();
-        mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
-        mHandler = new Handler();
-    }
-
-    @Override
-    public final Session onCreateSession(String inputId) {
-        if (DEBUG) Log.d(TAG, "onCreateSession()");
-        // Checks the pass-through TV input is properly setup.
-        String passthroughInputId = getPassthroughInputId(inputId);
-        if (passthroughInputId == null) {
-            Log.w(TAG, "The passthrough TV input for input id(" + inputId + ") is not setup yet.");
-            return null;
-        }
-        // Checks if input id from the derived class is really pass-through type.
-        TvInputInfo info = mTvInputManager.getTvInputInfo(passthroughInputId);
-        if (info == null || !info.isPassthroughInputType()) {
-            Log.w(TAG, "Invalid TV input id from derived class: " + passthroughInputId);
-            return null;
-        }
-        // Creates a PassthroughWrapperSession.
-        PassthroughWrapperSession session = onCreatePassthroughWrapperSession();
-        if (session == null) {
-            return null;
-        }
-        // Connects to the pass-through input the external device is connected to.
-        if (!session.connect(passthroughInputId)) {
-            throw new IllegalStateException("WrapperSession cannot be reused.");
-        }
-        notifyWrappedInputId(inputId, passthroughInputId);
-        return session;
-    }
-
-    /**
-     * Returns an implementation of {@link PassthroughWrapperSession}.
-     * <p>
-     * May return {@code null} if {@link TvInputPassthroughWrapperService} fails to create a
-     * session.
-     * </p>
-     */
-    public abstract PassthroughWrapperSession onCreatePassthroughWrapperSession();
-
-    /**
-     * Returns the TV input id the external device is connected to.
-     * <p>
-     * {@link TvInputPassthroughWrapperService} is expected to identify the pass-though TV
-     * input the external device is connected to in the setup phase of this TV input.
-     * May return {@code null} if the pass-though TV input is not identified yet.
-     * </p>
-     * @param inputId The ID of the TV input which controls the external device.
-     */
-    public abstract String getPassthroughInputId(String inputId);
-
-    /**
-     * Base session class for derived classes to handle the request from the application. This
-     * creates additional session to the pass-through TV input internally and delegates the
-     * {@link Surface} given from the application.
-     */
-    public abstract class PassthroughWrapperSession extends Session {
-        private static final float VOLUME_ON = 1.0f;
-        private static final float VOLUME_OFF = 0f;
-        private TvInputManager.Session mSession;
-        private Surface mSurface;
-        private Float mVolume;
-        private boolean mReleased;
-        private int mSurfaceFormat;
-        private int mSurfaceWidth;
-        private int mSurfaceHeight;
-        private boolean mSurfaceChanged;
-        private boolean mConnectionRequested;
-
-        private final TvInputManager.SessionCallback mSessionCallback =
-                new TvInputManager.SessionCallback() {
-            @Override
-            public void onSessionCreated(TvInputManager.Session session) {
-                if (session == null) {
-                    Log.w(TAG, "Failed to create session.");
-                    onPassthroughSessionCreationFailed();
-                    return;
-                }
-                if (mReleased) {
-                    session.release();
-                    return;
-                }
-                if (mSurface != null) {
-                    session.setSurface(mSurface);
-                    mSurface = null;
-                }
-                if (mVolume != null) {
-                    session.setStreamVolume(mVolume);
-                    mVolume = null;
-                }
-                if (mSurfaceChanged) {
-                    session.dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
-                    mSurfaceChanged = false;
-                }
-                mSession = session;
-            }
-
-            @Override
-            public void onSessionReleased(TvInputManager.Session session) {
-                mReleased = true;
-                mSession = null;
-                onPassthroughSessionReleased();
-            }
-
-            @Override
-            public void onVideoAvailable(TvInputManager.Session session) {
-                if (mSession == session) {
-                    onPassthroughVideoAvailable();
-                }
-            }
-
-            @Override
-            public void onVideoUnavailable(TvInputManager.Session session, int reason) {
-                if (mSession == session) {
-                    onPassthroughVideoUnavailable(reason);
-                }
-            }
-
-            @Override
-            public void onSessionEvent(TvInputManager.Session session, String eventType,
-                    Bundle eventArgs) {
-                if (mSession == session) {
-                    notifySessionEvent(eventType, eventArgs);
-                }
-            }
-        };
-
-        /**
-         * Called when failed to create a session for pass-through TV input.
-         */
-        public abstract void onPassthroughSessionCreationFailed();
-
-        /**
-         * Called when the pass-through TV input session is released. This typically happens when
-         * the process hosting the pass-through TV input has crashed or been killed.
-         */
-        public abstract void onPassthroughSessionReleased();
-
-        /**
-         * Called when the underlying pass-through TV input session calls
-         * {@link #notifyVideoAvailable()}.
-         */
-        public abstract void onPassthroughVideoAvailable();
-
-        /**
-         * Called when the underlying pass-through TV input session calls
-         * {@link #notifyVideoUnavailable(int)}.
-         *
-         * @param reason The reason why the pass-through TV input stopped the playback.
-         */
-        public abstract void onPassthroughVideoUnavailable(int reason);
-
-        @Override
-        public final boolean onSetSurface(Surface surface) {
-            if (DEBUG) Log.d(TAG, "onSetSurface(" + surface + ")");
-            if (mSession == null) {
-                mSurface = surface;
-            } else {
-                mSession.setSurface(surface);
-            }
-            return true;
-        }
-
-        private boolean connect(String inputId) {
-            if (mConnectionRequested) {
-                return false;
-            }
-            mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
-            mConnectionRequested = true;
-            return true;
-        }
-
-        @Override
-        void release() {
-            super.release();
-            mReleased = true;
-            if (mSession != null) {
-                mSession.release();
-            }
-        }
-
-        @Override
-        void dispatchSurfaceChanged(int format, int width, int height) {
-            super.dispatchSurfaceChanged(format, width, height);
-            if (mSession == null) {
-                mSurfaceFormat = format;
-                mSurfaceWidth = width;
-                mSurfaceHeight = height;
-                mSurfaceChanged = true;
-            } else {
-                mSession.dispatchSurfaceChanged(format, width, height);
-            }
-        }
-
-        @Override
-        void setStreamVolume(float volume) {
-            super.setStreamVolume(volume);
-            // Here, we let the pass-through TV input know only whether volume is on or off and
-            // make the fine control done in the derived class to prevent that the volume is
-            // controlled in the both side.
-            float volumeForPassthriughInput = (volume > 0.0f) ? VOLUME_ON : VOLUME_OFF;
-            if (mSession == null) {
-                mVolume = Float.valueOf(volumeForPassthriughInput);
-            } else {
-                mSession.setStreamVolume(volumeForPassthriughInput);
-            }
-        }
-    }
-}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 7ce278c..0f90c2d 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -31,6 +31,7 @@
 import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.InputChannel;
@@ -47,7 +48,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * The TvInputService class represents a TV input or source such as HDMI or built-in tuner which
@@ -159,6 +162,8 @@
      * Returns a concrete implementation of {@link Session}.
      * <p>
      * May return {@code null} if this TV input service fails to create a session for some reason.
+     * If TV input represents an external device connected to a hardware TV input,
+     * {@link HardwareSession} should be returned.
      * </p>
      * @param inputId The ID of the TV input associated with the session.
      */
@@ -217,21 +222,6 @@
     }
 
     /**
-     * Notify wrapped TV input ID of current input to TV input framework manager
-     *
-     * @param inputId The TV input ID of {@link TvInputPassthroughWrapperService}
-     * @param wrappedInputId The ID of the wrapped TV input such as external pass-though TV input
-     * @hide
-     */
-    public final void notifyWrappedInputId(String inputId, String wrappedInputId) {
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = inputId;
-        args.arg2 = wrappedInputId;
-        mHandler.obtainMessage(TvInputService.ServiceHandler.DO_SET_WRAPPED_TV_INPUT_ID,
-                args).sendToTarget();
-    }
-
-    /**
      * Base class for derived classes to implement to provide a TV input session.
      */
     public abstract class Session implements KeyEvent.Callback {
@@ -319,42 +309,56 @@
         }
 
         /**
-         * Sends the change on the track information. This is expected to be called whenever a
-         * track is added/removed and the metadata of a track is modified.
+         * Sends the change on the track information. This is expected to be called whenever a track
+         * is added/removed and the metadata of a track is modified.
          *
          * @param tracks A list which includes track information.
+         * @throws IllegalArgumentException if {@code tracks} contains redundant tracks.
          */
-        public void notifyTrackInfoChanged(final List<TvTrackInfo> tracks) {
+        public void notifyTracksChanged(final List<TvTrackInfo> tracks) {
+            Set<String> trackIdSet = new HashSet<String>();
+            for (TvTrackInfo track : tracks) {
+                String trackId = track.getId();
+                if (trackIdSet.contains(trackId)) {
+                    throw new IllegalArgumentException("redundant track ID: " + trackId);
+                }
+                trackIdSet.add(trackId);
+            }
+            trackIdSet.clear();
+
+            // TODO: Validate the track list.
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     try {
-                        if (DEBUG) Log.d(TAG, "notifyTrackInfoChanged");
-                        mSessionCallback.onTrackInfoChanged(tracks);
+                        if (DEBUG) Log.d(TAG, "notifyTracksChanged");
+                        mSessionCallback.onTracksChanged(tracks);
                     } catch (RemoteException e) {
-                        Log.w(TAG, "error in notifyTrackInfoChanged");
+                        Log.w(TAG, "error in notifyTracksChanged");
                     }
                 }
             });
         }
 
         /**
-         * Sends the list of selected tracks. This is expected to be called whenever there is a
-         * change on track selection.
+         * Sends the ID of the selected track for a given track type. This is expected to be called
+         * whenever there is a change on track selection.
          *
-         * @param selectedTracks A list of selected tracks.
-         * @see #onSelectTrack(TvTrackInfo)
-         * @see #onUnselectTrack(TvTrackInfo)
+         * @param type The type of the selected track. The type can be
+         *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
+         *            {@link TvTrackInfo#TYPE_SUBTITLE}.
+         * @param trackId The ID of the selected track.
+         * @see #onSelectTrack
          */
-        public void notifyTrackSelectionChanged(final List<TvTrackInfo> selectedTracks) {
+        public void notifyTrackSelected(final int type, final String trackId) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     try {
-                        if (DEBUG) Log.d(TAG, "notifyTrackSelectionChanged");
-                        mSessionCallback.onTrackSelectionChanged(selectedTracks);
+                        if (DEBUG) Log.d(TAG, "notifyTrackSelected");
+                        mSessionCallback.onTrackSelected(type, trackId);
                     } catch (RemoteException e) {
-                        Log.w(TAG, "error in notifyTrackSelectionChanged");
+                        Log.w(TAG, "error in notifyTrackSelected");
                     }
                 }
             });
@@ -382,7 +386,7 @@
          * Informs the application that video is not available, so the TV input cannot continue
          * playing the TV stream.
          *
-         * @param reason The reason why the TV input stopped the playback:
+         * @param reason The reason that the TV input stopped the playback:
          * <ul>
          * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
          * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
@@ -598,34 +602,20 @@
         }
 
         /**
-         * Selects a given track.
+         * Select a given track.
          * <p>
-         * If it is called multiple times on the same type of track (ie. Video, Audio, Text), the
-         * track selected previously should be unselected in the implementation of this method.
-         * Also, if the select operation was successful, the implementation should call
-         * {@link #notifyTrackSelectionChanged(List)} to report the selected track list.
+         * If this is done successfully, the implementation should call {@link #notifyTrackSelected}
+         * to help applications maintain the selcted track lists.
          * </p>
          *
-         * @param track The track to be selected.
-         * @return {@code true} if the select operation was successful, {@code false} otherwise.
-         * @see #notifyTrackSelectionChanged(List)
+         * @param trackId The ID of the track to select. {@code null} means to unselect the current
+         *            track for a given type.
+         * @param type The type of the track to select. The type can be
+         *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
+         *            {@link TvTrackInfo#TYPE_SUBTITLE}.
+         * @see #notifyTrackSelected
          */
-        public boolean onSelectTrack(TvTrackInfo track) {
-            return false;
-        }
-
-        /**
-         * Unselects a given track.
-         * <p>
-         * If the unselect operation was successful, the implementation should call
-         * {@link #notifyTrackSelectionChanged(List)} to report the selected track list.
-         * </p>
-         *
-         * @param track The track to be unselected.
-         * @return {@code true} if the unselect operation was successful, {@code false} otherwise.
-         * @see #notifyTrackSelectionChanged(List)
-         */
-        public boolean onUnselectTrack(TvTrackInfo track) {
+        public boolean onSelectTrack(int type, String trackId) {
             return false;
         }
 
@@ -837,17 +827,8 @@
         /**
          * Calls {@link #onSelectTrack}.
          */
-        void selectTrack(TvTrackInfo track) {
-            onSelectTrack(track);
-            // TODO: Handle failure.
-        }
-
-        /**
-         * Calls {@link #onUnselectTrack}.
-         */
-        void unselectTrack(TvTrackInfo track) {
-            onUnselectTrack(track);
-            // TODO: Handle failure.
+        void selectTrack(int type, String trackId) {
+            onSelectTrack(type, trackId);
         }
 
         /**
@@ -994,6 +975,97 @@
         }
     }
 
+    /**
+     * Base class for a TV input session which represents an external device connected to a
+     * hardware TV input. Once TV input returns an implementation of this class on
+     * {@link #onCreateSession(String)}, the framework will create a hardware session and forward
+     * the application's surface to the hardware TV input.
+     * @see #onCreateSession(String)
+     */
+    public abstract class HardwareSession extends Session {
+
+        private TvInputManager.Session mHardwareSession;
+        private ITvInputSession mProxySession;
+        private ITvInputSessionCallback mProxySessionCallback;
+
+        /**
+         * Returns the hardware TV input ID the external device is connected to.
+         * <p>
+         * TV input is expected to provide {@link android.R.attr#setupActivity} so that
+         * the application can launch it before using this TV input. The setup activity may let
+         * the user select the hardware TV input to which the external device is connected. The ID
+         * of the selected one should be stored in the TV input so that it can be returned here.
+         * </p>
+         */
+        public abstract String getHardwareInputId();
+
+        private final TvInputManager.SessionCallback mHardwareSessionCallback =
+                new TvInputManager.SessionCallback() {
+            @Override
+            public void onSessionCreated(TvInputManager.Session session) {
+                mHardwareSession = session;
+                SomeArgs args = SomeArgs.obtain();
+                if (session != null) {
+                    args.arg1 = mProxySession;
+                    args.arg2 = mProxySessionCallback;
+                    args.arg3 = session.getToken();
+                } else {
+                    args.arg1 = null;
+                    args.arg2 = mProxySessionCallback;
+                    args.arg3 = null;
+                    onRelease();
+                }
+                mHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, args)
+                        .sendToTarget();
+            }
+
+            @Override
+            public void onVideoAvailable(final TvInputManager.Session session) {
+                if (mHardwareSession == session) {
+                    onHardwareVideoAvailable();
+                }
+            }
+
+            @Override
+            public void onVideoUnavailable(final TvInputManager.Session session,
+                    final int reason) {
+                if (mHardwareSession == session) {
+                    onHardwareVideoUnavailable(reason);
+                }
+            }
+        };
+
+        /**
+         * This method will not be called in {@link HardwareSession}. Framework will
+         * forward the application's surface to the hardware TV input.
+         */
+        @Override
+        public final boolean onSetSurface(Surface surface) {
+            Log.e(TAG, "onSetSurface() should not be called in HardwareProxySession.");
+            return false;
+        }
+
+        /**
+         * Called when the underlying hardware TV input session calls
+         * {@link TvInputService.Session#notifyVideoAvailable()}.
+         */
+        public void onHardwareVideoAvailable() { }
+
+        /**
+         * Called when the underlying hardware TV input session calls
+         * {@link TvInputService.Session#notifyVideoUnavailable(int)}.
+         *
+         * @param reason The reason that the hardware TV input stopped the playback:
+         * <ul>
+         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
+         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
+         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
+         * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
+         * </ul>
+         */
+        public void onHardwareVideoUnavailable(int reason) { }
+    }
+
     /** @hide */
     public static boolean isNavigationKey(int keyCode) {
         switch (keyCode) {
@@ -1017,11 +1089,11 @@
     @SuppressLint("HandlerLeak")
     private final class ServiceHandler extends Handler {
         private static final int DO_CREATE_SESSION = 1;
-        private static final int DO_ADD_HARDWARE_TV_INPUT = 2;
-        private static final int DO_REMOVE_HARDWARE_TV_INPUT = 3;
-        private static final int DO_ADD_HDMI_CEC_TV_INPUT = 4;
-        private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 5;
-        private static final int DO_SET_WRAPPED_TV_INPUT_ID = 6;
+        private static final int DO_NOTIFY_SESSION_CREATED = 2;
+        private static final int DO_ADD_HARDWARE_TV_INPUT = 3;
+        private static final int DO_REMOVE_HARDWARE_TV_INPUT = 4;
+        private static final int DO_ADD_HDMI_CEC_TV_INPUT = 5;
+        private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 6;
 
         private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
             int n = mCallbacks.beginBroadcast();
@@ -1060,18 +1132,6 @@
             mCallbacks.finishBroadcast();
         }
 
-        private void broadcastSetWrappedTvInputId(String inputId, String wrappedInputId) {
-            int n = mCallbacks.beginBroadcast();
-            for (int i = 0; i < n; ++i) {
-                try {
-                    mCallbacks.getBroadcastItem(i).setWrappedInputId(inputId, wrappedInputId);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error while broadcasting.", e);
-                }
-            }
-            mCallbacks.finishBroadcast();
-        }
-
         @Override
         public final void handleMessage(Message msg) {
             switch (msg.what) {
@@ -1080,17 +1140,58 @@
                     InputChannel channel = (InputChannel) args.arg1;
                     ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
                     String inputId = (String) args.arg3;
-                    try {
-                        Session sessionImpl = onCreateSession(inputId);
-                        if (sessionImpl == null) {
+                    Session sessionImpl = onCreateSession(inputId);
+                    args.recycle();
+                    if (sessionImpl == null) {
+                        try {
                             // Failed to create a session.
-                            cb.onSessionCreated(null);
-                        } else {
-                            sessionImpl.setSessionCallback(cb);
-                            ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
-                                    sessionImpl, channel);
-                            cb.onSessionCreated(stub);
+                            cb.onSessionCreated(null, null);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "error in onSessionCreated");
                         }
+                    } else {
+                        sessionImpl.setSessionCallback(cb);
+                        ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
+                                sessionImpl, channel);
+                        if (sessionImpl instanceof HardwareSession) {
+                            HardwareSession proxySession =
+                                    ((HardwareSession) sessionImpl);
+                            String harewareInputId = proxySession.getHardwareInputId();
+                            if (!TextUtils.isEmpty(harewareInputId)) {
+                                // TODO: check if the given ID is really hardware TV input.
+                                proxySession.mProxySession = stub;
+                                proxySession.mProxySessionCallback = cb;
+                                TvInputManager manager = (TvInputManager) getSystemService(
+                                        Context.TV_INPUT_SERVICE);
+                                manager.createSession(harewareInputId,
+                                        proxySession.mHardwareSessionCallback, mHandler);
+                            } else {
+                                sessionImpl.onRelease();
+                                Log.w(TAG, "Hardware input id is not setup yet.");
+                                try {
+                                    cb.onSessionCreated(null, null);
+                                } catch (RemoteException e) {
+                                    Log.e(TAG, "error in onSessionCreated");
+                                }
+                            }
+                        } else {
+                            SomeArgs someArgs = SomeArgs.obtain();
+                            someArgs.arg1 = stub;
+                            someArgs.arg2 = cb;
+                            someArgs.arg3 = null;
+                            mHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED,
+                                    someArgs).sendToTarget();
+                        }
+                    }
+                    return;
+                }
+                case DO_NOTIFY_SESSION_CREATED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    ITvInputSession stub = (ITvInputSession) args.arg1;
+                    ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
+                    IBinder hardwareSessionToken = (IBinder) args.arg3;
+                    try {
+                        cb.onSessionCreated(stub, hardwareSessionToken);
                     } catch (RemoteException e) {
                         Log.e(TAG, "error in onSessionCreated");
                     }
@@ -1129,13 +1230,6 @@
                     }
                     return;
                 }
-                case DO_SET_WRAPPED_TV_INPUT_ID: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    String inputId = (String) args.arg1;
-                    String wrappedInputId = (String) args.arg2;
-                    broadcastSetWrappedTvInputId(inputId, wrappedInputId);
-                    return;
-                }
                 default: {
                     Log.w(TAG, "Unhandled message code: " + msg.what);
                     return;
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index f296984..6ddb2a2 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -40,6 +40,7 @@
     public static final int TYPE_SUBTITLE = 2;
 
     private final int mType;
+    private final String mId;
     private final String mLanguage;
     private final int mAudioChannelCount;
     private final int mAudioSampleRate;
@@ -47,9 +48,10 @@
     private final int mVideoHeight;
     private final Bundle mExtra;
 
-    private TvTrackInfo(int type, String language, int audioChannelCount,
+    private TvTrackInfo(int type, String id, String language, int audioChannelCount,
             int audioSampleRate, int videoWidth, int videoHeight, Bundle extra) {
         mType = type;
+        mId = id;
         mLanguage = language;
         mAudioChannelCount = audioChannelCount;
         mAudioSampleRate = audioSampleRate;
@@ -60,6 +62,7 @@
 
     private TvTrackInfo(Parcel in) {
         mType = in.readInt();
+        mId = in.readString();
         mLanguage = in.readString();
         mAudioChannelCount = in.readInt();
         mAudioSampleRate = in.readInt();
@@ -77,6 +80,13 @@
     }
 
     /**
+     * Returns the ID of the track.
+     */
+    public final String getId() {
+        return mId;
+    }
+
+    /**
      * Returns the language information encoded by either ISO 639-1 or ISO 639-2/T. If the language
      * is unknown or could not be determined, the corresponding value will be {@code null}.
      */
@@ -147,6 +157,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mType);
+        dest.writeString(mId);
         dest.writeString(mLanguage);
         dest.writeInt(mAudioChannelCount);
         dest.writeInt(mAudioSampleRate);
@@ -172,7 +183,8 @@
      * A builder class for creating {@link TvTrackInfo} objects.
      */
     public static final class Builder {
-        private int mType;
+        private final String mId;
+        private final int mType;
         private String mLanguage;
         private int mAudioChannelCount;
         private int mAudioSampleRate;
@@ -185,14 +197,20 @@
          * must be added.
          *
          * @param type The type of the track.
+         * @param id The ID of the track that uniquely identifies the current track among all the
+         *            other tracks in the same TV program.
          */
-        public Builder(int type) {
+        public Builder(int type, String id) {
             if (type != TYPE_AUDIO
                     && type != TYPE_VIDEO
                     && type != TYPE_SUBTITLE) {
                 throw new IllegalArgumentException("Unknown type: " + type);
             }
+            if (id == null) {
+                throw new IllegalArgumentException("id cannot be null");
+            }
             mType = type;
+            mId = id;
         }
 
         /**
@@ -276,8 +294,8 @@
          * @return The new {@link TvTrackInfo} instance
          */
         public TvTrackInfo build() {
-            return new TvTrackInfo(mType, mLanguage, mAudioChannelCount,
-                    mAudioSampleRate, mVideoWidth, mVideoHeight, mExtra);
+            return new TvTrackInfo(mType, mId, mLanguage, mAudioChannelCount, mAudioSampleRate,
+                    mVideoWidth, mVideoHeight, mExtra);
         }
     }
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 78b1754..0959800 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -294,55 +294,49 @@
 
     /**
      * Selects a track.
-     * <p>
-     * If it is called multiple times on the same type of track (ie. Video, Audio, Text), the track
-     * selected in previous will be unselected. Note that this method does not take any effect
-     * unless the current TvView is tuned.
-     * </p>
      *
-     * @param track the track to be selected.
-     * @see #getTracks()
+     * @param type The type of the track to select. The type can be {@link TvTrackInfo#TYPE_AUDIO},
+     *            {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
+     * @param trackId The ID of the track to select. {@code null} means to unselect the current
+     *            track for a given type.
+     * @see #getTracks
+     * @see #getSelectedTrack
      */
-    public void selectTrack(TvTrackInfo track) {
+    public void selectTrack(int type, String trackId) {
         if (mSession != null) {
-            mSession.selectTrack(track);
+            mSession.selectTrack(type, trackId);
         }
     }
 
     /**
-     * Unselects a track.
-     * <p>
-     * Note that this method does not take any effect unless the current TvView is tuned.
+     * Returns the list of tracks. Returns {@code null} if the information is not available.
      *
-     * @param track the track to be unselected.
-     * @see #getTracks()
+     * @param type The type of the tracks. The type can be {@link TvTrackInfo#TYPE_AUDIO},
+     *            {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
+     * @see #selectTrack
+     * @see #getSelectedTrack
      */
-    public void unselectTrack(TvTrackInfo track) {
-        if (mSession != null) {
-            mSession.unselectTrack(track);
-        }
-    }
-
-    /**
-     * Returns a list which includes of track information. May return {@code null} if the
-     * information is not available.
-     */
-    public List<TvTrackInfo> getTracks() {
+    public List<TvTrackInfo> getTracks(int type) {
         if (mSession == null) {
             return null;
         }
-        return mSession.getTracks();
+        return mSession.getTracks(type);
     }
 
     /**
-     * Returns a list of selected tracks. May return {@code null} if the information is not
-     * available.
+     * Returns the ID of the selected track for a given type. Returns {@code null} if the
+     * information is not available or the track is not selected.
+     *
+     * @param type The type of the selected tracks. The type can be {@link TvTrackInfo#TYPE_AUDIO},
+     *            {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
+     * @see #selectTrack
+     * @see #getTracks
      */
-    public List<TvTrackInfo> getSelectedTracks() {
+    public String getSelectedTrack(int type) {
         if (mSession == null) {
             return null;
         }
-        return mSession.getSelectedTracks();
+        return mSession.getSelectedTrack(type);
     }
 
     /**
@@ -592,22 +586,6 @@
                 location[0] + getWidth(), location[1] + getHeight());
     }
 
-    private void updateVideoSize(List<TvTrackInfo> tracks) {
-        for (TvTrackInfo track : tracks) {
-            if (track.getType() == TvTrackInfo.TYPE_VIDEO) {
-                int width = track.getVideoWidth();
-                int height = track.getVideoHeight();
-                if (width != mVideoWidth || height != mVideoHeight) {
-                    mVideoWidth = width;
-                    mVideoHeight = height;
-                    if (mListener != null) {
-                        mListener.onVideoSizeChanged(mSessionCallback.mInputId, width, height);
-                    }
-                }
-            }
-        }
-    }
-
     /**
      * Interface used to receive various status updates on the {@link TvView}.
      */
@@ -650,16 +628,19 @@
          * @param inputId The ID of the TV input bound to this view.
          * @param tracks A list which includes track information.
          */
-        public void onTrackInfoChanged(String inputId, List<TvTrackInfo> tracks) {
+        public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
         }
 
         /**
          * This is called when there is a change on the selected tracks.
          *
          * @param inputId The ID of the TV input bound to this view.
-         * @param selectedTracks A list which includes track information.
+         * @param type The type of the track selected. The type can be
+         *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
+         *            {@link TvTrackInfo#TYPE_SUBTITLE}.
+         * @param trackId The ID of the track selected.
          */
-        public void onTrackSelectionChanged(String inputId, List<TvTrackInfo> selectedTracks) {
+        public void onTrackSelected(String inputId, int type, String trackId) {
         }
 
         /**
@@ -807,29 +788,29 @@
         }
 
         @Override
-        public void onTrackInfoChanged(Session session, List<TvTrackInfo> tracks) {
+        public void onTracksChanged(Session session, List<TvTrackInfo> tracks) {
             if (this != mSessionCallback) {
                 return;
             }
             if (DEBUG) {
-                Log.d(TAG, "onTrackInfoChanged()");
+                Log.d(TAG, "onTracksChanged()");
             }
             if (mListener != null) {
-                mListener.onTrackInfoChanged(mInputId, tracks);
+                mListener.onTracksChanged(mInputId, tracks);
             }
         }
 
         @Override
-        public void onTrackSelectionChanged(Session session, List<TvTrackInfo> selectedTracks) {
+        public void onTrackSelected(Session session, int type, String trackId) {
             if (this != mSessionCallback) {
                 return;
             }
             if (DEBUG) {
-                Log.d(TAG, "onTrackInfoChanged()");
+                Log.d(TAG, "onTrackSelected()");
             }
-            updateVideoSize(selectedTracks);
+            // TODO: Update the video size when the type is TYPE_VIDEO.
             if (mListener != null) {
-                mListener.onTrackSelectionChanged(mInputId, selectedTracks);
+                mListener.onTrackSelected(mInputId, type, trackId);
             }
         }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
index a23dd15..6c8ca20 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -27,6 +27,8 @@
 import android.os.Bundle;
 import android.provider.DocumentsContract.Document;
 
+import com.android.documentsui.model.DocumentInfo;
+
 /**
  * Cursor wrapper that presents a sorted view of the underlying cursor. Handles
  * common {@link Document} sorting modes, such as ordering directories first.
@@ -68,7 +70,7 @@
                     final String displayName = getCursorString(
                             cursor, Document.COLUMN_DISPLAY_NAME);
                     if (Document.MIME_TYPE_DIR.equals(mimeType)) {
-                        mValueString[i] = '\001' + displayName;
+                        mValueString[i] = DocumentInfo.DIR_PREFIX + displayName;
                     } else {
                         mValueString[i] = displayName;
                     }
@@ -180,14 +182,7 @@
 
                 final String lhs = pivotValue;
                 final String rhs = value[mid];
-                final int compare;
-                if (lhs == null) {
-                    compare = -1;
-                } else if (rhs == null) {
-                    compare = 1;
-                } else {
-                    compare = lhs.compareToIgnoreCase(rhs);
-                }
+                final int compare = DocumentInfo.compareToIgnoreCaseNullable(lhs, rhs);
 
                 if (compare < 0) {
                     right = mid;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 91d9124..1c5ca86 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -25,6 +25,7 @@
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsProvider;
+import android.text.TextUtils;
 
 import com.android.documentsui.DocumentsApplication;
 import com.android.documentsui.RootCursorWrapper;
@@ -36,6 +37,7 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.net.ProtocolException;
+import java.text.Collator;
 
 /**
  * Representation of a {@link Document}.
@@ -44,6 +46,13 @@
     private static final int VERSION_INIT = 1;
     private static final int VERSION_SPLIT_URI = 2;
 
+    private static final Collator sCollator;
+
+    static {
+        sCollator = Collator.getInstance();
+        sCollator.setStrength(Collator.SECONDARY);
+    }
+
     public String authority;
     public String documentId;
     public String mimeType;
@@ -268,9 +277,30 @@
         throw fnfe;
     }
 
+    /**
+     * String prefix used to indicate the document is a directory.
+     */
+    public static final char DIR_PREFIX = '\001';
+
+    /**
+     * Compare two strings against each other using system default collator in a
+     * case-insensitive mode. Clusters strings prefixed with {@link #DIR_PREFIX}
+     * before other items.
+     */
     public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
-        if (lhs == null) return -1;
-        if (rhs == null) return 1;
-        return lhs.compareToIgnoreCase(rhs);
+        final boolean leftEmpty = TextUtils.isEmpty(lhs);
+        final boolean rightEmpty = TextUtils.isEmpty(rhs);
+
+        if (leftEmpty && rightEmpty) return 0;
+        if (leftEmpty) return -1;
+        if (rightEmpty) return 1;
+
+        final boolean leftDir = (lhs.charAt(0) == DIR_PREFIX);
+        final boolean rightDir = (rhs.charAt(0) == DIR_PREFIX);
+
+        if (leftDir && !rightDir) return -1;
+        if (rightDir && !leftDir) return 1;
+
+        return sCollator.compare(lhs, rhs);
     }
 }
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 9473eb9..c323a33 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -18,6 +18,7 @@
 
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.MatrixCursor;
@@ -28,7 +29,9 @@
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.FileUtils;
+import android.os.Handler;
 import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.OnCloseListener;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
 import android.provider.DocumentsContract;
@@ -80,6 +83,7 @@
     private static final String ROOT_ID_PRIMARY_EMULATED = "primary";
 
     private StorageManager mStorageManager;
+    private Handler mHandler;
 
     private final Object mRootsLock = new Object();
 
@@ -96,6 +100,7 @@
     @Override
     public boolean onCreate() {
         mStorageManager = (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
+        mHandler = new Handler();
 
         mRoots = Lists.newArrayList();
         mIdToRoot = Maps.newHashMap();
@@ -422,7 +427,25 @@
             String documentId, String mode, CancellationSignal signal)
             throws FileNotFoundException {
         final File file = getFileForDocId(documentId);
-        return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode));
+        final int pfdMode = ParcelFileDescriptor.parseMode(mode);
+        if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY) {
+            return ParcelFileDescriptor.open(file, pfdMode);
+        } else {
+            try {
+                // When finished writing, kick off media scanner
+                return ParcelFileDescriptor.open(file, pfdMode, mHandler, new OnCloseListener() {
+                    @Override
+                    public void onClose(IOException e) {
+                        final Intent intent = new Intent(
+                                Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+                        intent.setData(Uri.fromFile(file));
+                        getContext().sendBroadcast(intent);
+                    }
+                });
+            } catch (IOException e) {
+                throw new FileNotFoundException("Failed to open for writing: " + e);
+            }
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/res/drawable/qs_navbar_scrim.xml b/packages/SystemUI/res/drawable/qs_navbar_scrim.xml
new file mode 100644
index 0000000..bbb2617
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_navbar_scrim.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient
+            android:type="linear"
+            android:angle="90"
+            android:startColor="#55000000"
+            android:endColor="#00000000" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 53a832a..fa1077b 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -115,4 +115,12 @@
         layout="@layout/keyguard_bottom_area"
         android:visibility="gone" />
 
+    <com.android.systemui.statusbar.AlphaOptimizedView
+        android:id="@+id/qs_navbar_scrim"
+        android:layout_height="96dp"
+        android:layout_width="match_parent"
+        android:layout_gravity="bottom"
+        android:visibility="invisible"
+        android:background="@drawable/qs_navbar_scrim" />
+
 </com.android.systemui.statusbar.phone.NotificationPanelView><!-- end of sliding panel -->
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index f0f50e1..eff3758 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -30,25 +30,39 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         />
-    <TextView
-        android:id="@+id/more_text"
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:layout_marginStart="20dp"
-        android:layout_gravity="center_vertical"
-        android:background="@drawable/keyguard_overflow_number_background"
-        android:gravity="center"
-        android:textColor="#ff686868"
-        android:textStyle="bold"
-        android:textSize="14dp"
-        />
-    <com.android.systemui.statusbar.NotificationOverflowIconsView
-        android:id="@+id/overflow_icons_view"
-        android:layout_gravity="center_vertical"
-        android:layout_marginStart="68dp"
-        android:layout_width="120dp"
-        android:layout_height="wrap_content"
-        />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <TextView
+            android:id="@+id/more_text"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_marginStart="20dp"
+            android:layout_marginEnd="16dp"
+            android:layout_gravity="center_vertical"
+            android:background="@drawable/keyguard_overflow_number_background"
+            android:gravity="center"
+            android:textColor="#ff686868"
+            android:textStyle="bold"
+            android:textSize="14dp"
+            />
+        <com.android.systemui.statusbar.StatusBarIconView
+            android:id="@+id/more_icon_overflow"
+            android:layout_width="@dimen/status_bar_icon_size"
+            android:layout_height="match_parent"
+            android:src="@drawable/stat_notify_more"
+            android:tint="@color/keyguard_overflow_content_color"
+            android:visibility="gone"
+            />
+        <com.android.systemui.statusbar.NotificationOverflowIconsView
+            android:id="@+id/overflow_icons_view"
+            android:layout_gravity="center_vertical"
+            android:layout_marginEnd="8dp"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            />
+    </LinearLayout>
 
     <com.android.systemui.statusbar.NotificationScrimView
         android:id="@+id/scrim_view"
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 9650435..41d5984 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -25,7 +25,7 @@
     }
 
     void showRecents(boolean triggeredFromAltTab, View statusBarView);
-    void hideRecents(boolean triggeredFromAltTab);
+    void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
     void toggleRecents(Display display, int layoutDirection, View statusBarView);
     void preloadRecents();
     void cancelPreloadingRecents();
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 6c30c89..a18b0c0 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -384,10 +384,19 @@
         }
 
         if (!mDragging) {
-            // We are not doing anything, make sure the long press callback
-            // is not still ticking like a bomb waiting to go off.
-            removeLongPressCallback();
-            return false;
+            if (mCallback.getChildAtPosition(ev) != null) {
+
+                // We are dragging directly over a card, make sure that we also catch the gesture
+                // even if nobody else wants the touch event.
+                onInterceptTouchEvent(ev);
+                return true;
+            } else {
+
+                // We are not doing anything, make sure the long press callback
+                // is not still ticking like a bomb waiting to go off.
+                removeLongPressCallback();
+                return false;
+            }
         }
 
         mVelocityTracker.addMovement(ev);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
index e03c01c..4cf5fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -77,9 +77,9 @@
     }
 
     @Override
-    public void hideRecents(boolean triggeredFromAltTab) {
+    public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         if (mUseAlternateRecents) {
-            mAlternateRecents.onHideRecents(triggeredFromAltTab);
+            mAlternateRecents.onHideRecents(triggeredFromAltTab, triggeredFromHomeKey);
         } else {
             Intent intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
             intent.setPackage("com.android.systemui");
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 0b08b93..a55c0f2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -51,8 +51,13 @@
     final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
     final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
     final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail";
+    final public static String EXTRA_FROM_TASK_ID = "recents.activeTaskId";
     final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
-    final public static String EXTRA_TRIGGERED_FROM_TASK_ID = "recents.activeTaskId";
+    final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "recents.triggeredFromHomeKey";
+
+    final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
+    final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
+    final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
 
     final static int sMinToggleDelay = 425;
 
@@ -126,14 +131,15 @@
     }
 
     /** Hides the recents */
-    public void onHideRecents(boolean triggeredFromAltTab) {
+    public void onHideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         if (mBootCompleted) {
             if (isRecentsTopMost(getTopMostTask(), null)) {
                 // Notify recents to hide itself
-                Intent intent = new Intent(RecentsActivity.ACTION_HIDE_RECENTS_ACTIVITY);
+                Intent intent = new Intent(ACTION_HIDE_RECENTS_ACTIVITY);
                 intent.setPackage(mContext.getPackageName());
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                intent.putExtra(RecentsActivity.EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
+                intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
+                intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
                 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
             }
         }
@@ -220,7 +226,7 @@
         AtomicBoolean isTopTaskHome = new AtomicBoolean();
         if (isRecentsTopMost(topTask, isTopTaskHome)) {
             // Notify recents to toggle itself
-            Intent intent = new Intent(RecentsActivity.ACTION_TOGGLE_RECENTS_ACTIVITY);
+            Intent intent = new Intent(ACTION_TOGGLE_RECENTS_ACTIVITY);
             intent.setPackage(mContext.getPackageName());
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
@@ -403,7 +409,7 @@
             intent.putExtra(extraFlag, true);
         }
         intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
-        intent.putExtra(EXTRA_TRIGGERED_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
+        intent.putExtra(EXTRA_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
         if (opts != null) {
             mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
         } else {
@@ -442,7 +448,7 @@
     public void onAnimationStarted() {
         // Notify recents to start the enter animation
         if (!mStartAnimationTriggered) {
-            Intent intent = new Intent(RecentsActivity.ACTION_START_ENTER_ANIMATION);
+            Intent intent = new Intent(ACTION_START_ENTER_ANIMATION);
             intent.setPackage(mContext.getPackageName());
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 75fbad8..417049c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -59,12 +59,6 @@
         RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
         DebugOverlayView.DebugOverlayViewCallbacks {
 
-    // Actions and Extras sent from AlternateRecentsComponent
-    final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
-    final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
-    final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
-    final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
-
     RecentsConfiguration mConfig;
     boolean mVisible;
 
@@ -131,18 +125,22 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(ACTION_HIDE_RECENTS_ACTIVITY)) {
-                if (intent.getBooleanExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
+            if (action.equals(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY)) {
+                // Mark Recents as no longer visible
+                AlternateRecentsComponent.notifyVisibilityChanged(false);
+                if (intent.getBooleanExtra(AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
                     // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
                     dismissRecentsToFocusedTaskOrHome(false);
-                } else {
+                } else if (intent.getBooleanExtra(AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
                     // Otherwise, dismiss Recents to Home
                     dismissRecentsToHome(true);
+                } else {
+                    // Do nothing, another activity is being launched on top of Recents
                 }
-            } else if (action.equals(ACTION_TOGGLE_RECENTS_ACTIVITY)) {
+            } else if (action.equals(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
                 // If we are toggling Recents, then first unfilter any filtered stacks first
                 dismissRecentsToFocusedTaskOrHome(true);
-            } else if (action.equals(ACTION_START_ENTER_ANIMATION)) {
+            } else if (action.equals(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION)) {
                 // Try and start the enter animation (or restart it on configuration changed)
                 ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
                 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
@@ -195,11 +193,11 @@
                 AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false);
         mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra(
                 AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
+        mConfig.launchedToTaskId = launchIntent.getIntExtra(
+                AlternateRecentsComponent.EXTRA_FROM_TASK_ID, -1);
         mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
                 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
         mConfig.launchedWithNoRecentTasks = !root.hasTasks();
-        mConfig.launchedToTaskId = launchIntent.getIntExtra(
-                AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_TASK_ID, -1);
 
         // Mark the task that is the launch target
         int taskStackCount = stacks.size();
@@ -444,9 +442,9 @@
 
         // Register the broadcast receiver to handle messages from our service
         IntentFilter filter = new IntentFilter();
-        filter.addAction(ACTION_HIDE_RECENTS_ACTIVITY);
-        filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY);
-        filter.addAction(ACTION_START_ENTER_ANIMATION);
+        filter.addAction(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY);
+        filter.addAction(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY);
+        filter.addAction(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION);
         registerReceiver(mServiceBroadcastReceiver, filter);
 
         // Register any broadcast receivers for the task loader
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a33fb3a..ebde080 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -684,10 +684,11 @@
     }
 
     @Override
-    public void hideRecentApps(boolean triggeredFromAltTab) {
+    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         int msg = MSG_HIDE_RECENT_APPS;
         mHandler.removeMessages(msg);
-        mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
+        mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
+                triggeredFromHomeKey ? 1 : 0).sendToTarget();
     }
 
     @Override
@@ -797,9 +798,9 @@
         }
     }
 
-    protected void hideRecents(boolean triggeredFromAltTab) {
+    protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         if (mRecents != null) {
-            mRecents.hideRecents(triggeredFromAltTab);
+            mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
         }
     }
 
@@ -890,13 +891,12 @@
 
     protected class H extends Handler {
         public void handleMessage(Message m) {
-            Intent intent;
             switch (m.what) {
              case MSG_SHOW_RECENT_APPS:
                  showRecents(m.arg1 > 0);
                  break;
              case MSG_HIDE_RECENT_APPS:
-                 hideRecents(m.arg1 > 0);
+                 hideRecents(m.arg1 > 0, m.arg2 > 0);
                  break;
              case MSG_TOGGLE_RECENTS_APPS:
                  toggleRecents();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 9107790..63dd1e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -89,7 +89,7 @@
                 boolean showImeSwitcher);
         public void setHardKeyboardStatus(boolean available, boolean enabled);
         public void showRecentApps(boolean triggeredFromAltTab);
-        public void hideRecentApps(boolean triggeredFromAltTab);
+        public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
         public void toggleRecentApps();
         public void preloadRecentApps();
         public void cancelPreloadRecentApps();
@@ -191,11 +191,12 @@
         }
     }
 
-    public void hideRecentApps(boolean triggeredFromAltTab) {
+    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         synchronized (mList) {
             mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
             mHandler.obtainMessage(MSG_HIDE_RECENT_APPS,
-                    triggeredFromAltTab ? 1 : 0, 0, null).sendToTarget();
+                    triggeredFromAltTab ? 1 : 0, triggeredFromHomeKey ? 1 : 0,
+                    null).sendToTarget();
         }
     }
 
@@ -306,7 +307,7 @@
                     mCallbacks.showRecentApps(msg.arg1 != 0);
                     break;
                 case MSG_HIDE_RECENT_APPS:
-                    mCallbacks.hideRecentApps(msg.arg1 != 0);
+                    mCallbacks.hideRecentApps(msg.arg1 != 0, msg.arg2 != 0);
                     break;
                 case MSG_TOGGLE_RECENT_APPS:
                     mCallbacks.toggleRecentApps();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index 451c5c5..edfd205 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -38,6 +38,7 @@
         super.onFinishInflate();
         mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
         mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
+        mIconsView.setOverflowIndicator(findViewById(R.id.more_icon_overflow));
     }
 
     public NotificationOverflowIconsView getIconsView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 9cc559f..20ffba2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -98,7 +98,7 @@
 
     private void initDimens() {
         final ViewConfiguration configuration = ViewConfiguration.get(mContext);
-        mTouchSlop = configuration.getScaledTouchSlop();
+        mTouchSlop = configuration.getScaledPagingTouchSlop();
         mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
         mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
                 R.dimen.keyguard_min_swipe_amount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 82e59c0..ef4a73e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -33,7 +33,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -72,6 +71,7 @@
     private UnlockMethodCache mUnlockMethodCache;
     private LockPatternUtils mLockPatternUtils;
     private FlashlightController mFlashlightController;
+    private PreviewInflater mPreviewInflater;
 
     public KeyguardBottomAreaView(Context context) {
         super(context);
@@ -108,6 +108,7 @@
         updateTrust();
         setClipChildren(false);
         setClipToPadding(false);
+        mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext));
         inflatePreviews();
     }
 
@@ -208,7 +209,8 @@
     public void launchCamera() {
         mFlashlightController.killFlashlight();
         Intent intent = getCameraIntent();
-        if (intent == SECURE_CAMERA_INTENT) {
+        if (intent == SECURE_CAMERA_INTENT &&
+                !mPreviewInflater.wouldLaunchResolverActivity(intent)) {
             mContext.startActivityAsUser(intent, UserHandle.CURRENT);
         } else {
             mActivityStarter.startActivity(intent);
@@ -277,9 +279,8 @@
     }
 
     private void inflatePreviews() {
-        PreviewInflater inflater = new PreviewInflater(mContext, new LockPatternUtils(mContext));
-        mPhonePreview = inflater.inflatePreview(PHONE_INTENT);
-        mCameraPreview = inflater.inflatePreview(getCameraIntent());
+        mPhonePreview = mPreviewInflater.inflatePreview(PHONE_INTENT);
+        mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent());
         if (mPhonePreview != null) {
             mPreviewContainer.addView(mPhonePreview);
             mPhonePreview.setVisibility(View.INVISIBLE);
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 98bb591..aa68b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -69,6 +69,7 @@
     private TextView mClockView;
     private View mReserveNotificationSpace;
     private MirrorView mSystemIconsCopy;
+    private View mQsNavbarScrim;
 
     private NotificationStackScrollLayout mNotificationStackScroller;
     private int mNotificationTopPadding;
@@ -149,6 +150,8 @@
 
     private boolean mShadeEmpty;
 
+    private boolean mQsScrimEnabled = true;
+
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mSystemIconsCopy = new MirrorView(context);
@@ -183,6 +186,7 @@
         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
                 android.R.interpolator.linear_out_slow_in);
         mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
+        mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
         mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
     }
 
@@ -446,7 +450,13 @@
                 mIntercepting = false;
                 break;
         }
-        return !mQsExpanded && super.onInterceptTouchEvent(event);
+
+        // Allow closing the whole panel when in SHADE state.
+        if (mStatusBarState == StatusBarState.SHADE) {
+            return super.onInterceptTouchEvent(event);
+        } else {
+            return !mQsExpanded && super.onInterceptTouchEvent(event);
+        }
     }
 
     private void resetDownStates(MotionEvent event) {
@@ -499,7 +509,7 @@
             return true;
         }
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
-                && mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
+                && mStatusBar.getBarState() != StatusBarState.KEYGUARD && !mQsExpanded) {
 
             // Down in the empty area while fully expanded - go to QS.
             mQsTracking = true;
@@ -512,7 +522,7 @@
         if (mExpandedHeight != 0) {
             handleQsDown(event);
         }
-        if (!mTwoFingerQsExpand && (mQsTracking || mQsExpanded)) {
+        if (!mTwoFingerQsExpand && mQsTracking) {
             onQsTouch(event);
             if (!mConflictingQsExpansionGesture) {
                 return true;
@@ -535,9 +545,15 @@
         return true;
     }
 
+    private boolean isInQsArea(float x, float y) {
+        return mStatusBarState != StatusBarState.SHADE
+                || y <= mNotificationStackScroller.getBottomMostNotificationBottom()
+                || y <= mQsContainer.getY() + mQsContainer.getHeight();
+    }
+
     private void handleQsDown(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN
-                && shouldQuickSettingsIntercept(event.getX(), event.getY(), 0)) {
+                && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
             mQsTracking = true;
             onQsExpansionStarted();
             mInitialHeightOnTouch = mQsExpansionHeight;
@@ -623,8 +639,9 @@
     }
 
     @Override
-    public void onOverscrolled(int amount) {
-        if (mIntercepting) {
+    public void onOverscrolled(float lastTouchX, float lastTouchY, int amount) {
+        if (mIntercepting && shouldQuickSettingsIntercept(lastTouchX, lastTouchY,
+                -1 /* yDiff: Not relevant here */)) {
             onQsExpansionStarted(amount);
             mInitialHeightOnTouch = mQsExpansionHeight;
             mInitialTouchY = mLastTouchY;
@@ -882,6 +899,10 @@
                 mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
         mScrollView.setTouchEnabled(mQsExpanded);
         updateEmptyShadeView();
+        mQsNavbarScrim.setVisibility(mStatusBarState == StatusBarState.SHADE && mQsExpanded
+                && !mStackScrollerOverscrolling && mQsScrimEnabled
+                        ? View.VISIBLE
+                        : View.INVISIBLE);
     }
 
     private void setQsExpansion(float height) {
@@ -908,6 +929,10 @@
                 mKeyguardStatusBar.setAlpha(alpha);
             }
         }
+        if (mStatusBarState == StatusBarState.SHADE && mQsExpanded
+                && !mStackScrollerOverscrolling && mQsScrimEnabled) {
+            mQsNavbarScrim.setAlpha(getQsExpansionFraction());
+        }
     }
 
     private void updateNotificationScrim(float height) {
@@ -1025,7 +1050,7 @@
         boolean onHeader = x >= header.getLeft() && x <= header.getRight()
                 && y >= header.getTop() && y <= header.getBottom();
         if (mQsExpanded) {
-            return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
+            return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0) && isInQsArea(x, y);
         } else {
             return onHeader;
         }
@@ -1075,8 +1100,9 @@
         }
         if (!isInSettings()) {
             return mNotificationStackScroller.isScrolledToBottom();
+        } else {
+            return mScrollView.isScrolledToBottom();
         }
-        return super.isScrolledToBottom();
     }
 
     @Override
@@ -1342,6 +1368,9 @@
                 || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
             mAfforanceHelper.animateHideLeftRightIcon();
         }
+        if (mQsExpanded) {
+            mTwoFingerQsExpand = true;
+        }
     }
 
     @Override
@@ -1629,4 +1658,12 @@
         // Hide "No notifications" in QS.
         mNotificationStackScroller.updateEmptyShadeView(mShadeEmpty && !mQsExpanded);
     }
+
+    public void setQsScrimEnabled(boolean qsScrimEnabled) {
+        boolean changed = mQsScrimEnabled != qsScrimEnabled;
+        mQsScrimEnabled = qsScrimEnabled;
+        if (changed) {
+            updateQsState();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
index 5920580..ee6b1a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -31,6 +31,8 @@
     private int mLastOverscrollAmount;
     private boolean mTouchEnabled = true;
     private boolean mHandlingTouchEvent;
+    private float mLastX;
+    private float mLastY;
 
     public ObservableScrollView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -65,6 +67,8 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         mHandlingTouchEvent = true;
+        mLastX = ev.getX();
+        mLastY = ev.getY();
         boolean result = super.onTouchEvent(ev);
         mHandlingTouchEvent = false;
         return result;
@@ -73,6 +77,8 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         mHandlingTouchEvent = true;
+        mLastX = ev.getX();
+        mLastY = ev.getY();
         boolean result = super.onInterceptTouchEvent(ev);
         mHandlingTouchEvent = false;
         return result;
@@ -107,12 +113,12 @@
     protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
         super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
         if (mListener != null && mLastOverscrollAmount > 0) {
-            mListener.onOverscrolled(mLastOverscrollAmount);
+            mListener.onOverscrolled(mLastX, mLastY, mLastOverscrollAmount);
         }
     }
 
     public interface Listener {
         void onScrollChanged();
-        void onOverscrolled(int amount);
+        void onOverscrolled(float lastX, float lastY, int amount);
     }
 }
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 3c111b6..469a831 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -853,6 +853,12 @@
         }
     }
 
+    private final Runnable mPostCollapseRunnable = new Runnable() {
+        @Override
+        public void run() {
+            collapse();
+        }
+    };
     private boolean onMiddleClicked() {
         switch (mStatusBar.getBarState()) {
             case StatusBarState.KEYGUARD:
@@ -862,7 +868,10 @@
                 mStatusBar.goToKeyguard();
                 return true;
             case StatusBarState.SHADE:
-                collapse();
+
+                // This gets called in the middle of the touch handling, where the state is still
+                // that we are tracking the panel. Collapse the panel after this is done.
+                post(mPostCollapseRunnable);
                 return false;
             default:
                 return true;
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 e7a9779..750fb39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -650,6 +650,7 @@
                         if (mSearchPanelView != null) {
                             mSearchPanelView.setHorizontal(isVertical);
                         }
+                        mNotificationPanel.setQsScrimEnabled(!isVertical);
                     }
                 });
                 mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
@@ -2129,8 +2130,10 @@
         }
 
         if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
-            mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
-            mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
+            if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
+                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
+                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
+            }
         }
 
         if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
@@ -2744,7 +2747,7 @@
         pw.print("  mMediaMetadata=");
         pw.print(mMediaMetadata);
         if (mMediaMetadata != null) {
-            pw.print(" title=" + mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE));
+            pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
         }
         pw.println();
 
@@ -3751,11 +3754,11 @@
     }
 
     @Override
-    protected void hideRecents(boolean triggeredFromAltTab) {
+    protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         // Unset the recents visibility flag
         mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
         notifyUiVisibilityChanged(mSystemUiVisibility);
-        super.hideRecents(triggeredFromAltTab);
+        super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 0134fe8..330b599 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -63,7 +63,6 @@
                     // Just an old-fashioned ImageView
                     performLongClick();
                 }
-                setPressed(false);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
index d405172..cdbe494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
@@ -107,6 +107,19 @@
         return info;
     }
 
+    public boolean wouldLaunchResolverActivity(Intent intent) {
+        PackageManager packageManager = mContext.getPackageManager();
+        final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
+                intent, PackageManager.MATCH_DEFAULT_ONLY, mLockPatternUtils.getCurrentUser());
+        if (appList.size() == 0) {
+            return false;
+        }
+        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
+                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+                mLockPatternUtils.getCurrentUser());
+        return wouldLaunchResolverActivity(resolved, appList);
+    }
+
     private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
         // If the list contains the above resolved activity, then it can't be
         // ResolverActivity itself.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index fcca5fa..fec5e74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -568,8 +568,11 @@
             float childTop = slidingChild.getTranslationY();
             float top = childTop + slidingChild.getClipTopAmount();
             float bottom = top + slidingChild.getActualHeight();
-            int left = slidingChild.getLeft();
-            int right = slidingChild.getRight();
+
+            // Allow the full width of this view to prevent gesture conflict on Keyguard (phone and
+            // camera affordance).
+            int left = 0;
+            int right = getWidth();
 
             if (touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 return slidingChild;
@@ -2120,6 +2123,22 @@
         return mDismissView.getHeight() + mPaddingBetweenElementsNormal;
     }
 
+    public float getBottomMostNotificationBottom() {
+        final int count = getChildCount();
+        float max = 0;
+        for (int childIdx = 0; childIdx < count; childIdx++) {
+            ExpandableView child = (ExpandableView) getChildAt(childIdx);
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+            float bottom = child.getTranslationY() + child.getActualHeight();
+            if (bottom > max) {
+                max = bottom;
+            }
+        }
+        return max + getTranslationY();
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 984a5f4..d202036 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -680,15 +680,15 @@
             // never disable touch interactions for remote playback, the muting is not tied to
             // the state of the phone.
             sc.seekbarView.setEnabled(!fixedVolume);
-        } else if (fixedVolume ||
-                        (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
-                        (sSafetyWarning != null)) {
-            sc.seekbarView.setEnabled(false);
         } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
             sc.seekbarView.setEnabled(false);
             sc.icon.setEnabled(false);
             sc.icon.setAlpha(mDisabledAlpha);
             sc.icon.setClickable(false);
+        } else if (fixedVolume ||
+                (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
+                (sSafetyWarning != null)) {
+            sc.seekbarView.setEnabled(false);
         } else {
             sc.seekbarView.setEnabled(true);
             sc.icon.setEnabled(true);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 324f536..14f6c5a 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -279,6 +279,7 @@
     int[] mNavigationBarHeightForRotation = new int[4];
     int[] mNavigationBarWidthForRotation = new int[4];
 
+    boolean mBootMessageNeedsHiding;
     KeyguardServiceDelegate mKeyguardDelegate;
     // The following are only accessed on the mHandler thread.
     boolean mKeyguardDrawComplete;
@@ -537,6 +538,7 @@
     private static final int MSG_WAKING_UP = 8;
     private static final int MSG_DISPATCH_SHOW_RECENTS = 9;
     private static final int MSG_DISPATCH_SHOW_GLOBAL_ACTIONS = 10;
+    private static final int MSG_HIDE_BOOT_MESSAGE = 11;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -579,6 +581,9 @@
                 case MSG_WAKING_UP:
                     handleWakingUp((ScreenOnListener) msg.obj);
                     break;
+                case MSG_HIDE_BOOT_MESSAGE:
+                    handleHideBootMessage();
+                    break;
             }
         }
     }
@@ -1096,9 +1101,7 @@
         initializeHdmiState();
 
         // Match current screen state.
-        if (mPowerManager.isInteractive()) {
-            wakingUp(null);
-        } else {
+        if (!mPowerManager.isInteractive()) {
             goingToSleep(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
         }
     }
@@ -2138,7 +2141,7 @@
 
         // Cancel any pending meta actions if we see any other keys being pressed between the down
         // of the meta key and its corresponding up.
-        if (mPendingMetaAction && keyCode != KeyEvent.KEYCODE_META_LEFT) {
+        if (mPendingMetaAction && KeyEvent.isMetaKey(keyCode)) {
             mPendingMetaAction = false;
         }
 
@@ -2343,11 +2346,10 @@
                         UserHandle.CURRENT_OR_SELF);
             }
             return -1;
-        } else if (keyCode == KeyEvent.KEYCODE_META_LEFT) {
+        } else if (KeyEvent.isMetaKey(keyCode)) {
             if (down) {
                 mPendingMetaAction = true;
             } else if (mPendingMetaAction) {
-                mPendingMetaAction = false;
                 launchAssistAction();
             }
             return -1;
@@ -2426,7 +2428,9 @@
         if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) {
             if (mRecentAppsHeldModifiers == 0 && !keyguardOn) {
                 final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
-                if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) {
+                if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)
+                        || KeyEvent.metaStateHasModifiers(
+                        shiftlessModifiers, KeyEvent.META_META_ON)) {
                     mRecentAppsHeldModifiers = shiftlessModifiers;
                     showRecentApps(true);
                     return -1;
@@ -2435,7 +2439,7 @@
         } else if (!down && mRecentAppsHeldModifiers != 0
                 && (metaState & mRecentAppsHeldModifiers) == 0) {
             mRecentAppsHeldModifiers = 0;
-            hideRecentApps(true);
+            hideRecentApps(true, false);
         }
 
         // Handle keyboard language switching.
@@ -2459,7 +2463,7 @@
         }
 
         // Reserve all the META modifier combos for system behavior
-        if ((metaState & KeyEvent.META_META_LEFT_ON) != 0) {
+        if ((metaState & KeyEvent.META_META_ON) != 0) {
             return -1;
         }
 
@@ -2654,12 +2658,12 @@
         }
     }
 
-    private void hideRecentApps(boolean triggeredFromAltTab) {
+    private void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHome) {
         mPreloadedRecentApps = false; // preloading no longer needs to be canceled
         try {
             IStatusBarService statusbar = getStatusBarService();
             if (statusbar != null) {
-                statusbar.hideRecentApps(triggeredFromAltTab);
+                statusbar.hideRecentApps(triggeredFromAltTab, triggeredFromHome);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "RemoteException when closing recent apps", e);
@@ -2701,7 +2705,7 @@
                 // Hide Recents and notify it to launch Home
                 awakenDreams();
                 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-                hideRecentApps(false);
+                hideRecentApps(false, true);
             } else {
                 // Otherwise, just launch Home
                 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
@@ -4649,6 +4653,26 @@
         }
 
         setKeyguardDrawn();
+
+        if (mBootMessageNeedsHiding) {
+            handleHideBootMessage();
+            mBootMessageNeedsHiding = false;
+        }
+    }
+
+    private void handleHideBootMessage() {
+        if (mBootMsgDialog == null) {
+            if (DEBUG_WAKEUP) Slog.d(TAG, "handleHideBootMessage: boot message not up");
+            return;
+        }
+        if (!mKeyguardDrawComplete || !mWindowManagerDrawComplete) {
+            if (DEBUG_WAKEUP) Slog.d(TAG, "handleHideBootMessage: deferring until keyguard ready");
+            mBootMessageNeedsHiding = true;
+            return;
+        }
+        if (DEBUG_WAKEUP) Slog.d(TAG, "handleHideBootMessage: dismissing");
+        mBootMsgDialog.dismiss();
+        mBootMsgDialog = null;
     }
 
     @Override
@@ -5106,14 +5130,7 @@
     /** {@inheritDoc} */
     @Override
     public void hideBootMessages() {
-        mHandler.post(new Runnable() {
-            @Override public void run() {
-                if (mBootMsgDialog != null) {
-                    mBootMsgDialog.dismiss();
-                    mBootMsgDialog = null;
-                }
-            }
-        });
+        mHandler.sendEmptyMessage(MSG_HIDE_BOOT_MESSAGE);
     }
 
     /** {@inheritDoc} */
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
index 2fa23c9..3f95427 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
@@ -16,421 +16,32 @@
 
 package com.android.server.appwidget;
 
-import android.app.ActivityManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-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.util.Slog;
-import android.util.SparseArray;
-import android.widget.RemoteViews;
 
-import com.android.internal.appwidget.IAppWidgetHost;
-import com.android.internal.appwidget.IAppWidgetService;
-import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.AppWidgetBackupBridge;
-import com.android.server.WidgetBackupProvider;
 import com.android.server.SystemService;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.List;
-
-
 /**
  * SystemService that publishes an IAppWidgetService.
  */
-public class AppWidgetService extends SystemService implements WidgetBackupProvider {
-
-    static final String TAG = "AppWidgetService";
-
-    final Context mContext;
-    final Handler mSaveStateHandler;
-
-    final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
+public class AppWidgetService extends SystemService {
+    private final AppWidgetServiceImpl mImpl;
 
     public AppWidgetService(Context context) {
         super(context);
-        mContext = context;
-
-        mSaveStateHandler = BackgroundThread.getHandler();
-
-        mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
-        AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0, mSaveStateHandler);
-        mAppWidgetServices.append(0, primary);
+        mImpl = new AppWidgetServiceImpl(context);
     }
 
     @Override
     public void onStart() {
-        publishBinderService(Context.APPWIDGET_SERVICE, mServiceImpl);
-        AppWidgetBackupBridge.register(this);
+        publishBinderService(Context.APPWIDGET_SERVICE, mImpl);
+        AppWidgetBackupBridge.register(mImpl);
     }
 
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
-            mServiceImpl.systemRunning(isSafeMode());
+            mImpl.setSafeMode(isSafeMode());
         }
     }
-
-
-    // backup <-> app widget service bridge surface
-    @Override
-    public List<String> getWidgetParticipants(int userId) {
-        return mServiceImpl.getWidgetParticipants(userId);
-    }
-
-    @Override
-    public byte[] getWidgetState(String packageName, int userId) {
-        return mServiceImpl.getWidgetState(packageName, userId);
-    }
-
-    @Override
-    public void restoreStarting(int userId) {
-        mServiceImpl.restoreStarting(userId);
-    }
-
-    @Override
-    public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
-        mServiceImpl.restoreWidgetState(packageName, restoredState, userId);
-    }
-
-    @Override
-    public void restoreFinished(int userId) {
-        mServiceImpl.restoreFinished(userId);
-    }
-
-
-    // implementation entry point and binder service
-    private final AppWidgetServiceStub mServiceImpl = new AppWidgetServiceStub();
-
-    class AppWidgetServiceStub extends IAppWidgetService.Stub {
-
-        private boolean mSafeMode;
-
-        public void systemRunning(boolean safeMode) {
-            mSafeMode = safeMode;
-
-            mAppWidgetServices.get(0).systemReady(safeMode);
-
-            // Register for the boot completed broadcast, so we can send the
-            // ENABLE broacasts. If we try to send them now, they time out,
-            // because the system isn't ready to handle them yet.
-            IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
-            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-            mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                    filter, null, null);
-
-            // Register for configuration changes so we can update the names
-            // of the widgets when the locale changes.
-            mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                    new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
-
-            // Register for broadcasts about package install, etc., so we can
-            // update the provider list.
-            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addDataScheme("package");
-            mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                    filter, null, null);
-            // Register for events related to sdcard installation.
-            IntentFilter sdFilter = new IntentFilter();
-            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-            mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
-                    sdFilter, null, null);
-
-            IntentFilter userFilter = new IntentFilter();
-            userFilter.addAction(Intent.ACTION_USER_REMOVED);
-            userFilter.addAction(Intent.ACTION_USER_STOPPING);
-            mContext.registerReceiver(new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
-                        onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                                UserHandle.USER_NULL));
-                    } else if (Intent.ACTION_USER_STOPPING.equals(intent.getAction())) {
-                        onUserStopping(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                                UserHandle.USER_NULL));
-                    }
-                }
-            }, userFilter);
-        }
-
-        @Override
-        public int allocateAppWidgetId(String packageName, int hostId, int userId)
-                throws RemoteException {
-            return getImplForUser(userId).allocateAppWidgetId(packageName, hostId);
-        }
-
-        @Override
-        public int[] getAppWidgetIdsForHost(int hostId, int userId) throws RemoteException {
-            return getImplForUser(userId).getAppWidgetIdsForHost(hostId);
-        }
-
-        @Override
-        public void deleteAppWidgetId(int appWidgetId, int userId) throws RemoteException {
-            getImplForUser(userId).deleteAppWidgetId(appWidgetId);
-        }
-
-        @Override
-        public void deleteHost(int hostId, int userId) throws RemoteException {
-            getImplForUser(userId).deleteHost(hostId);
-        }
-
-        @Override
-        public void deleteAllHosts(int userId) throws RemoteException {
-            getImplForUser(userId).deleteAllHosts();
-        }
-
-        @Override
-        public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options,
-                int userId) throws RemoteException {
-            getImplForUser(userId).bindAppWidgetId(appWidgetId, provider, options);
-        }
-
-        @Override
-        public boolean bindAppWidgetIdIfAllowed(
-                String packageName, int appWidgetId, ComponentName provider, Bundle options,
-                int userId) throws RemoteException {
-            return getImplForUser(userId).bindAppWidgetIdIfAllowed(
-                    packageName, appWidgetId, provider, options);
-        }
-
-        @Override
-        public boolean hasBindAppWidgetPermission(String packageName, int userId)
-                throws RemoteException {
-            return getImplForUser(userId).hasBindAppWidgetPermission(packageName);
-        }
-
-        @Override
-        public void setBindAppWidgetPermission(String packageName, boolean permission, int userId)
-                throws RemoteException {
-            getImplForUser(userId).setBindAppWidgetPermission(packageName, permission);
-        }
-
-        @Override
-        public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection,
-                int userId) throws RemoteException {
-            getImplForUser(userId).bindRemoteViewsService(appWidgetId, intent, connection);
-        }
-
-        @Override
-        public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
-                List<RemoteViews> updatedViews, int userId) throws RemoteException {
-            return getImplForUser(userId).startListening(host, packageName, hostId, updatedViews);
-        }
-
-        public void onUserRemoved(int userId) {
-            if (userId < 1) return;
-            synchronized (mAppWidgetServices) {
-                AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
-                mAppWidgetServices.remove(userId);
-
-                if (impl == null) {
-                    AppWidgetServiceImpl.getSettingsFile(userId).delete();
-                } else {
-                    impl.onUserRemoved();
-                }
-            }
-        }
-
-        public void onUserStopping(int userId) {
-            if (userId < 1) return;
-            synchronized (mAppWidgetServices) {
-                AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
-                if (impl != null) {
-                    mAppWidgetServices.remove(userId);
-                    impl.onUserStopping();
-                }
-            }
-        }
-
-
-        // support of the widget/backup bridge
-        public List<String> getWidgetParticipants(int userId) {
-            return getImplForUser(userId).getWidgetParticipants();
-        }
-
-        public byte[] getWidgetState(String packageName, int userId) {
-            return getImplForUser(userId).getWidgetState(packageName);
-        }
-
-        public void restoreStarting(int userId) {
-            getImplForUser(userId).restoreStarting();
-        }
-
-        public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
-            getImplForUser(userId).restoreWidgetState(packageName, restoredState);
-        }
-
-        public void restoreFinished(int userId) {
-            getImplForUser(userId).restoreFinished();
-        }
-
-
-        private void checkPermission(int userId) {
-            int realUserId = ActivityManager.handleIncomingUser(
-                    Binder.getCallingPid(),
-                    Binder.getCallingUid(),
-                    userId,
-                    false, /* allowAll */
-                    true, /* requireFull */
-                    this.getClass().getSimpleName(),
-                    this.getClass().getPackage().getName());
-        }
-
-        private AppWidgetServiceImpl getImplForUser(int userId) {
-            checkPermission(userId);
-            boolean sendInitial = false;
-            AppWidgetServiceImpl service;
-            synchronized (mAppWidgetServices) {
-                service = mAppWidgetServices.get(userId);
-                if (service == null) {
-                    Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId
-                            + ", adding");
-                    // TODO: Verify that it's a valid user
-                    service = new AppWidgetServiceImpl(mContext, userId, mSaveStateHandler);
-                    service.systemReady(mSafeMode);
-                    // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
-                    mAppWidgetServices.append(userId, service);
-                    sendInitial = true;
-                }
-            }
-            if (sendInitial) {
-                service.sendInitialBroadcasts();
-            }
-            return service;
-        }
-
-        @Override
-        public int[] getAppWidgetIds(ComponentName provider, int userId) throws RemoteException {
-            return getImplForUser(userId).getAppWidgetIds(provider);
-        }
-
-        @Override
-        public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId, int userId)
-                throws RemoteException {
-            return getImplForUser(userId).getAppWidgetInfo(appWidgetId);
-        }
-
-        @Override
-        public RemoteViews getAppWidgetViews(int appWidgetId, int userId) throws RemoteException {
-            return getImplForUser(userId).getAppWidgetViews(appWidgetId);
-        }
-
-        @Override
-        public void updateAppWidgetOptions(int appWidgetId, Bundle options, int userId) {
-            getImplForUser(userId).updateAppWidgetOptions(appWidgetId, options);
-        }
-
-        @Override
-        public Bundle getAppWidgetOptions(int appWidgetId, int userId) {
-            return getImplForUser(userId).getAppWidgetOptions(appWidgetId);
-        }
-
-        @Override
-        public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int userId)
-                throws RemoteException {
-            return getImplForUser(userId).getInstalledProviders(categoryFilter);
-        }
-
-        @Override
-        public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId, int userId)
-                throws RemoteException {
-            getImplForUser(userId).notifyAppWidgetViewDataChanged(
-                    appWidgetIds, viewId);
-        }
-
-        @Override
-        public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views, int userId)
-                throws RemoteException {
-            getImplForUser(userId).partiallyUpdateAppWidgetIds(
-                    appWidgetIds, views);
-        }
-
-        @Override
-        public void stopListening(int hostId, int userId) throws RemoteException {
-            getImplForUser(userId).stopListening(hostId);
-        }
-
-        @Override
-        public void unbindRemoteViewsService(int appWidgetId, Intent intent, int userId)
-                throws RemoteException {
-            getImplForUser(userId).unbindRemoteViewsService(
-                    appWidgetId, intent);
-        }
-
-        @Override
-        public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views, int userId)
-                throws RemoteException {
-            getImplForUser(userId).updateAppWidgetIds(appWidgetIds, views);
-        }
-
-        @Override
-        public void updateAppWidgetProvider(ComponentName provider, RemoteViews views, int userId)
-                throws RemoteException {
-            getImplForUser(userId).updateAppWidgetProvider(provider, views);
-        }
-
-        @Override
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
-
-            // Dump the state of all the app widget providers
-            synchronized (mAppWidgetServices) {
-                IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-                for (int i = 0; i < mAppWidgetServices.size(); i++) {
-                    pw.println("User: " + mAppWidgetServices.keyAt(i));
-                    ipw.increaseIndent();
-                    AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
-                    service.dump(fd, ipw, args);
-                    ipw.decreaseIndent();
-                }
-            }
-        }
-
-        BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                // Slog.d(TAG, "received " + action);
-                if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                    int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                    if (userId >= 0) {
-                        getImplForUser(userId).sendInitialBroadcasts();
-                    } else {
-                        Slog.w(TAG, "Incorrect user handle supplied in " + intent);
-                    }
-                } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
-                    for (int i = 0; i < mAppWidgetServices.size(); i++) {
-                        AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
-                        service.onConfigurationChanged();
-                    }
-                } else {
-                    int sendingUser = getSendingUserId();
-                    if (sendingUser == UserHandle.USER_ALL) {
-                        for (int i = 0; i < mAppWidgetServices.size(); i++) {
-                            AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
-                            service.onBroadcastReceived(intent);
-                        }
-                    } else {
-                        AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
-                        if (service != null) {
-                            service.onBroadcastReceived(intent);
-                        }
-                    }
-                }
-            }
-        };
-    }
 }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7a67d63..bdaf9ec 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -19,21 +19,25 @@
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.Intent.FilterComparison;
+import android.content.IntentFilter;
+import android.content.IntentSender;
 import android.content.ServiceConnection;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
@@ -44,16 +48,20 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.AttributeSet;
-import android.util.MutableInt;
 import android.util.Pair;
 import android.util.Slog;
-import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.Display;
@@ -61,10 +69,16 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.appwidget.IAppWidgetHost;
+import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.widget.IRemoteViewsAdapterConnection;
 import com.android.internal.widget.IRemoteViewsFactory;
 
+import com.android.server.LocalServices;
+import com.android.server.WidgetBackupProvider;
+import libcore.io.IoUtils;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -79,230 +93,117 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map.Entry;
+import java.util.Map;
 import java.util.Set;
 
-class AppWidgetServiceImpl {
-
-    private static final String KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
-    private static final int KEYGUARD_HOST_ID = 0x4b455947;
+class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBackupProvider {
     private static final String TAG = "AppWidgetServiceImpl";
-    private static final String SETTINGS_FILENAME = "appwidgets.xml";
-    private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
-    private static final int CURRENT_VERSION = 1; // Bump if the stored widgets need to be upgraded.
-    private static final int WIDGET_STATE_VERSION = 1;  // version of backed-up widget state
 
-    private static boolean DBG = true;
-    private static boolean DEBUG_BACKUP = DBG || true;
+    private static boolean DEBUG = false;
 
-    /*
-     * When identifying a Host or Provider based on the calling process, use the uid field. When
-     * identifying a Host or Provider based on a package manager broadcast, use the package given.
-     */
+    private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
+    private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
+    private static final int KEYGUARD_HOST_ID = 0x4b455947;
 
-    static class Provider {
-        int uid;
-        AppWidgetProviderInfo info;
-        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
-        PendingIntent broadcast;
-        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+    private static final String STATE_FILENAME = "appwidgets.xml";
 
-        int tag; // for use while saving state (the index)
+    private static final int MIN_UPDATE_PERIOD = DEBUG ? 0 : 30 * 60 * 1000; // 30 minutes
 
-        // is there an instance of this provider hosted by the given app?
-        public boolean isHostedBy(String packageName) {
-            final int N = instances.size();
-            for (int i = 0; i < N; i++) {
-                AppWidgetId id = instances.get(i);
-                if (packageName.equals(id.host.packageName)) {
-                    return true;
-                }
+    private static final int TAG_UNDEFINED = -1;
+
+    private static final int UNKNOWN_UID = -1;
+
+    private static final int LOADED_PROFILE_ID = -1;
+
+    private static final int DISABLED_PROFILE = -1;
+
+    private static final int UNKNOWN_USER_ID = -10;
+
+    // Bump if the stored widgets need to be upgraded.
+    private static final int CURRENT_VERSION = 1;
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            if (DEBUG) {
+                Slog.i(TAG, "Received broadcast: " + action);
             }
-            return false;
-        }
 
-        @Override
-        public String toString() {
-            return "Provider{" + ((info == null) ? "null" : info.provider)
-                    + (zombie ? " Z" : "")
-                    + '}';
-        }
-    }
-
-    static class Host {
-        int uid;
-        int hostId;
-        String packageName;
-        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
-        IAppWidgetHost callbacks;
-        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
-
-        int tag; // for use while saving state (the index)
-
-        boolean uidMatches(int callingUid) {
-            if (UserHandle.getAppId(callingUid) == Process.myUid()) {
-                // For a host that's in the system process, ignore the user id
-                return UserHandle.isSameApp(this.uid, callingUid);
+            if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+                onConfigurationChanged();
+            } else if (Intent.ACTION_USER_STARTED.equals(action)) {
+                onUserStarted(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        UserHandle.USER_NULL));
+            } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+                onUserStopped(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        UserHandle.USER_NULL));
             } else {
-                return this.uid == callingUid;
+                onPackageBroadcastReceived(intent, intent.getIntExtra(
+                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
             }
         }
+    };
 
-        boolean hostsPackage(String pkg) {
-            final int N = instances.size();
-            for (int i = 0; i < N; i++) {
-                Provider p = instances.get(i).provider;
-                if (p != null && p.info != null && pkg.equals(p.info.provider.getPackageName())) {
-                    return true;
-                }
-            }
-            return false;
-        }
+    // Manages active connections to RemoteViewsServices.
+    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
+            mBoundRemoteViewsServices = new HashMap<>();
 
-        @Override
-        public String toString() {
-            return "Host{" + packageName + ":" + hostId + '}';
-        }
-    }
+    // Manages persistent references to RemoteViewsServices from different App Widgets.
+    private final HashMap<Pair<Integer, FilterComparison>, HashSet<Integer>>
+            mRemoteViewsServicesAppWidgets = new HashMap<>();
 
-    static class AppWidgetId {
-        int appWidgetId;
-        int restoredId;  // tracking & remapping any restored state
-        Provider provider;
-        RemoteViews views;
-        Bundle options;
-        Host host;
+    private final Object mLock = new Object();
 
-        @Override
-        public String toString() {
-            return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}';
-        }
-    }
+    private final ArrayList<Widget> mWidgets = new ArrayList<>();
+    private final ArrayList<Host> mHosts = new ArrayList<>();
+    private final ArrayList<Provider> mProviders = new ArrayList<>();
 
-    AppWidgetId findRestoredWidgetLocked(int restoredId, Host host, Provider p) {
-        if (DEBUG_BACKUP) {
-            Slog.i(TAG, "Find restored widget: id=" + restoredId
-                    + " host=" + host + " provider=" + p);
-        }
+    private final ArraySet<Pair<Integer, String>> mPackagesWithBindWidgetPermission =
+            new ArraySet<>();
 
-        if (p == null || host == null) {
-            return null;
-        }
+    private final SparseIntArray mLoadedUserIds = new SparseIntArray();
 
-        final int N = mAppWidgetIds.size();
-        for (int i = 0; i < N; i++) {
-            AppWidgetId widget = mAppWidgetIds.get(i);
-            if (widget.restoredId == restoredId
-                    && widget.host.hostId == host.hostId
-                    && widget.host.packageName.equals(host.packageName)
-                    && widget.provider.info.provider.equals(p.info.provider)) {
-                if (DEBUG_BACKUP) {
-                    Slog.i(TAG, "   Found at " + i + " : " + widget);
-                }
-                return widget;
-            }
-        }
-        return null;
-    }
+    private final BackupRestoreController mBackupRestoreController;
 
-    /**
-     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
-     * needs to be a static inner class since a reference to the ServiceConnection is held globally
-     * and may lead us to leak AppWidgetService instances (if there were more than one).
-     */
-    static class ServiceConnectionProxy implements ServiceConnection {
-        private final IBinder mConnectionCb;
+    private final Context mContext;
 
-        ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
-            mConnectionCb = connectionCb;
-        }
+    private final IPackageManager mPackageManager;
+    private final AlarmManager mAlarmManager;
+    private final UserManager mUserManager;
 
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
-                    .asInterface(mConnectionCb);
-            try {
-                cb.onServiceConnected(service);
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-
-        public void onServiceDisconnected(ComponentName name) {
-            disconnect();
-        }
-
-        public void disconnect() {
-            final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
-                    .asInterface(mConnectionCb);
-            try {
-                cb.onServiceDisconnected();
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    // Manages active connections to RemoteViewsServices
-    private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
-    // Manages persistent references to RemoteViewsServices from different App Widgets
-    private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
-
-    final Context mContext;
-    final IPackageManager mPm;
-    final AlarmManager mAlarmManager;
-    final ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
-    final int mUserId;
-    final boolean mHasFeature;
-
-    Locale mLocale;
-    int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
-    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
-    final ArrayList<Host> mHosts = new ArrayList<Host>();
-    // set of package names
-    final HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
-    boolean mSafeMode;
-    boolean mStateLoaded;
-    int mMaxWidgetBitmapMemory;
-
-    // Map old (restored) widget IDs to new AppWidgetId instances.  This object is used
-    // as the lock around manipulation of the overall restored-widget state, just as
-    // mAppWidgetIds is used as the lock object around all "live" widget state
-    // manipulations.  Methods that must be called with this lock held are decorated
-    // with the suffix "Lr".
-    //
-    // In cases when both of those locks must be held concurrently, mRestoredWidgetIds
-    // must be acquired *first.*
-    private final SparseArray<AppWidgetId> mRestoredWidgetIds = new SparseArray<AppWidgetId>();
-
-    // We need to make sure to wipe the pre-restore widget state only once for
-    // a given package.  Keep track of what we've done so far here; the list is
-    // cleared at the start of every system restore pass, but preserved through
-    // any install-time restore operations.
-    HashSet<String> mPrunedApps = new HashSet<String>();
+    private final SecurityPolicy mSecurityPolicy;
 
     private final Handler mSaveStateHandler;
+    private final Handler mCallbackHandler;
 
-    // These are for debugging only -- widgets are going missing in some rare instances
-    ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
-    ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+    private Locale mLocale;
 
-    AppWidgetServiceImpl(Context context, int userId, Handler saveStateHandler) {
+    private final SparseIntArray mNextAppWidgetIds = new SparseIntArray();
+
+    private boolean mSafeMode;
+    private int mMaxWidgetBitmapMemory;
+
+    AppWidgetServiceImpl(Context context) {
         mContext = context;
-        mPm = AppGlobals.getPackageManager();
+        mPackageManager = AppGlobals.getPackageManager();
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-        mUserId = userId;
-        mSaveStateHandler = saveStateHandler;
-        mHasFeature = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_APP_WIDGETS);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mSaveStateHandler = BackgroundThread.getHandler();
+        mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
+        mBackupRestoreController = new BackupRestoreController();
+        mSecurityPolicy = new SecurityPolicy();
         computeMaximumWidgetBitmapMemory();
+        registerBroadcastReceiver();
     }
 
-    void computeMaximumWidgetBitmapMemory() {
+    private void computeMaximumWidgetBitmapMemory() {
         WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         Display display = wm.getDefaultDisplay();
         Point size = new Point();
@@ -312,51 +213,99 @@
         mMaxWidgetBitmapMemory = 6 * size.x * size.y;
     }
 
-    public void systemReady(boolean safeMode) {
+    private void registerBroadcastReceiver() {
+        // Register for configuration changes so we can update the names
+        // of the widgets when the locale changes.
+        IntentFilter configFilter = new IntentFilter();
+        configFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                configFilter, null, null);
+
+        // Register for broadcasts about package install, etc., so we can
+        // update the provider list.
+        IntentFilter packageFilter = new IntentFilter();
+        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        packageFilter.addDataScheme("package");
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                packageFilter, null, null);
+
+        // Register for events related to sdcard installation.
+        IntentFilter sdFilter = new IntentFilter();
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                sdFilter, null, null);
+
+        IntentFilter userFilter = new IntentFilter();
+        userFilter.addAction(Intent.ACTION_USER_STARTED);
+        userFilter.addAction(Intent.ACTION_USER_STOPPED);
+        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+                userFilter, null, null);
+    }
+
+    public void setSafeMode(boolean safeMode) {
         mSafeMode = safeMode;
+    }
 
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
+    private void onConfigurationChanged() {
+        if (DEBUG) {
+            Slog.i(TAG, "onConfigurationChanged()");
         }
-    }
 
-    private void log(String msg) {
-        Slog.i(TAG, "u=" + mUserId + ": " + msg);
-    }
-
-    void onConfigurationChanged() {
-        if (DBG) log("Got onConfigurationChanged()");
         Locale revised = Locale.getDefault();
-        if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
+        if (revised == null || mLocale == null || !revised.equals(mLocale)) {
             mLocale = revised;
 
-            synchronized (mAppWidgetIds) {
-                ensureStateLoadedLocked();
+            synchronized (mLock) {
+                SparseIntArray changedGroups = null;
+
                 // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the
                 // list of installed providers and skip providers that we don't need to update.
                 // Also note that remove the provider does not clear the Provider component data.
-                ArrayList<Provider> installedProviders =
-                        new ArrayList<Provider>(mInstalledProviders);
-                HashSet<ComponentName> removedProviders = new HashSet<ComponentName>();
+                ArrayList<Provider> installedProviders = new ArrayList<>(mProviders);
+                HashSet<ProviderId> removedProviders = new HashSet<>();
+
                 int N = installedProviders.size();
                 for (int i = N - 1; i >= 0; i--) {
-                    Provider p = installedProviders.get(i);
-                    ComponentName cn = p.info.provider;
-                    if (!removedProviders.contains(cn)) {
-                        updateProvidersForPackageLocked(cn.getPackageName(), removedProviders);
+                    Provider provider = installedProviders.get(i);
+
+                    ensureGroupStateLoadedLocked(provider.getUserId());
+
+                    if (!removedProviders.contains(provider.id)) {
+                        final boolean changed = updateProvidersForPackageLocked(
+                                provider.id.componentName.getPackageName(),
+                                provider.getUserId(), removedProviders);
+
+                        if (changed) {
+                            if (changedGroups == null) {
+                                changedGroups = new SparseIntArray();
+                            }
+                            final int groupId = mSecurityPolicy.getGroupParent(
+                                    provider.getUserId());
+                            changedGroups.put(groupId, groupId);
+                        }
                     }
                 }
-                saveStateAsync();
+
+                if (changedGroups != null) {
+                    final int groupCount = changedGroups.size();
+                    for (int i = 0; i < groupCount; i++) {
+                        final int groupId = changedGroups.get(i);
+                        saveGroupStateAsync(groupId);
+                    }
+                }
             }
         }
     }
 
-    void onBroadcastReceived(Intent intent) {
-        if (DBG) log("onBroadcast " + intent);
+    private void onPackageBroadcastReceived(Intent intent, int userId) {
         final String action = intent.getAction();
         boolean added = false;
         boolean changed = false;
-        boolean providersModified = false;
+        boolean componentsModified = false;
+
         String pkgList[] = null;
         if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
             pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
@@ -380,555 +329,1177 @@
         if (pkgList == null || pkgList.length == 0) {
             return;
         }
-        if (added || changed) {
-            synchronized (mAppWidgetIds) {
-                ensureStateLoadedLocked();
-                Bundle extras = intent.getExtras();
-                if (changed
-                        || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
-                    for (String pkgName : pkgList) {
-                        // The package was just upgraded
-                        providersModified |= updateProvidersForPackageLocked(pkgName, null);
-                    }
-                } else {
-                    // The package was just added.  Fix up the providers...
-                    for (String pkgName : pkgList) {
-                        providersModified |= addProvidersForPackageLocked(pkgName);
-                    }
-                    // ...and see if these are hosts we've been awaiting
-                    for (String pkg : pkgList) {
-                        try {
-                            int uid = getUidForPackage(pkg);
-                            resolveHostUidLocked(pkg, uid);
-                        } catch (NameNotFoundException e) {
-                            // shouldn't happen; we just installed it!
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            Bundle extras = intent.getExtras();
+
+            if (added || changed) {
+                final boolean newPackageAdded = added && (extras == null
+                        || !extras.getBoolean(Intent.EXTRA_REPLACING, false));
+
+                for (String pkgName : pkgList) {
+                    // Fix up the providers - add/remove/update.
+                    componentsModified |= updateProvidersForPackageLocked(pkgName, userId, null);
+
+                    // ... and see if these are hosts we've been awaiting.
+                    // NOTE: We are backing up and restoring only the owner.
+                    if (newPackageAdded && userId == UserHandle.USER_OWNER) {
+                        final int uid = getUidForPackage(pkgName, userId);
+                        if (uid >= 0 ) {
+                            resolveHostUidLocked(pkgName, uid);
                         }
                     }
                 }
-                saveStateAsync();
-            }
-        } else {
-            Bundle extras = intent.getExtras();
-            if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
-                // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
             } else {
-                synchronized (mAppWidgetIds) {
-                    ensureStateLoadedLocked();
+                // If the package is being updated, we'll receive a PACKAGE_ADDED
+                // shortly, otherwise it is removed permanently.
+                final boolean packageRemovedPermanently = (extras == null
+                        || !extras.getBoolean(Intent.EXTRA_REPLACING, false));
+
+                if (packageRemovedPermanently) {
                     for (String pkgName : pkgList) {
-                        providersModified |= removeProvidersForPackageLocked(pkgName);
-                        saveStateAsync();
+                        componentsModified |= removeHostsAndProvidersForPackageLocked(
+                                pkgName, userId);
                     }
                 }
             }
-        }
 
-        if (providersModified) {
-            // If the set of providers has been modified, notify each active AppWidgetHost
-            synchronized (mAppWidgetIds) {
-                ensureStateLoadedLocked();
-                notifyHostsForProvidersChangedLocked();
+            if (componentsModified) {
+                saveGroupStateAsync(userId);
+
+                // If the set of providers has been modified, notify each active AppWidgetHost
+                scheduleNotifyHostsForProvidersChangedLocked();
             }
         }
     }
 
-    void resolveHostUidLocked(String pkg, int uid) {
+    private void resolveHostUidLocked(String pkg, int uid) {
         final int N = mHosts.size();
         for (int i = 0; i < N; i++) {
-            Host h = mHosts.get(i);
-            if (h.uid == -1 && pkg.equals(h.packageName)) {
-                if (DEBUG_BACKUP) {
-                    Slog.i(TAG, "host " + pkg + ":" + h.hostId + " resolved to uid " + uid);
+            Host host = mHosts.get(i);
+            if (host.id.uid == UNKNOWN_UID && pkg.equals(host.id.packageName)) {
+                if (DEBUG) {
+                    Slog.i(TAG, "host " + host.id + " resolved to uid " + uid);
                 }
-                h.uid = uid;
+                host.id = new HostId(uid, host.id.hostId, host.id.packageName);
+                return;
             }
         }
     }
 
-    private void dumpProvider(Provider p, int index, PrintWriter pw) {
-        AppWidgetProviderInfo info = p.info;
-        pw.print("  ["); pw.print(index); pw.print("] provider ");
-                pw.print(info.provider.flattenToShortString());
-                pw.println(':');
-        pw.print("    min=("); pw.print(info.minWidth);
-                pw.print("x"); pw.print(info.minHeight);
-        pw.print(")   minResize=("); pw.print(info.minResizeWidth);
-                pw.print("x"); pw.print(info.minResizeHeight);
-                pw.print(") updatePeriodMillis=");
-                pw.print(info.updatePeriodMillis);
-                pw.print(" resizeMode=");
-                pw.print(info.resizeMode);
-                pw.print(info.widgetCategory);
-                pw.print(" autoAdvanceViewId=");
-                pw.print(info.autoAdvanceViewId);
-                pw.print(" initialLayout=#");
-                pw.print(Integer.toHexString(info.initialLayout));
-                pw.print(" uid="); pw.print(p.uid);
-                pw.print(" zombie="); pw.println(p.zombie);
-    }
+    private void ensureGroupStateLoadedLocked(int userId) {
+        final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
 
-    private void dumpHost(Host host, int index, PrintWriter pw) {
-        pw.print("  ["); pw.print(index); pw.print("] hostId=");
-                pw.print(host.hostId); pw.print(' ');
-                pw.print(host.packageName); pw.print('/');
-        pw.print(host.uid); pw.println(':');
-        pw.print("    callbacks="); pw.println(host.callbacks);
-        pw.print("    instances.size="); pw.print(host.instances.size());
-                pw.print(" zombie="); pw.println(host.zombie);
-    }
+        // Careful lad, we may have already loaded the state for some
+        // group members, so check before loading and read only the
+        // state for the new member(s).
+        int newMemberCount = 0;
+        final int profileIdCount = profileIds.length;
+        for (int i = 0; i < profileIdCount; i++) {
+            final int profileId = profileIds[i];
+            if (mLoadedUserIds.indexOfKey(profileId) >= 0) {
+                profileIds[i] = LOADED_PROFILE_ID;
+            } else {
+                newMemberCount++;
+            }
+        }
 
-    private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
-        pw.print("  ["); pw.print(index); pw.print("] id=");
-                pw.println(id.appWidgetId);
-        pw.print("    hostId=");
-                pw.print(id.host.hostId); pw.print(' ');
-                pw.print(id.host.packageName); pw.print('/');
-                pw.println(id.host.uid);
-        if (id.provider != null) {
-            pw.print("    provider=");
-                    pw.println(id.provider.info.provider.flattenToShortString());
-        }
-        if (id.host != null) {
-            pw.print("    host.callbacks="); pw.println(id.host.callbacks);
-        }
-        if (id.views != null) {
-            pw.print("    views="); pw.println(id.views);
-        }
-    }
-
-    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
+        if (newMemberCount <= 0) {
             return;
         }
 
-        synchronized (mAppWidgetIds) {
-            int N = mInstalledProviders.size();
+        int newMemberIndex = 0;
+        final int[] newProfileIds = new int[newMemberCount];
+        for (int i = 0; i < profileIdCount; i++) {
+            final int profileId = profileIds[i];
+            if (profileId != LOADED_PROFILE_ID) {
+                mLoadedUserIds.put(profileId, profileId);
+                newProfileIds[newMemberIndex] = profileId;
+                newMemberIndex++;
+            }
+        }
+
+        loadGroupWidgetProvidersLocked(newProfileIds);
+        loadGroupStateLocked(newProfileIds);
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Permission Denial: can't dump from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+        }
+
+        synchronized (mLock) {
+            int N = mProviders.size();
             pw.println("Providers:");
-            for (int i=0; i<N; i++) {
-                dumpProvider(mInstalledProviders.get(i), i, pw);
+            for (int i = 0; i < N; i++) {
+                dumpProvider(mProviders.get(i), i, pw);
             }
 
-            N = mAppWidgetIds.size();
+            N = mWidgets.size();
             pw.println(" ");
-            pw.println("AppWidgetIds:");
-            for (int i=0; i<N; i++) {
-                dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
+            pw.println("Widgets:");
+            for (int i = 0; i < N; i++) {
+                dumpWidget(mWidgets.get(i), i, pw);
             }
 
             N = mHosts.size();
             pw.println(" ");
             pw.println("Hosts:");
-            for (int i=0; i<N; i++) {
+            for (int i = 0; i < N; i++) {
                 dumpHost(mHosts.get(i), i, pw);
             }
 
-            N = mDeletedProviders.size();
-            pw.println(" ");
-            pw.println("Deleted Providers:");
-            for (int i=0; i<N; i++) {
-                dumpProvider(mDeletedProviders.get(i), i, pw);
-            }
 
-            N = mDeletedHosts.size();
+            N = mPackagesWithBindWidgetPermission.size();
             pw.println(" ");
-            pw.println("Deleted Hosts:");
-            for (int i=0; i<N; i++) {
-                dumpHost(mDeletedHosts.get(i), i, pw);
+            pw.println("Grants:");
+            for (int i = 0; i < N; i++) {
+                Pair<Integer, String> grant = mPackagesWithBindWidgetPermission.valueAt(i);
+                dumpGrant(grant, i, pw);
             }
         }
     }
 
-    private void ensureStateLoadedLocked() {
-        if (!mStateLoaded) {
-            if (!mHasFeature) {
-                return;
+    @Override
+    public int[] startListening(IAppWidgetHost callbacks, String callingPackage,
+            int hostId, List<RemoteViews> updatedViews) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "startListening() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access hosts it owns.
+            HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
+            Host host = lookupOrAddHostLocked(id);
+
+            host.callbacks = callbacks;
+
+            updatedViews.clear();
+
+            ArrayList<Widget> instances = host.widgets;
+            int N = instances.size();
+            int[] updatedIds = new int[N];
+            for (int i = 0; i < N; i++) {
+                Widget widget = instances.get(i);
+                updatedIds[i] = widget.appWidgetId;
+                updatedViews.add(cloneIfLocalBinder(widget.views));
             }
-            loadWidgetProviderListLocked();
-            loadStateLocked();
-            mStateLoaded = true;
+
+            return updatedIds;
         }
     }
 
-    public int allocateAppWidgetId(String packageName, int hostId) {
-        int callingUid = enforceSystemOrCallingUid(packageName);
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return -1;
+    @Override
+    public void stopListening(String callingPackage, int hostId) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "stopListening() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access hosts it owns.
+            HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
+            Host host = lookupHostLocked(id);
+
+            if (host != null) {
+                host.callbacks = null;
+                pruneHostLocked(host);
             }
-            ensureStateLoadedLocked();
-            int appWidgetId = mNextAppWidgetId++;
+        }
+    }
 
-            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+    @Override
+    public int allocateAppWidgetId(String callingPackage, int hostId) {
+        final int userId = UserHandle.getCallingUserId();
 
-            AppWidgetId id = new AppWidgetId();
-            id.appWidgetId = appWidgetId;
-            id.host = host;
+        if (DEBUG) {
+            Slog.i(TAG, "allocateAppWidgetId() " + userId);
+        }
 
-            host.instances.add(id);
-            mAppWidgetIds.add(id);
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
 
-            saveStateAsync();
-            if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
-                    + " id=" + appWidgetId);
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            if (mNextAppWidgetIds.indexOfKey(userId) < 0) {
+                mNextAppWidgetIds.put(userId, AppWidgetManager.INVALID_APPWIDGET_ID + 1);
+            }
+
+            final int appWidgetId = incrementAndGetAppWidgetIdLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access hosts it owns.
+            HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
+            Host host = lookupOrAddHostLocked(id);
+
+            Widget widget = new Widget();
+            widget.appWidgetId = appWidgetId;
+            widget.host = host;
+
+            host.widgets.add(widget);
+            mWidgets.add(widget);
+
+            saveGroupStateAsync(userId);
+
+            if (DEBUG) {
+                Slog.i(TAG, "Allocated widget id " + appWidgetId
+                        + " for host " + host.id);
+            }
+
             return appWidgetId;
         }
     }
 
-    public void deleteAppWidgetId(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
+    @Override
+    public void deleteAppWidgetId(String callingPackage, int appWidgetId) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "deleteAppWidgetId() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access widgets it hosts or provides.
+            Widget widget = lookupWidgetLocked(appWidgetId,
+                    Binder.getCallingUid(), callingPackage);
+
+            if (widget == null) {
                 return;
             }
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null) {
-                deleteAppWidgetLocked(id);
-                saveStateAsync();
+
+            deleteAppWidgetLocked(widget);
+
+            saveGroupStateAsync(userId);
+
+            if (DEBUG) {
+                Slog.i(TAG, "Deleted widget id " + appWidgetId
+                        + " for host " + widget.host.id);
             }
         }
     }
 
-    public void deleteHost(int hostId) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return;
-            }
-            ensureStateLoadedLocked();
-            int callingUid = Binder.getCallingUid();
-            Host host = lookupHostLocked(callingUid, hostId);
-            if (host != null) {
-                deleteHostLocked(host);
-                saveStateAsync();
-            }
+    @Override
+    public boolean hasBindAppWidgetPermission(String packageName, int grantId) {
+        if (DEBUG) {
+            Slog.i(TAG, "hasBindAppWidgetPermission() " + UserHandle.getCallingUserId());
         }
-    }
 
-    public void deleteAllHosts() {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return;
-            }
-            ensureStateLoadedLocked();
-            int callingUid = Binder.getCallingUid();
-            final int N = mHosts.size();
-            boolean changed = false;
-            for (int i = N - 1; i >= 0; i--) {
-                Host host = mHosts.get(i);
-                if (host.uidMatches(callingUid)) {
-                    deleteHostLocked(host);
-                    changed = true;
-                }
-            }
-            if (changed) {
-                saveStateAsync();
-            }
-        }
-    }
+        // A special permission is required for managing white listing.
+        mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName);
 
-    void deleteHostLocked(Host host) {
-        if (DBG) log("Deleting host " + host);
-        final int N = host.instances.size();
-        for (int i = N - 1; i >= 0; i--) {
-            AppWidgetId id = host.instances.get(i);
-            deleteAppWidgetLocked(id);
-        }
-        host.instances.clear();
-        mHosts.remove(host);
-        mDeletedHosts.add(host);
-        // it's gone or going away, abruptly drop the callback connection
-        host.callbacks = null;
-    }
+        synchronized (mLock) {
+            // The grants are stored in user state wich gets the grant.
+            ensureGroupStateLoadedLocked(grantId);
 
-    void deleteAppWidgetLocked(AppWidgetId id) {
-        // We first unbind all services that are bound to this id
-        unbindAppWidgetRemoteViewsServicesLocked(id);
-
-        Host host = id.host;
-        host.instances.remove(id);
-        pruneHostLocked(host);
-
-        mAppWidgetIds.remove(id);
-
-        Provider p = id.provider;
-        if (p != null) {
-            p.instances.remove(id);
-            if (!p.zombie) {
-                // send the broacast saying that this appWidgetId has been deleted
-                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
-                intent.setComponent(p.info.provider);
-                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
-                mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
-                if (p.instances.size() == 0) {
-                    // cancel the future updates
-                    cancelBroadcasts(p);
-
-                    // send the broacast saying that the provider is not in use any more
-                    intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
-                    intent.setComponent(p.info.provider);
-                    mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
-                }
-            }
-        }
-    }
-
-    void cancelBroadcasts(Provider p) {
-        if (DBG) log("cancelBroadcasts for " + p);
-        if (p.broadcast != null) {
-            mAlarmManager.cancel(p.broadcast);
-            long token = Binder.clearCallingIdentity();
-            try {
-                p.broadcast.cancel();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            p.broadcast = null;
-        }
-    }
-
-    private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
-        if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
-                + " provider=" + provider);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mAppWidgetIds) {
-                if (!mHasFeature) {
-                    return;
-                }
-                options = cloneIfLocalBinder(options);
-                ensureStateLoadedLocked();
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-                if (id == null) {
-                    throw new IllegalArgumentException("bad appWidgetId");
-                }
-                if (id.provider != null) {
-                    throw new IllegalArgumentException("appWidgetId " + appWidgetId
-                            + " already bound to " + id.provider.info.provider);
-                }
-                Provider p = lookupProviderLocked(provider);
-                if (p == null) {
-                    throw new IllegalArgumentException("not a appwidget provider: " + provider);
-                }
-                if (p.zombie) {
-                    throw new IllegalArgumentException("can't bind to a 3rd party provider in"
-                            + " safe mode: " + provider);
-                }
-
-                id.provider = p;
-                if (options == null) {
-                    options = new Bundle();
-                }
-                id.options = options;
-
-                // We need to provide a default value for the widget category if it is not specified
-                if (!options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
-                    options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
-                            AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
-                }
-
-                p.instances.add(id);
-                int instancesSize = p.instances.size();
-                if (instancesSize == 1) {
-                    // tell the provider that it's ready
-                    sendEnableIntentLocked(p);
-                }
-
-                // send an update now -- We need this update now, and just for this appWidgetId.
-                // It's less critical when the next one happens, so when we schedule the next one,
-                // we add updatePeriodMillis to its start time. That time will have some slop,
-                // but that's okay.
-                sendUpdateIntentLocked(p, new int[] { appWidgetId });
-
-                // schedule the future updates
-                registerForBroadcastsLocked(p, getAppWidgetIds(p));
-                saveStateAsync();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET,
-            "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
-        bindAppWidgetIdImpl(appWidgetId, provider, options);
-    }
-
-    public boolean bindAppWidgetIdIfAllowed(
-            String packageName, int appWidgetId, ComponentName provider, Bundle options) {
-        if (!mHasFeature) {
-            return false;
-        }
-        try {
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
-        } catch (SecurityException se) {
-            if (!callerHasBindAppWidgetPermission(packageName)) {
+            final int packageUid = getUidForPackage(packageName, grantId);
+            if (packageUid < 0) {
                 return false;
             }
+
+            Pair<Integer, String> packageId = Pair.create(grantId, packageName);
+            return mPackagesWithBindWidgetPermission.contains(packageId);
         }
-        bindAppWidgetIdImpl(appWidgetId, provider, options);
+    }
+
+    @Override
+    public void setBindAppWidgetPermission(String packageName, int grantId,
+            boolean grantPermission) {
+        if (DEBUG) {
+            Slog.i(TAG, "setBindAppWidgetPermission() " + UserHandle.getCallingUserId());
+        }
+
+        // A special permission is required for managing white listing.
+        mSecurityPolicy.enforceModifyAppWidgetBindPermissions(packageName);
+
+        synchronized (mLock) {
+            // The grants are stored in user state wich gets the grant.
+            ensureGroupStateLoadedLocked(grantId);
+
+            final int packageUid = getUidForPackage(packageName, grantId);
+            if (packageUid < 0) {
+                return;
+            }
+
+            Pair<Integer, String> packageId = Pair.create(grantId, packageName);
+            if (grantPermission) {
+                mPackagesWithBindWidgetPermission.add(packageId);
+            } else {
+                mPackagesWithBindWidgetPermission.remove(packageId);
+            }
+
+            saveGroupStateAsync(grantId);
+        }
+    }
+
+    @Override
+    public IntentSender createAppWidgetConfigIntentSender(String callingPackage, Intent intent) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "createAppWidgetConfigIntentSender() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        // The only allowed action is the one to start the configure activity.
+        if (!AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {
+            throw new IllegalArgumentException("Only allowed action is "
+                    + AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
+        }
+
+        // Verify that widget id is provided.
+        final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+                AppWidgetManager.INVALID_APPWIDGET_ID);
+        if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+            throw new IllegalArgumentException("Widget id required");
+        }
+
+        // Make sure a component name is provided.
+        ComponentName component = intent.getComponent();
+        if (component == null) {
+            throw new IllegalArgumentException("Component name required");
+        }
+
+        // Verify the user handle.
+        UserHandle userHandle = intent.getParcelableExtra(
+                AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
+        if (userHandle != null) {
+            // Remove the profile extra as the receiver already runs under this
+            // user and this information is of no use to this receiver.
+            intent.removeExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
+
+            // If the user handle is not the caller, check if it is an enabled
+            // profile for which the package is white-listed.
+            final int profileId = userHandle.getIdentifier();
+            if (profileId != userId) {
+                // Make sure the passed user handle is a profile in the group.
+                final int[] profileIds = mSecurityPolicy.resolveCallerEnabledGroupProfiles(
+                        new int[]{profileId});
+                if (profileIds.length <= 0) {
+                    // The profile is not in the group or not enabled, done.
+                    return null;
+                }
+
+                // Make sure the provider is white-listed.
+                if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+                        component.getPackageName(), profileId)) {
+                    throw new IllegalArgumentException("Cannot access provider "
+                            + component + " in user " + profileIds);
+                }
+            }
+        } else {
+            // If a profile is not specified use the caller user id.
+            userHandle = new UserHandle(userId);
+        }
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access widgets it hosts or provides.
+            Widget widget = lookupWidgetLocked(appWidgetId,
+                    Binder.getCallingUid(), callingPackage);
+
+            if (widget == null) {
+                throw new IllegalArgumentException("Bad widget id " + appWidgetId);
+            }
+
+            Provider provider = widget.provider;
+            if (provider == null) {
+                throw new IllegalArgumentException("Widget not bound " + appWidgetId);
+            }
+
+            // Make sure the component refers to the provider config activity.
+            if (!component.equals(provider.info.configure)
+                    || !provider.info.getProfile().equals(userHandle)) {
+                throw new IllegalArgumentException("No component" + component
+                        + " for user " + userHandle.getIdentifier());
+            }
+
+            // All right, create the sender.
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return PendingIntent.getActivityAsUser(
+                        mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
+                                | PendingIntent.FLAG_CANCEL_CURRENT, null, userHandle)
+                        .getIntentSender();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @Override
+    public boolean bindAppWidgetId(String callingPackage, int appWidgetId,
+            int providerProfileId, ComponentName providerComponent, Bundle options) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "bindAppWidgetId() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        // Check that if a cross-profile binding is attempted, it is allowed.
+        final int[] profileIds = mSecurityPolicy.resolveCallerEnabledGroupProfiles(
+                new int[] {providerProfileId});
+        if (profileIds.length <= 0) {
+            return false;
+        }
+
+        // If the provider is not under the calling user, make sure this
+        // provider is white listed for access from the parent.
+        if (!mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+                providerComponent.getPackageName(), providerProfileId)) {
+            return false;
+        }
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // A special permission or white listing is required to bind widgets.
+            if (!mSecurityPolicy.hasCallerBindPermissionOrBindWhiteListedLocked(
+                    callingPackage)) {
+                return false;
+            }
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access widgets it hosts or provides.
+            Widget widget = lookupWidgetLocked(appWidgetId,
+                    Binder.getCallingUid(), callingPackage);
+
+            if (widget == null) {
+                Slog.e(TAG, "Bad widget id " + appWidgetId);
+                return false;
+            }
+
+            if (widget.provider != null) {
+                Slog.e(TAG, "Widget id " + appWidgetId
+                        + " already bound to: " + widget.provider.id);
+                return false;
+            }
+
+            final int providerUid = getUidForPackage(providerComponent.getPackageName(),
+                    providerProfileId);
+            if (providerUid < 0) {
+                Slog.e(TAG, "Package " + providerComponent.getPackageName() + " not installed "
+                        + " for profile " + providerProfileId);
+                return false;
+            }
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the provider is in the already vetted user profile.
+            ProviderId providerId = new ProviderId(providerUid, providerComponent);
+            Provider provider = lookupProviderLocked(providerId);
+
+            if (provider == null) {
+                Slog.e(TAG, "No widget provider " + providerComponent + " for profile "
+                        + providerProfileId);
+                return false;
+            }
+
+            if (provider.zombie) {
+                Slog.e(TAG, "Can't bind to a 3rd party provider in"
+                        + " safe mode " + provider);
+                return false;
+            }
+
+            widget.provider = provider;
+            widget.options = (options != null) ? cloneIfLocalBinder(options) : new Bundle();
+
+            // We need to provide a default value for the widget category if it is not specified
+            if (!widget.options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
+                widget.options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                        AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+            }
+
+            provider.widgets.add(widget);
+
+            final int widgetCount = provider.widgets.size();
+            if (widgetCount == 1) {
+                // Tell the provider that it's ready.
+                sendEnableIntentLocked(provider);
+            }
+
+            // Send an update now -- We need this update now, and just for this appWidgetId.
+            // It's less critical when the next one happens, so when we schedule the next one,
+            // we add updatePeriodMillis to its start time. That time will have some slop,
+            // but that's okay.
+            sendUpdateIntentLocked(provider, new int[] {appWidgetId});
+
+            // Schedule the future updates.
+            registerForBroadcastsLocked(provider, getWidgetIds(provider.widgets));
+
+            saveGroupStateAsync(userId);
+
+            if (DEBUG) {
+                Slog.i(TAG, "Bound widget " + appWidgetId + " to provider " + provider.id);
+            }
+        }
+
         return true;
     }
 
-    private boolean callerHasBindAppWidgetPermission(String packageName) {
-        int callingUid = Binder.getCallingUid();
-        try {
-            if (!UserHandle.isSameApp(callingUid, getUidForPackage(packageName))) {
-                return false;
-            }
-        } catch (Exception e) {
-            return false;
+    @Override
+    public int[] getAppWidgetIds(ComponentName componentName) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "getAppWidgetIds() " + userId);
         }
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            return mPackagesWithBindWidgetPermission.contains(packageName);
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(componentName.getPackageName());
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can access only its providers.
+            ProviderId providerId = new ProviderId(Binder.getCallingUid(), componentName);
+            Provider provider = lookupProviderLocked(providerId);
+
+            if (provider != null) {
+                return getWidgetIds(provider.widgets);
+            }
+
+            return new int[0];
         }
     }
 
-    public boolean hasBindAppWidgetPermission(String packageName) {
-        if (!mHasFeature) {
-            return false;
-        }
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
-                "hasBindAppWidgetPermission packageName=" + packageName);
+    @Override
+    public int[] getAppWidgetIdsForHost(String callingPackage, int hostId) {
+        final int userId = UserHandle.getCallingUserId();
 
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            return mPackagesWithBindWidgetPermission.contains(packageName);
+        if (DEBUG) {
+            Slog.i(TAG, "getAppWidgetIdsForHost() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access its hosts.
+            HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
+            Host host = lookupHostLocked(id);
+
+            if (host != null) {
+                return getWidgetIds(host.widgets);
+            }
+
+            return new int[0];
         }
     }
 
-    public void setBindAppWidgetPermission(String packageName, boolean permission) {
-        if (!mHasFeature) {
-            return;
+    @Override
+    public void bindRemoteViewsService(String callingPackage, int appWidgetId,
+            Intent intent, IBinder callbacks) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "bindRemoteViewsService() " + userId);
         }
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
-                "setBindAppWidgetPermission packageName=" + packageName);
 
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            if (permission) {
-                mPackagesWithBindWidgetPermission.add(packageName);
-            } else {
-                mPackagesWithBindWidgetPermission.remove(packageName);
-            }
-            saveStateAsync();
-        }
-    }
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
 
-    // Binds to a specific RemoteViewsService
-    public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return;
-            }
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id == null) {
-                throw new IllegalArgumentException("bad appWidgetId");
-            }
-            final ComponentName componentName = intent.getComponent();
-            try {
-                final ServiceInfo si = mPm.getServiceInfo(componentName,
-                        PackageManager.GET_PERMISSIONS, mUserId);
-                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
-                    throw new SecurityException("Selected service does not require "
-                            + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
-                }
-            } catch (RemoteException e) {
-                throw new IllegalArgumentException("Unknown component " + componentName);
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access widgets it hosts or provides.
+            Widget widget = lookupWidgetLocked(appWidgetId,
+                    Binder.getCallingUid(), callingPackage);
+
+            if (widget == null) {
+                throw new IllegalArgumentException("Bad widget id");
             }
 
-            // Ensure that the service specified by the passed intent belongs to the same package
-            // as provides the passed widget id.
-            String widgetIdPackage = id.provider.info.provider.getPackageName();
+            // Make sure the widget has a provider.
+            if (widget.provider == null) {
+                throw new IllegalArgumentException("No provider for widget "
+                        + appWidgetId);
+            }
+
+            ComponentName componentName = intent.getComponent();
+
+            // Ensure that the service belongs to the same package as the provider.
+            // But this is not enough as they may be under different users - see below...
+            String providerPackage = widget.provider.id.componentName.getPackageName();
             String servicePackage = componentName.getPackageName();
-            if (!servicePackage.equals(widgetIdPackage)) {
-                throw new SecurityException("Specified intent doesn't belong to the same package"
-                        + " as the provided AppWidget id");
+            if (!servicePackage.equals(providerPackage)) {
+                throw new SecurityException("The taget service not in the same package"
+                        + " as the widget provider");
             }
 
-            // If there is already a connection made for this service intent, then disconnect from
-            // that first. (This does not allow multiple connections to the same service under
-            // the same key)
-            ServiceConnectionProxy conn = null;
+            // Make sure this service exists under the same user as the provider and
+            // requires a permission which allows only the system to bind to it.
+            mSecurityPolicy.enforceServiceExistsAndRequiresBindRemoteViewsPermission(
+                    componentName, widget.provider.getUserId());
+
+            // Good to go - the service pakcage is correct, it exists for the correct
+            // user, and requires the bind permission.
+
+            // If there is already a connection made for this service intent, then
+            // disconnect from that first. (This does not allow multiple connections
+            // to the same service under the same key).
+            ServiceConnectionProxy connection = null;
             FilterComparison fc = new FilterComparison(intent);
             Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
+
             if (mBoundRemoteViewsServices.containsKey(key)) {
-                conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
+                connection = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
+                connection.disconnect();
+                unbindService(connection);
                 mBoundRemoteViewsServices.remove(key);
             }
 
-            int userId = UserHandle.getUserId(id.provider.uid);
-            if (userId != mUserId) {
-                Slog.w(TAG, "AppWidgetServiceImpl of user " + mUserId
-                        + " binding to provider on user " + userId);
-            }
             // Bind to the RemoteViewsService (which will trigger a callback to the
             // RemoteViewsAdapter.onServiceConnected())
-            final long token = Binder.clearCallingIdentity();
-            try {
-                conn = new ServiceConnectionProxy(key, connection);
-                mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
-                        new UserHandle(userId));
-                mBoundRemoteViewsServices.put(key, conn);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            connection = new ServiceConnectionProxy(callbacks);
+            bindService(intent, connection, widget.provider.info.getProfile());
+            mBoundRemoteViewsServices.put(key, connection);
 
-            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
-            // when we can call back to the RemoteViewsService later to destroy associated
-            // factories.
-            incrementAppWidgetServiceRefCount(appWidgetId, fc);
+            // Add it to the mapping of RemoteViewsService to appWidgetIds so that we
+            // can determine when we can call back to the RemoteViewsService later to
+            // destroy associated factories.
+            Pair<Integer, FilterComparison> serviceId = Pair.create(widget.provider.id.uid, fc);
+            incrementAppWidgetServiceRefCount(appWidgetId, serviceId);
         }
     }
 
-    // Unbinds from a specific RemoteViewsService
-    public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return;
-            }
-            ensureStateLoadedLocked();
+    @Override
+    public void unbindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "unbindRemoteViewsService() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
             // Unbind from the RemoteViewsService (which will trigger a callback to the bound
             // RemoteViewsAdapter)
-            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
-                    intent));
+            Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
+                    new FilterComparison(intent));
             if (mBoundRemoteViewsServices.containsKey(key)) {
                 // We don't need to use the appWidgetId until after we are sure there is something
                 // to unbind. Note that this may mask certain issues with apps calling unbind()
                 // more than necessary.
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-                if (id == null) {
-                    throw new IllegalArgumentException("bad appWidgetId");
+
+                // NOTE: The lookup is enforcing security across users by making
+                // sure the caller can only access widgets it hosts or provides.
+                Widget widget = lookupWidgetLocked(appWidgetId,
+                        Binder.getCallingUid(), callingPackage);
+
+                if (widget == null) {
+                    throw new IllegalArgumentException("Bad widget id " + appWidgetId);
                 }
 
-                ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
-                        .get(key);
-                conn.disconnect();
-                mContext.unbindService(conn);
+                ServiceConnectionProxy connection = (ServiceConnectionProxy)
+                        mBoundRemoteViewsServices.get(key);
+                connection.disconnect();
+                mContext.unbindService(connection);
                 mBoundRemoteViewsServices.remove(key);
             }
         }
     }
 
+    @Override
+    public void deleteHost(String callingPackage, int hostId) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "deleteHost() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access hosts in its uid and package.
+            HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
+            Host host = lookupHostLocked(id);
+
+            if (host == null) {
+                return;
+            }
+
+            deleteHostLocked(host);
+
+            saveGroupStateAsync(userId);
+
+            if (DEBUG) {
+                Slog.i(TAG, "Deleted host " + host.id);
+            }
+        }
+    }
+
+    @Override
+    public void deleteAllHosts() {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "deleteAllHosts() " + userId);
+        }
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            boolean changed = false;
+
+            final int N = mHosts.size();
+            for (int i = N - 1; i >= 0; i--) {
+                Host host = mHosts.get(i);
+
+                // Delete only hosts in the calling uid.
+                if (host.id.uid == Binder.getCallingUid()) {
+                    deleteHostLocked(host);
+                    changed = true;
+
+                    if (DEBUG) {
+                        Slog.i(TAG, "Deleted host " + host.id);
+                    }
+                }
+            }
+
+            if (changed) {
+                saveGroupStateAsync(userId);
+            }
+        }
+    }
+
+    @Override
+    public AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "getAppWidgetInfo() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access widgets it hosts or provides.
+            Widget widget = lookupWidgetLocked(appWidgetId,
+                    Binder.getCallingUid(), callingPackage);
+
+            if (widget != null && widget.provider != null && !widget.provider.zombie) {
+                return cloneIfLocalBinder(widget.provider.info);
+            }
+
+            return null;
+        }
+    }
+
+    @Override
+    public RemoteViews getAppWidgetViews(String callingPackage, int appWidgetId) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "getAppWidgetViews() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access widgets it hosts or provides.
+            Widget widget = lookupWidgetLocked(appWidgetId,
+                    Binder.getCallingUid(), callingPackage);
+
+            if (widget != null) {
+                return cloneIfLocalBinder(widget.views);
+            }
+
+            return null;
+        }
+    }
+
+    @Override
+    public void updateAppWidgetOptions(String callingPackage, int appWidgetId, Bundle options) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "updateAppWidgetOptions() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access widgets it hosts or provides.
+            Widget widget = lookupWidgetLocked(appWidgetId,
+                    Binder.getCallingUid(), callingPackage);
+
+            if (widget == null) {
+                return;
+            }
+
+            // Merge the options.
+            widget.options.putAll(options);
+
+            // Send the broacast to notify the provider that options changed.
+            sendOptionsChangedIntentLocked(widget);
+
+            saveGroupStateAsync(userId);
+        }
+    }
+
+    @Override
+    public Bundle getAppWidgetOptions(String callingPackage, int appWidgetId) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "getAppWidgetOptions() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can only access widgets it hosts or provides.
+            Widget widget = lookupWidgetLocked(appWidgetId,
+                    Binder.getCallingUid(), callingPackage);
+
+            if (widget != null && widget.options != null) {
+                return cloneIfLocalBinder(widget.options);
+            }
+
+            return Bundle.EMPTY;
+        }
+    }
+
+    @Override
+    public void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
+            RemoteViews views) {
+        if (DEBUG) {
+            Slog.i(TAG, "updateAppWidgetIds() " + UserHandle.getCallingUserId());
+        }
+
+        updateAppWidgetIds(callingPackage, appWidgetIds, views, false);
+    }
+
+    @Override
+    public void partiallyUpdateAppWidgetIds(String callingPackage, int[] appWidgetIds,
+            RemoteViews views) {
+        if (DEBUG) {
+            Slog.i(TAG, "partiallyUpdateAppWidgetIds() " + UserHandle.getCallingUserId());
+        }
+
+        updateAppWidgetIds(callingPackage, appWidgetIds, views, true);
+    }
+
+    @Override
+    public void notifyAppWidgetViewDataChanged(String callingPackage, int[] appWidgetIds,
+            int viewId) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "notifyAppWidgetViewDataChanged() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        if (appWidgetIds == null || appWidgetIds.length == 0) {
+            return;
+        }
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            final int N = appWidgetIds.length;
+            for (int i = 0; i < N; i++) {
+                final int appWidgetId = appWidgetIds[i];
+
+                // NOTE: The lookup is enforcing security across users by making
+                // sure the caller can only access widgets it hosts or provides.
+                Widget widget = lookupWidgetLocked(appWidgetId,
+                        Binder.getCallingUid(), callingPackage);
+
+                if (widget != null) {
+                    scheduleNotifyAppWidgetViewDataChanged(widget, viewId);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void updateAppWidgetProvider(ComponentName componentName, RemoteViews views) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "updateAppWidgetProvider() " + userId);
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(componentName.getPackageName());
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            // NOTE: The lookup is enforcing security across users by making
+            // sure the caller can access only its providers.
+            ProviderId providerId = new ProviderId(Binder.getCallingUid(), componentName);
+            Provider provider = lookupProviderLocked(providerId);
+
+            if (provider == null) {
+                Slog.w(TAG, "Provider doesn't exist " + providerId);
+                return;
+            }
+
+            ArrayList<Widget> instances = provider.widgets;
+            final int N = instances.size();
+            for (int i = 0; i < N; i++) {
+                Widget widget = instances.get(i);
+                updateAppWidgetInstanceLocked(widget, views, false);
+            }
+        }
+    }
+
+    @Override
+    public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int[] profileIds) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (DEBUG) {
+            Slog.i(TAG, "getInstalledProvidersForProfiles() " + userId);
+        }
+
+        if (profileIds != null && profileIds.length > 0) {
+            // Make sure the profile ids are children of the calling user.
+            profileIds = mSecurityPolicy.resolveCallerEnabledGroupProfiles(profileIds);
+        } else {
+            profileIds = new int[] {userId};
+        }
+
+        if (profileIds.length == 0) {
+            return null;
+        }
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            ArrayList<AppWidgetProviderInfo> result = new ArrayList<>();
+
+            final int providerCount = mProviders.size();
+            for (int i = 0; i < providerCount; i++) {
+                Provider provider = mProviders.get(i);
+                AppWidgetProviderInfo info = provider.info;
+
+                // Ignore an invalid provider or one not matching the filter.
+                if (provider.zombie || (info.widgetCategory & categoryFilter) == 0) {
+                    continue;
+                }
+
+                // Add providers only for the requested profiles ...
+                final int providerProfileId = info.getProfile().getIdentifier();
+                final int profileCount = profileIds.length;
+                for (int j = 0; j < profileCount; j++) {
+                    final int profileId = profileIds[j];
+                    if (providerProfileId == profileId) {
+                        // ... that are white-listed by the profile manager.
+                        if (mSecurityPolicy.isProviderInCallerOrInProfileAndWhitelListed(
+                                provider.id.componentName.getPackageName(), providerProfileId)) {
+                            result.add(cloneIfLocalBinder(info));
+                        }
+                        break;
+                    }
+                }
+            }
+
+            return result;
+        }
+    }
+
+    private void updateAppWidgetIds(String callingPackage, int[] appWidgetIds,
+            RemoteViews views, boolean partially) {
+        final int userId = UserHandle.getCallingUserId();
+
+        if (appWidgetIds == null || appWidgetIds.length == 0) {
+            return;
+        }
+
+        // Make sure the package runs under the caller uid.
+        mSecurityPolicy.enforceCallFromPackage(callingPackage);
+
+        final int bitmapMemoryUsage = (views != null) ? views.estimateMemoryUsage() : 0;
+        if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
+            throw new IllegalArgumentException("RemoteViews for widget update exceeds"
+                    + " maximum bitmap memory usage (used: " + bitmapMemoryUsage
+                    + ", max: " + mMaxWidgetBitmapMemory + ")");
+        }
+
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            final int N = appWidgetIds.length;
+            for (int i = 0; i < N; i++) {
+                final int appWidgetId = appWidgetIds[i];
+
+                // NOTE: The lookup is enforcing security across users by making
+                // sure the caller can only access widgets it hosts or provides.
+                Widget widget = lookupWidgetLocked(appWidgetId,
+                        Binder.getCallingUid(), callingPackage);
+
+                if (widget != null) {
+                    updateAppWidgetInstanceLocked(widget, views, partially);
+                }
+            }
+        }
+    }
+
+    private int incrementAndGetAppWidgetIdLocked(int userId) {
+        final int appWidgetId = peekNextAppWidgetIdLocked(userId) + 1;
+        mNextAppWidgetIds.put(userId, appWidgetId);
+        return appWidgetId;
+    }
+
+    private void setMinAppWidgetIdLocked(int userId, int minWidgetId) {
+        final int nextAppWidgetId = peekNextAppWidgetIdLocked(userId);
+        if (nextAppWidgetId < minWidgetId) {
+            mNextAppWidgetIds.put(userId, minWidgetId);
+        }
+    }
+
+    private int peekNextAppWidgetIdLocked(int userId) {
+        if (mNextAppWidgetIds.indexOfKey(userId) < 0) {
+            return AppWidgetManager.INVALID_APPWIDGET_ID + 1;
+        } else {
+            return mNextAppWidgetIds.get(userId);
+        }
+    }
+
+    private Host lookupOrAddHostLocked(HostId id) {
+        Host host = lookupHostLocked(id);
+        if (host != null) {
+            return host;
+        }
+
+        host = new Host();
+        host.id = id;
+        mHosts.add(host);
+
+        return host;
+    }
+
+    private void deleteHostLocked(Host host) {
+        final int N = host.widgets.size();
+        for (int i = N - 1; i >= 0; i--) {
+            Widget widget = host.widgets.remove(i);
+            deleteAppWidgetLocked(widget);
+        }
+        mHosts.remove(host);
+
+        // it's gone or going away, abruptly drop the callback connection
+        host.callbacks = null;
+    }
+
+    private void deleteAppWidgetLocked(Widget widget) {
+        // We first unbind all services that are bound to this id
+        unbindAppWidgetRemoteViewsServicesLocked(widget);
+
+        Host host = widget.host;
+        host.widgets.remove(widget);
+        pruneHostLocked(host);
+
+        mWidgets.remove(widget);
+
+        Provider provider = widget.provider;
+        if (provider != null) {
+            provider.widgets.remove(widget);
+            if (!provider.zombie) {
+                // send the broacast saying that this appWidgetId has been deleted
+                sendDeletedIntentLocked(widget);
+
+                if (provider.widgets.isEmpty()) {
+                    // cancel the future updates
+                    cancelBroadcasts(provider);
+
+                    // send the broacast saying that the provider is not in use any more
+                    sendDisabledIntentLocked(provider);
+                }
+            }
+        }
+    }
+
+    private void cancelBroadcasts(Provider provider) {
+        if (DEBUG) {
+            Slog.i(TAG, "cancelBroadcasts() for " + provider);
+        }
+        if (provider.broadcast != null) {
+            mAlarmManager.cancel(provider.broadcast);
+            long token = Binder.clearCallingIdentity();
+            try {
+                provider.broadcast.cancel();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            provider.broadcast = null;
+        }
+    }
+
     // Unbinds from a RemoteViewsService when we delete an app widget
-    private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
-        int appWidgetId = id.appWidgetId;
+    private void unbindAppWidgetRemoteViewsServicesLocked(Widget widget) {
+        int appWidgetId = widget.appWidgetId;
         // Unbind all connections to Services bound to this AppWidgetId
         Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
                 .iterator();
         while (it.hasNext()) {
             final Pair<Integer, Intent.FilterComparison> key = it.next();
-            if (key.first.intValue() == appWidgetId) {
-                final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
-                        .get(key);
+            if (key.first == appWidgetId) {
+                final ServiceConnectionProxy conn = (ServiceConnectionProxy)
+                        mBoundRemoteViewsServices.get(key);
                 conn.disconnect();
                 mContext.unbindService(conn);
                 it.remove();
@@ -937,337 +1508,132 @@
 
         // Check if we need to destroy any services (if no other app widgets are
         // referencing the same service)
-        decrementAppWidgetServiceRefCount(id);
+        decrementAppWidgetServiceRefCount(widget);
     }
 
     // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
-    private void destroyRemoteViewsService(final Intent intent, AppWidgetId id) {
+    private void destroyRemoteViewsService(final Intent intent, Widget widget) {
         final ServiceConnection conn = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
                 final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
                 try {
                     cb.onDestroy(intent);
-                } catch (RemoteException e) {
-                    e.printStackTrace();
-                } catch (RuntimeException e) {
-                    e.printStackTrace();
+                } catch (RemoteException re) {
+                    Slog.e(TAG, "Error calling remove view factory", re);
                 }
                 mContext.unbindService(this);
             }
 
             @Override
-            public void onServiceDisconnected(android.content.ComponentName name) {
+            public void onServiceDisconnected(ComponentName name) {
                 // Do nothing
             }
         };
 
-        int userId = UserHandle.getUserId(id.provider.uid);
         // Bind to the service and remove the static intent->factory mapping in the
         // RemoteViewsService.
         final long token = Binder.clearCallingIdentity();
         try {
             mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
-                    new UserHandle(userId));
+                    widget.provider.info.getProfile());
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
     // Adds to the ref-count for a given RemoteViewsService intent
-    private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
+    private void incrementAppWidgetServiceRefCount(int appWidgetId,
+            Pair<Integer, FilterComparison> serviceId) {
         HashSet<Integer> appWidgetIds = null;
-        if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
-            appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
+        if (mRemoteViewsServicesAppWidgets.containsKey(serviceId)) {
+            appWidgetIds = mRemoteViewsServicesAppWidgets.get(serviceId);
         } else {
-            appWidgetIds = new HashSet<Integer>();
-            mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
+            appWidgetIds = new HashSet<>();
+            mRemoteViewsServicesAppWidgets.put(serviceId, appWidgetIds);
         }
         appWidgetIds.add(appWidgetId);
     }
 
     // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
     // the ref-count reaches zero.
-    private void decrementAppWidgetServiceRefCount(AppWidgetId id) {
-        Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
+    private void decrementAppWidgetServiceRefCount(Widget widget) {
+        Iterator<Pair<Integer, FilterComparison>> it = mRemoteViewsServicesAppWidgets
+                .keySet().iterator();
         while (it.hasNext()) {
-            final FilterComparison key = it.next();
+            final Pair<Integer, FilterComparison> key = it.next();
             final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
-            if (ids.remove(id.appWidgetId)) {
+            if (ids.remove(widget.appWidgetId)) {
                 // If we have removed the last app widget referencing this service, then we
                 // should destroy it and remove it from this set
                 if (ids.isEmpty()) {
-                    destroyRemoteViewsService(key.getIntent(), id);
+                    destroyRemoteViewsService(key.second.getIntent(), widget);
                     it.remove();
                 }
             }
         }
     }
 
-    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return null;
-            }
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null && id.provider != null && !id.provider.zombie) {
-                return cloneIfLocalBinder(id.provider.info);
-            }
-            return null;
-        }
+    private void saveGroupStateAsync(int groupId) {
+        mSaveStateHandler.post(new SaveStateRunnable(groupId));
     }
 
-    public RemoteViews getAppWidgetViews(int appWidgetId) {
-        if (DBG) log("getAppWidgetViews id=" + appWidgetId);
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return null;
-            }
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null) {
-                return cloneIfLocalBinder(id.views);
-            }
-            if (DBG) log("   couldn't find appwidgetid");
-            return null;
-        }
-    }
+    private void updateAppWidgetInstanceLocked(Widget widget, RemoteViews views,
+            boolean isPartialUpdate) {
+        if (widget != null && widget.provider != null
+                && !widget.provider.zombie && !widget.host.zombie) {
 
-    public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return new ArrayList<AppWidgetProviderInfo>(0);
-            }
-            ensureStateLoadedLocked();
-            final int N = mInstalledProviders.size();
-            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
-            for (int i = 0; i < N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (!p.zombie && (p.info.widgetCategory & categoryFilter) != 0) {
-                    result.add(cloneIfLocalBinder(p.info));
-                }
-            }
-            return result;
-        }
-    }
-
-    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
-        if (!mHasFeature) {
-            return;
-        }
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (DBG) log("updateAppWidgetIds views: " + views);
-        int bitmapMemoryUsage = 0;
-        if (views != null) {
-            bitmapMemoryUsage = views.estimateMemoryUsage();
-        }
-        if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
-            throw new IllegalArgumentException("RemoteViews for widget update exceeds maximum" +
-                    " bitmap memory usage (used: " + bitmapMemoryUsage + ", max: " +
-                    mMaxWidgetBitmapMemory + ") The total memory cannot exceed that required to" +
-                    " fill the device's screen once.");
-        }
-
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i = 0; i < N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                updateAppWidgetInstanceLocked(id, views);
-            }
-        }
-    }
-
-    private void saveStateAsync() {
-        mSaveStateHandler.post(mSaveStateRunnable);
-    }
-
-    private final Runnable mSaveStateRunnable = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (mAppWidgetIds) {
-                ensureStateLoadedLocked();
-                saveStateLocked();
-            }
-        }
-    };
-
-    public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return;
-            }
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-
-            if (id == null) {
-                return;
-            }
-
-            Provider p = id.provider;
-            // Merge the options
-            id.options.putAll(cloneIfLocalBinder(options));
-
-            // send the broacast saying that this appWidgetId has been deleted
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
-            intent.setComponent(p.info.provider);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, id.options);
-            mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
-            saveStateAsync();
-        }
-    }
-
-    public Bundle getAppWidgetOptions(int appWidgetId) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return Bundle.EMPTY;
-            }
-            ensureStateLoadedLocked();
-            AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
-            if (id != null && id.options != null) {
-                return cloneIfLocalBinder(id.options);
-            } else {
-                return Bundle.EMPTY;
-            }
-        }
-    }
-
-    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
-        if (!mHasFeature) {
-            return;
-        }
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i = 0; i < N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                if (id == null) {
-                    Slog.w(TAG, "widget id " + appWidgetIds[i] + " not found!");
-                } else if (id.views != null) {
-                    // Only trigger a partial update for a widget if it has received a full update
-                    updateAppWidgetInstanceLocked(id, views, true);
-                }
-            }
-        }
-    }
-
-    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
-        if (!mHasFeature) {
-            return;
-        }
-        if (appWidgetIds == null) {
-            return;
-        }
-        if (appWidgetIds.length == 0) {
-            return;
-        }
-        final int N = appWidgetIds.length;
-
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            for (int i = 0; i < N; i++) {
-                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
-                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
-            }
-        }
-    }
-
-    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
-        if (!mHasFeature) {
-            return;
-        }
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Provider p = lookupProviderLocked(provider);
-            if (p == null) {
-                Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
-                return;
-            }
-            ArrayList<AppWidgetId> instances = p.instances;
-            final int callingUid = Binder.getCallingUid();
-            final int N = instances.size();
-            for (int i = 0; i < N; i++) {
-                AppWidgetId id = instances.get(i);
-                if (canAccessAppWidgetId(id, callingUid)) {
-                    updateAppWidgetInstanceLocked(id, views);
-                }
-            }
-        }
-    }
-
-    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
-        updateAppWidgetInstanceLocked(id, views, false);
-    }
-
-    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
-        // allow for stale appWidgetIds and other badness
-        // lookup also checks that the calling process can access the appWidgetId
-        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
-        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-
-            if (!isPartialUpdate) {
-                // For a full update we replace the RemoteViews completely.
-                id.views = views;
-            } else {
+            if (isPartialUpdate && widget.views != null) {
                 // For a partial update, we merge the new RemoteViews with the old.
-                id.views.mergeRemoteViews(views);
+                widget.views.mergeRemoteViews(views);
+            } else {
+                // For a full update we replace the RemoteViews completely.
+                widget.views = views;
             }
 
-            // is anyone listening?
-            if (id.host.callbacks != null) {
-                try {
-                    // the lock is held, but this is a oneway call
-                    id.host.callbacks.updateAppWidget(id.appWidgetId, views, mUserId);
-                } catch (RemoteException e) {
-                    // It failed; remove the callback. No need to prune because
-                    // we know that this host is still referenced by this instance.
-                    id.host.callbacks = null;
-                }
-            }
+            scheduleNotifyUpdateAppWidgetLocked(widget);
         }
     }
 
-    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
-        // allow for stale appWidgetIds and other badness
-        // lookup also checks that the calling process can access the appWidgetId
-        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
-        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-            // is anyone listening?
-            if (id.host.callbacks != null) {
-                try {
-                    // the lock is held, but this is a oneway call
-                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId, mUserId);
-                } catch (RemoteException e) {
-                    // It failed; remove the callback. No need to prune because
-                    // we know that this host is still referenced by this instance.
-                    id.host.callbacks = null;
-                }
-            }
+    private void scheduleNotifyAppWidgetViewDataChanged(Widget widget, int viewId) {
+        if (widget == null || widget.host == null || widget.host.zombie
+                || widget.host.callbacks == null || widget.provider == null
+                || widget.provider.zombie) {
+            return;
+        }
 
-            // If the host is unavailable, then we call the associated
-            // RemoteViewsFactory.onDataSetChanged() directly
-            if (id.host.callbacks == null) {
-                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
-                for (FilterComparison key : keys) {
-                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
-                        Intent intent = key.getIntent();
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = widget.host;
+        args.arg2 = widget.host.callbacks;
+        args.argi1 = widget.appWidgetId;
+        args.argi2 = viewId;
 
-                        final ServiceConnection conn = new ServiceConnection() {
+        mCallbackHandler.obtainMessage(
+                CallbackHandler.MSG_NOTIFY_VIEW_DATA_CHANGED,
+                args).sendToTarget();
+    }
+
+
+    private void handleNotifyAppWidgetViewDataChanged(Host host, IAppWidgetHost callbacks,
+            int appWidgetId, int viewId) {
+        try {
+            callbacks.viewDataChanged(appWidgetId, viewId);
+        } catch (RemoteException re) {
+            // It failed; remove the callback. No need to prune because
+            // we know that this host is still referenced by this instance.
+            callbacks = null;
+        }
+
+        // If the host is unavailable, then we call the associated
+        // RemoteViewsFactory.onDataSetChanged() directly
+        synchronized (mLock) {
+            if (callbacks == null) {
+                host.callbacks = null;
+
+                Set<Pair<Integer, FilterComparison>> keys = mRemoteViewsServicesAppWidgets.keySet();
+                for (Pair<Integer, FilterComparison> key : keys) {
+                    if (mRemoteViewsServicesAppWidgets.get(key).contains(appWidgetId)) {
+                        final ServiceConnection connection = new ServiceConnection() {
                             @Override
                             public void onServiceConnected(ComponentName name, IBinder service) {
                                 IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
@@ -1275,9 +1641,7 @@
                                 try {
                                     cb.onDataSetChangedAsync();
                                 } catch (RemoteException e) {
-                                    e.printStackTrace();
-                                } catch (RuntimeException e) {
-                                    e.printStackTrace();
+                                    Slog.e(TAG, "Error calling onDataSetChangedAsync()", e);
                                 }
                                 mContext.unbindService(this);
                             }
@@ -1288,40 +1652,124 @@
                             }
                         };
 
-                        int userId = UserHandle.getUserId(id.provider.uid);
+                        final int userId = UserHandle.getUserId(key.first);
+                        Intent intent = key.second.getIntent();
+
                         // Bind to the service and call onDataSetChanged()
-                        final long token = Binder.clearCallingIdentity();
-                        try {
-                            mContext.bindServiceAsUser(intent, conn, Context.BIND_AUTO_CREATE,
-                                    new UserHandle(userId));
-                        } finally {
-                            Binder.restoreCallingIdentity(token);
-                        }
+                        bindService(intent, connection, new UserHandle(userId));
                     }
                 }
             }
         }
     }
 
-    private boolean isLocalBinder() {
+    private void scheduleNotifyUpdateAppWidgetLocked(Widget widget) {
+        if (widget == null || widget.provider == null || widget.provider.zombie
+                || widget.host.callbacks == null || widget.host.zombie) {
+            return;
+        }
+
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = widget.host;
+        args.arg2 = widget.host.callbacks;
+        args.arg3 = widget.views;
+        args.argi1 = widget.appWidgetId;
+
+        mCallbackHandler.obtainMessage(
+                CallbackHandler.MSG_NOTIFY_UPDATE_APP_WIDGET,
+                args).sendToTarget();
+    }
+
+    private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
+            int appWidgetId, RemoteViews views) {
+        try {
+            callbacks.updateAppWidget(appWidgetId, views);
+        } catch (RemoteException re) {
+            synchronized (mLock) {
+                Slog.e(TAG, "Widget host dead: " + host.id, re);
+                host.callbacks = null;
+            }
+        }
+    }
+
+    private void scheduleNotifyProviderChangedLocked(Widget widget) {
+        if (widget == null || widget.provider == null || widget.provider.zombie
+                || widget.host.callbacks == null || widget.host.zombie) {
+            return;
+        }
+
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = widget.host;
+        args.arg2 = widget.host.callbacks;
+        args.arg3 = widget.provider.info;
+        args.argi1 = widget.appWidgetId;
+
+        mCallbackHandler.obtainMessage(
+                CallbackHandler.MSG_NOTIFY_PROVIDER_CHANGED,
+                args).sendToTarget();
+    }
+
+    private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks,
+            int appWidgetId, AppWidgetProviderInfo info) {
+        try {
+            callbacks.providerChanged(appWidgetId, info);
+        } catch (RemoteException re) {
+            synchronized (mLock){
+                Slog.e(TAG, "Widget host dead: " + host.id, re);
+                host.callbacks = null;
+            }
+        }
+    }
+
+    private void scheduleNotifyHostsForProvidersChangedLocked() {
+        final int N = mHosts.size();
+        for (int i = N - 1; i >= 0; i--) {
+            Host host = mHosts.get(i);
+
+            if (host == null || host.zombie || host.callbacks == null) {
+                continue;
+            }
+
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = host;
+            args.arg2 = host.callbacks;
+
+            mCallbackHandler.obtainMessage(
+                    CallbackHandler.MSG_NOTIFY_PROVIDERS_CHANGED,
+                    args).sendToTarget();
+        }
+    }
+
+    private void handleNotifyProvidersChanged(Host host, IAppWidgetHost callbacks) {
+        try {
+            callbacks.providersChanged();
+        } catch (RemoteException re) {
+            synchronized (mLock) {
+                Slog.e(TAG, "Widget host dead: " + host.id, re);
+                host.callbacks = null;
+            }
+        }
+    }
+
+    private static boolean isLocalBinder() {
         return Process.myPid() == Binder.getCallingPid();
     }
 
-    private RemoteViews cloneIfLocalBinder(RemoteViews rv) {
+    private static RemoteViews cloneIfLocalBinder(RemoteViews rv) {
         if (isLocalBinder() && rv != null) {
             return rv.clone();
         }
         return rv;
     }
 
-    private AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
+    private static AppWidgetProviderInfo cloneIfLocalBinder(AppWidgetProviderInfo info) {
         if (isLocalBinder() && info != null) {
             return info.clone();
         }
         return info;
     }
 
-    private Bundle cloneIfLocalBinder(Bundle bundle) {
+    private static Bundle cloneIfLocalBinder(Bundle bundle) {
         // Note: this is only a shallow copy. For now this will be fine, but it could be problematic
         // if we start adding objects to the options. Further, it would only be an issue if keyguard
         // used such options.
@@ -1331,832 +1779,326 @@
         return bundle;
     }
 
-    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
-            List<RemoteViews> updatedViews) {
-        if (!mHasFeature) {
-            return new int[0];
-        }
-        int callingUid = enforceCallingUid(packageName);
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
-            host.callbacks = callbacks;
-
-            updatedViews.clear();
-
-            ArrayList<AppWidgetId> instances = host.instances;
-            int N = instances.size();
-            int[] updatedIds = new int[N];
-            for (int i = 0; i < N; i++) {
-                AppWidgetId id = instances.get(i);
-                updatedIds[i] = id.appWidgetId;
-                updatedViews.add(cloneIfLocalBinder(id.views));
-            }
-            return updatedIds;
-        }
-    }
-
-    public void stopListening(int hostId) {
-        synchronized (mAppWidgetIds) {
-            if (!mHasFeature) {
-                return;
-            }
-            ensureStateLoadedLocked();
-            Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
-            if (host != null) {
-                host.callbacks = null;
-                pruneHostLocked(host);
-            }
-        }
-    }
-
-    boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
-        if (id.host.uidMatches(callingUid)) {
-            // Apps hosting the AppWidget have access to it.
-            return true;
-        }
-        if (id.provider != null && id.provider.uid == callingUid) {
-            // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
-            return true;
-        }
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
-            // Apps that can bind have access to all appWidgetIds.
-            return true;
-        }
-        // Nobody else can access it.
-        return false;
-    }
-
-    AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
-        int callingUid = Binder.getCallingUid();
-        final int N = mAppWidgetIds.size();
+    private Widget lookupWidgetLocked(int appWidgetId, int uid, String packageName) {
+        final int N = mWidgets.size();
         for (int i = 0; i < N; i++) {
-            AppWidgetId id = mAppWidgetIds.get(i);
-            if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
-                return id;
+            Widget widget = mWidgets.get(i);
+            if (widget.appWidgetId == appWidgetId
+                    && mSecurityPolicy.canAccessAppWidget(widget, uid, packageName)) {
+                return widget;
             }
         }
         return null;
     }
 
-    Provider lookupProviderLocked(ComponentName provider) {
-        return lookupProviderLocked(provider, mInstalledProviders);
-    }
-
-    Provider lookupProviderLocked(ComponentName provider, ArrayList<Provider> installedProviders) {
-        final int N = installedProviders.size();
+    private Provider lookupProviderLocked(ProviderId id) {
+        final int N = mProviders.size();
         for (int i = 0; i < N; i++) {
-            Provider p = installedProviders.get(i);
-            if (p.info.provider.equals(provider)) {
-                return p;
+            Provider provider = mProviders.get(i);
+            if (provider.id.equals(id)) {
+                return provider;
             }
         }
         return null;
     }
 
-    Host lookupHostLocked(int uid, int hostId) {
+    private Host lookupHostLocked(HostId hostId) {
         final int N = mHosts.size();
         for (int i = 0; i < N; i++) {
-            Host h = mHosts.get(i);
-            if (h.uidMatches(uid) && h.hostId == hostId) {
-                return h;
+            Host host = mHosts.get(i);
+            if (host.id.equals(hostId)) {
+                return host;
             }
         }
         return null;
     }
 
-    Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
-        final int N = mHosts.size();
-        for (int i = 0; i < N; i++) {
-            Host h = mHosts.get(i);
-            if (h.hostId == hostId && h.packageName.equals(packageName)) {
-                return h;
+    private void pruneHostLocked(Host host) {
+        if (host.widgets.size() == 0 && host.callbacks == null) {
+            if (DEBUG) {
+                Slog.i(TAG, "Pruning host " + host.id);
             }
-        }
-        Host host = new Host();
-        host.packageName = packageName;
-        host.uid = uid;
-        host.hostId = hostId;
-        mHosts.add(host);
-        return host;
-    }
-
-    void pruneHostLocked(Host host) {
-        if (host.instances.size() == 0 && host.callbacks == null) {
-            if (DBG) log("Pruning host " + host);
             mHosts.remove(host);
         }
     }
 
-    void loadWidgetProviderListLocked() {
+    private void loadGroupWidgetProvidersLocked(int[] profileIds) {
+        List<ResolveInfo> allReceivers = null;
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        try {
-            List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
-                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    PackageManager.GET_META_DATA, mUserId);
 
-            final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
-            for (int i = 0; i < N; i++) {
-                ResolveInfo ri = broadcastReceivers.get(i);
-                addProviderLocked(ri);
+        final int profileCount = profileIds.length;
+        for (int i = 0; i < profileCount; i++) {
+            final int profileId = profileIds[i];
+
+            List<ResolveInfo> receivers = queryIntentReceivers(intent, profileId);
+            if (receivers != null && !receivers.isEmpty()) {
+                if (allReceivers == null) {
+                    allReceivers = new ArrayList<>();
+                }
+                allReceivers.addAll(receivers);
             }
-        } catch (RemoteException re) {
-            // Shouldn't happen, local call
+        }
+
+        final int N = (allReceivers == null) ? 0 : allReceivers.size();
+        for (int i = 0; i < N; i++) {
+            ResolveInfo receiver = allReceivers.get(i);
+            addProviderLocked(receiver);
         }
     }
 
-    boolean addProviderLocked(ResolveInfo ri) {
+    private boolean addProviderLocked(ResolveInfo ri) {
         if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
             return false;
         }
+
         if (!ri.activityInfo.isEnabled()) {
             return false;
         }
-        Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
-                ri.activityInfo.name), ri);
-        if (p != null) {
+
+        ComponentName componentName = new ComponentName(ri.activityInfo.packageName,
+                ri.activityInfo.name);
+        ProviderId providerId = new ProviderId(ri.activityInfo.applicationInfo.uid, componentName);
+
+        Provider provider = parseProviderInfoXml(providerId, ri);
+        if (provider != null) {
             // we might have an inactive entry for this provider already due to
             // a preceding restore operation.  if so, fix it up in place; otherwise
             // just add this new one.
-            Provider existing = lookupProviderLocked(p.info.provider);
+            Provider existing = lookupProviderLocked(providerId);
+
+            // If the provider was not found it may be because it was restored and
+            // we did not know its UID so let us find if there is such one.
+            if (existing == null) {
+                providerId = new ProviderId(UNKNOWN_UID, componentName);
+                existing = lookupProviderLocked(providerId);
+            }
+
             if (existing != null) {
                 if (existing.zombie && !mSafeMode) {
                     // it's a placeholder that was set up during an app restore
                     existing.zombie = false;
-                    existing.info = p.info; // the real one filled out from the ResolveInfo
-                    existing.uid = p.uid;
-                    if (DEBUG_BACKUP) {
+                    existing.info = provider.info; // the real one filled out from the ResolveInfo
+                    if (DEBUG) {
                         Slog.i(TAG, "Provider placeholder now reified: " + existing);
                     }
                 }
             } else {
-                mInstalledProviders.add(p);
+                mProviders.add(provider);
             }
             return true;
-        } else {
-            return false;
         }
+
+        return false;
     }
 
-    void removeProviderLocked(int index, Provider p) {
-        int N = p.instances.size();
+    private void deleteProviderLocked(Provider provider) {
+        int N = provider.widgets.size();
         for (int i = 0; i < N; i++) {
-            AppWidgetId id = p.instances.get(i);
+            Widget widget = provider.widgets.remove(i);
             // Call back with empty RemoteViews
-            updateAppWidgetInstanceLocked(id, null);
-            // Stop telling the host about updates for this from now on
-            cancelBroadcasts(p);
+            updateAppWidgetInstanceLocked(widget, null, false);
             // clear out references to this appWidgetId
-            id.host.instances.remove(id);
-            mAppWidgetIds.remove(id);
-            id.provider = null;
-            pruneHostLocked(id.host);
-            id.host = null;
+            widget.host.widgets.remove(widget);
+            mWidgets.remove(widget);
+            widget.provider = null;
+            pruneHostLocked(widget.host);
+            widget.host = null;
         }
-        p.instances.clear();
-        mInstalledProviders.remove(index);
-        mDeletedProviders.add(p);
+        mProviders.remove(provider);
+
         // no need to send the DISABLE broadcast, since the receiver is gone anyway
-        cancelBroadcasts(p);
+        cancelBroadcasts(provider);
     }
 
-    void sendEnableIntentLocked(Provider p) {
+    private void sendEnableIntentLocked(Provider p) {
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
         intent.setComponent(p.info.provider);
-        mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
+        sendBroadcastAsUser(intent, p.info.getProfile());
     }
 
-    void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
-        if (appWidgetIds != null && appWidgetIds.length > 0) {
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
-            intent.setComponent(p.info.provider);
-            mContext.sendBroadcastAsUser(intent, new UserHandle(mUserId));
-        }
+    private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+        intent.setComponent(provider.info.provider);
+        sendBroadcastAsUser(intent, provider.info.getProfile());
     }
 
-    void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
-        if (p.info.updatePeriodMillis > 0) {
+    private void sendDeletedIntentLocked(Widget widget) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
+        intent.setComponent(widget.provider.info.provider);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId);
+        sendBroadcastAsUser(intent, widget.provider.info.getProfile());
+    }
+
+    private void sendDisabledIntentLocked(Provider provider) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
+        intent.setComponent(provider.info.provider);
+        sendBroadcastAsUser(intent, provider.info.getProfile());
+    }
+
+    public void sendOptionsChangedIntentLocked(Widget widget) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
+        intent.setComponent(widget.provider.info.provider);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, widget.options);
+        sendBroadcastAsUser(intent, widget.provider.info.getProfile());
+    }
+
+    private void registerForBroadcastsLocked(Provider provider, int[] appWidgetIds) {
+        if (provider.info.updatePeriodMillis > 0) {
             // if this is the first instance, set the alarm. otherwise,
             // rely on the fact that we've already set it and that
             // PendingIntent.getBroadcast will update the extras.
-            boolean alreadyRegistered = p.broadcast != null;
+            boolean alreadyRegistered = provider.broadcast != null;
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
-            intent.setComponent(p.info.provider);
+            intent.setComponent(provider.info.provider);
             long token = Binder.clearCallingIdentity();
             try {
-                p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
-                        PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
+                provider.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
+                        PendingIntent.FLAG_UPDATE_CURRENT, provider.info.getProfile());
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
             if (!alreadyRegistered) {
-                long period = p.info.updatePeriodMillis;
+                long period = provider.info.updatePeriodMillis;
                 if (period < MIN_UPDATE_PERIOD) {
                     period = MIN_UPDATE_PERIOD;
                 }
-                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
-                        .elapsedRealtime()
-                        + period, period, p.broadcast);
+                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                        SystemClock.elapsedRealtime() + period, period, provider.broadcast);
             }
         }
     }
 
-    static int[] getAppWidgetIds(Provider p) {
-        int instancesSize = p.instances.size();
+    private static int[] getWidgetIds(ArrayList<Widget> widgets) {
+        int instancesSize = widgets.size();
         int appWidgetIds[] = new int[instancesSize];
         for (int i = 0; i < instancesSize; i++) {
-            appWidgetIds[i] = p.instances.get(i).appWidgetId;
+            appWidgetIds[i] = widgets.get(i).appWidgetId;
         }
         return appWidgetIds;
     }
 
-    public int[] getAppWidgetIds(ComponentName provider) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            Provider p = lookupProviderLocked(provider);
-            if (p != null && Binder.getCallingUid() == p.uid) {
-                return getAppWidgetIds(p);
-            } else {
-                return new int[0];
-            }
+    private static void dumpProvider(Provider provider, int index, PrintWriter pw) {
+        AppWidgetProviderInfo info = provider.info;
+        pw.print("  ["); pw.print(index); pw.print("] provider ");
+        pw.println(provider.id);
+        pw.print("    min=("); pw.print(info.minWidth);
+        pw.print("x"); pw.print(info.minHeight);
+        pw.print(")   minResize=("); pw.print(info.minResizeWidth);
+        pw.print("x"); pw.print(info.minResizeHeight);
+        pw.print(") updatePeriodMillis=");
+        pw.print(info.updatePeriodMillis);
+        pw.print(" resizeMode=");
+        pw.print(info.resizeMode);
+        pw.print(info.widgetCategory);
+        pw.print(" autoAdvanceViewId=");
+        pw.print(info.autoAdvanceViewId);
+        pw.print(" initialLayout=#");
+        pw.print(Integer.toHexString(info.initialLayout));
+        pw.print(" initialKeyguardLayout=#");
+        pw.print(Integer.toHexString(info.initialKeyguardLayout));
+        pw.print(" zombie="); pw.println(provider.zombie);
+    }
+
+    private static void dumpHost(Host host, int index, PrintWriter pw) {
+        pw.print("  ["); pw.print(index); pw.print("] hostId=");
+        pw.println(host.id);
+        pw.print("    callbacks="); pw.println(host.callbacks);
+        pw.print("    widgets.size="); pw.print(host.widgets.size());
+        pw.print(" zombie="); pw.println(host.zombie);
+    }
+
+    private static void dumpGrant(Pair<Integer, String> grant, int index, PrintWriter pw) {
+        pw.print("  ["); pw.print(index); pw.print(']');
+        pw.print(" user="); pw.print(grant.first);
+        pw.print(" package="); pw.println(grant.second);
+    }
+
+    private static void dumpWidget(Widget widget, int index, PrintWriter pw) {
+        pw.print("  ["); pw.print(index); pw.print("] id=");
+        pw.println(widget.appWidgetId);
+        pw.print("    host=");
+        pw.println(widget.host.id);
+        if (widget.provider != null) {
+            pw.print("    provider="); pw.println(widget.provider.id);
+        }
+        if (widget.host != null) {
+            pw.print("    host.callbacks="); pw.println(widget.host.callbacks);
+        }
+        if (widget.views != null) {
+            pw.print("    views="); pw.println(widget.views);
         }
     }
 
-    static int[] getAppWidgetIds(Host h) {
-        int instancesSize = h.instances.size();
-        int appWidgetIds[] = new int[instancesSize];
-        for (int i = 0; i < instancesSize; i++) {
-            appWidgetIds[i] = h.instances.get(i).appWidgetId;
-        }
-        return appWidgetIds;
-    }
-
-    public int[] getAppWidgetIdsForHost(int hostId) {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            int callingUid = Binder.getCallingUid();
-            Host host = lookupHostLocked(callingUid, hostId);
-            if (host != null) {
-                return getAppWidgetIds(host);
-            } else {
-                return new int[0];
-            }
-        }
-    }
-
-    public List<String> getWidgetParticipants() {
-        HashSet<String> packages = new HashSet<String>();
-        synchronized (mAppWidgetIds) {
-            final int N = mAppWidgetIds.size();
-            for (int i = 0; i < N; i++) {
-                final AppWidgetId id = mAppWidgetIds.get(i);
-                packages.add(id.host.packageName);
-                packages.add(id.provider.info.provider.getPackageName());
-            }
-        }
-        return new ArrayList<String>(packages);
-    }
-
-    private void serializeProvider(XmlSerializer out, Provider p) throws IOException {
+    private static void serializeProvider(XmlSerializer out, Provider p) throws IOException {
         out.startTag(null, "p");
         out.attribute(null, "pkg", p.info.provider.getPackageName());
         out.attribute(null, "cl", p.info.provider.getClassName());
+        out.attribute(null, "tag", Integer.toHexString(p.tag));
         out.endTag(null, "p");
     }
 
-    private void serializeHost(XmlSerializer out, Host host) throws IOException {
+    private static void serializeHost(XmlSerializer out, Host host) throws IOException {
         out.startTag(null, "h");
-        out.attribute(null, "pkg", host.packageName);
-        out.attribute(null, "id", Integer.toHexString(host.hostId));
+        out.attribute(null, "pkg", host.id.packageName);
+        out.attribute(null, "id", Integer.toHexString(host.id.hostId));
+        out.attribute(null, "tag", Integer.toHexString(host.tag));
         out.endTag(null, "h");
     }
 
-    private void serializeAppWidgetId(XmlSerializer out, AppWidgetId id) throws IOException {
+    private static void serializeAppWidget(XmlSerializer out, Widget widget) throws IOException {
         out.startTag(null, "g");
-        out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
-        out.attribute(null, "rid", Integer.toHexString(id.restoredId));
-        out.attribute(null, "h", Integer.toHexString(id.host.tag));
-        if (id.provider != null) {
-            out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+        out.attribute(null, "id", Integer.toHexString(widget.appWidgetId));
+        out.attribute(null, "rid", Integer.toHexString(widget.restoredId));
+        out.attribute(null, "h", Integer.toHexString(widget.host.tag));
+        if (widget.provider != null) {
+            out.attribute(null, "p", Integer.toHexString(widget.provider.tag));
         }
-        if (id.options != null) {
-            out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
+        if (widget.options != null) {
+            out.attribute(null, "min_width", Integer.toHexString(widget.options.getInt(
                     AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
-            out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
+            out.attribute(null, "min_height", Integer.toHexString(widget.options.getInt(
                     AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
-            out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
+            out.attribute(null, "max_width", Integer.toHexString(widget.options.getInt(
                     AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
-            out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
+            out.attribute(null, "max_height", Integer.toHexString(widget.options.getInt(
                     AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
-            out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
+            out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt(
                     AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
         }
         out.endTag(null, "g");
     }
 
-    private Bundle parseWidgetIdOptions(XmlPullParser parser) {
-        Bundle options = new Bundle();
-        String minWidthString = parser.getAttributeValue(null, "min_width");
-        if (minWidthString != null) {
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
-                    Integer.parseInt(minWidthString, 16));
-        }
-        String minHeightString = parser.getAttributeValue(null, "min_height");
-        if (minHeightString != null) {
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
-                    Integer.parseInt(minHeightString, 16));
-        }
-        String maxWidthString = parser.getAttributeValue(null, "max_width");
-        if (maxWidthString != null) {
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
-                    Integer.parseInt(maxWidthString, 16));
-        }
-        String maxHeightString = parser.getAttributeValue(null, "max_height");
-        if (maxHeightString != null) {
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
-                    Integer.parseInt(maxHeightString, 16));
-        }
-        String categoryString = parser.getAttributeValue(null, "host_category");
-        if (categoryString != null) {
-            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
-                    Integer.parseInt(categoryString, 16));
-        }
-        return options;
+    @Override
+    public List<String> getWidgetParticipants(int userId) {
+        return mBackupRestoreController.getWidgetParticipants(userId);
     }
 
-    // Does this package either host or provide any active widgets?
-    private boolean packageNeedsWidgetBackupLocked(String packageName) {
-        int N = mAppWidgetIds.size();
-        for (int i = 0; i < N; i++) {
-            AppWidgetId id = mAppWidgetIds.get(i);
-            if (packageName.equals(id.host.packageName)) {
-                // this package is hosting widgets, so it knows widget IDs
-                return true;
-            }
-            Provider p = id.provider;
-            if (p != null && packageName.equals(p.info.provider.getPackageName())) {
-                // someone is hosting this app's widgets, so it knows widget IDs
-                return true;
-            }
-        }
-        return false;
+    @Override
+    public byte[] getWidgetState(String packageName, int userId) {
+        return mBackupRestoreController.getWidgetState(packageName, userId);
     }
 
-    // build the widget-state blob that we save for the app during backup.
-    public byte[] getWidgetState(String backupTarget) {
-        if (!mHasFeature) {
-            return null;
-        }
-
-        ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        synchronized (mAppWidgetIds) {
-            // Preflight: if this app neither hosts nor provides any live widgets
-            // we have no work to do.
-            if (!packageNeedsWidgetBackupLocked(backupTarget)) {
-                return null;
-            }
-
-            try {
-                XmlSerializer out = new FastXmlSerializer();
-                out.setOutput(stream, "utf-8");
-                out.startDocument(null, true);
-                out.startTag(null, "ws");      // widget state
-                out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION));
-                out.attribute(null, "pkg", backupTarget);
-
-                // Remember all the providers that are currently hosted or published
-                // by this package: that is, all of the entities related to this app
-                // which will need to be told about id remapping.
-                int N = mInstalledProviders.size();
-                int index = 0;
-                for (int i = 0; i < N; i++) {
-                    Provider p = mInstalledProviders.get(i);
-                    if (p.instances.size() > 0) {
-                        if (backupTarget.equals(p.info.provider.getPackageName())
-                                || p.isHostedBy(backupTarget)) {
-                            serializeProvider(out, p);
-                            p.tag = index++;
-                        }
-                    }
-                }
-
-                N = mHosts.size();
-                index = 0;
-                for (int i = 0; i < N; i++) {
-                    Host host = mHosts.get(i);
-                    if (backupTarget.equals(host.packageName)
-                            || host.hostsPackage(backupTarget)) {
-                        serializeHost(out, host);
-                        host.tag = index++;
-                    }
-                }
-
-                // All widget instances involving this package,
-                // either as host or as provider
-                N = mAppWidgetIds.size();
-                for (int i = 0; i < N; i++) {
-                    AppWidgetId id = mAppWidgetIds.get(i);
-                    if (backupTarget.equals(id.host.packageName)
-                            || (id.provider != null && backupTarget.equals(
-                                    id.provider.info.provider.getPackageName()))) {
-                        serializeAppWidgetId(out, id);
-                    }
-                }
-
-                out.endTag(null, "ws");
-                out.endDocument();
-            } catch (IOException e) {
-                Slog.w(TAG, "Unable to save widget state for " + backupTarget);
-                return null;
-            }
-
-        }
-        return stream.toByteArray();
+    @Override
+    public void restoreStarting(int userId) {
+        mBackupRestoreController.restoreStarting(userId);
     }
 
-    public void restoreStarting() {
-        if (DEBUG_BACKUP) {
-            Slog.i(TAG, "restore starting for user " + mUserId);
-        }
-        synchronized (mRestoredWidgetIds) {
-            // We're starting a new "system" restore operation, so any widget restore
-            // state that we see from here on is intended to replace the current
-            // widget configuration of any/all of the affected apps.
-            mPrunedApps.clear();
-            mUpdatesByProvider.clear();
-            mUpdatesByHost.clear();
-        }
+    @Override
+    public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+        mBackupRestoreController.restoreWidgetState(packageName, restoredState, userId);
     }
 
-    // We're restoring widget state for 'pkg', so we start by wiping (a) all widget
-    // instances that are hosted by that app, and (b) all instances in other hosts
-    // for which 'pkg' is the provider.  We assume that we'll be restoring all of
-    // these hosts & providers, so will be reconstructing a correct live state.
-    private void pruneWidgetStateLr(String pkg) {
-        if (!mPrunedApps.contains(pkg)) {
-            if (DEBUG_BACKUP) {
-                Slog.i(TAG, "pruning widget state for restoring package " + pkg);
-            }
-            for (int i = mAppWidgetIds.size() - 1; i >= 0; i--) {
-                AppWidgetId id = mAppWidgetIds.get(i);
-                Provider p = id.provider;
-                if (id.host.packageName.equals(pkg)
-                        || p.info.provider.getPackageName().equals(pkg)) {
-                    // 'pkg' is either the host or the provider for this instances,
-                    // so we tear it down in anticipation of it (possibly) being
-                    // reconstructed due to the restore
-                    p.instances.remove(id);
-
-                    unbindAppWidgetRemoteViewsServicesLocked(id);
-                    mAppWidgetIds.remove(i);
-                }
-            }
-            mPrunedApps.add(pkg);
-        } else {
-            if (DEBUG_BACKUP) {
-                Slog.i(TAG, "already pruned " + pkg + ", continuing normally");
-            }
-        }
+    @Override
+    public void restoreFinished(int userId) {
+        mBackupRestoreController.restoreFinished(userId);
     }
 
-    // Accumulate a list of updates that affect the given provider for a final
-    // coalesced notification broadcast once restore is over.
-    class RestoreUpdateRecord {
-        public int oldId;
-        public int newId;
-        public boolean notified;
-
-        public RestoreUpdateRecord(int theOldId, int theNewId) {
-            oldId = theOldId;
-            newId = theNewId;
-            notified = false;
-        }
-    }
-
-    HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider
-            = new HashMap<Provider, ArrayList<RestoreUpdateRecord>>();
-    HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost
-            = new HashMap<Host, ArrayList<RestoreUpdateRecord>>();
-
-    private boolean alreadyStashed(ArrayList<RestoreUpdateRecord> stash,
-            final int oldId, final int newId) {
-        final int N = stash.size();
-        for (int i = 0; i < N; i++) {
-            RestoreUpdateRecord r = stash.get(i);
-            if (r.oldId == oldId && r.newId == newId) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void stashProviderRestoreUpdateLr(Provider provider, int oldId, int newId) {
-        ArrayList<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider);
-        if (r == null) {
-            r = new ArrayList<RestoreUpdateRecord>();
-            mUpdatesByProvider.put(provider, r);
-        } else {
-            // don't duplicate
-            if (alreadyStashed(r, oldId, newId)) {
-                if (DEBUG_BACKUP) {
-                    Slog.i(TAG, "ID remap " + oldId + " -> " + newId
-                            + " already stashed for " + provider);
-                }
-                return;
-            }
-        }
-        r.add(new RestoreUpdateRecord(oldId, newId));
-    }
-
-    private void stashHostRestoreUpdateLr(Host host, int oldId, int newId) {
-        ArrayList<RestoreUpdateRecord> r = mUpdatesByHost.get(host);
-        if (r == null) {
-            r = new ArrayList<RestoreUpdateRecord>();
-            mUpdatesByHost.put(host, r);
-        } else {
-            if (alreadyStashed(r, oldId, newId)) {
-                if (DEBUG_BACKUP) {
-                    Slog.i(TAG, "ID remap " + oldId + " -> " + newId
-                            + " already stashed for " + host);
-                }
-                return;
-            }
-        }
-        r.add(new RestoreUpdateRecord(oldId, newId));
-    }
-
-    public void restoreWidgetState(String packageName, byte[] restoredState) {
-        if (!mHasFeature) {
-            return;
-        }
-
-        if (DEBUG_BACKUP) {
-            Slog.i(TAG, "Restoring widget state for " + packageName);
-        }
-
-        ByteArrayInputStream stream = new ByteArrayInputStream(restoredState);
-        try {
-            // Providers mentioned in the widget dataset by ordinal
-            ArrayList<Provider> restoredProviders = new ArrayList<Provider>();
-
-            // Hosts mentioned in the widget dataset by ordinal
-            ArrayList<Host> restoredHosts = new ArrayList<Host>();
-
-            //HashSet<String> toNotify = new HashSet<String>();
-
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(stream, null);
-
-            synchronized (mAppWidgetIds) {
-                synchronized (mRestoredWidgetIds) {
-                    int type;
-                    do {
-                        type = parser.next();
-                        if (type == XmlPullParser.START_TAG) {
-                            final String tag = parser.getName();
-                            if ("ws".equals(tag)) {
-                                String v = parser.getAttributeValue(null, "version");
-                                String pkg = parser.getAttributeValue(null, "pkg");
-
-                                // TODO: fix up w.r.t. canonical vs current package names
-                                if (!packageName.equals(pkg)) {
-                                    Slog.w(TAG, "Package mismatch in ws");
-                                    return;
-                                }
-
-                                int version = Integer.parseInt(v);
-                                if (version > WIDGET_STATE_VERSION) {
-                                    Slog.w(TAG, "Unable to process state version " + version);
-                                    return;
-                                }
-                            } else if ("p".equals(tag)) {
-                                String pkg = parser.getAttributeValue(null, "pkg");
-                                String cl = parser.getAttributeValue(null, "cl");
-
-                                // hostedProviders index will match 'p' attribute in widget's
-                                // entry in the xml file being restored
-                                // If there's no live entry for this provider, add an inactive one
-                                // so that widget IDs referring to them can be properly allocated
-                                final ComponentName cn = new ComponentName(pkg, cl);
-                                Provider p = lookupProviderLocked(cn, mInstalledProviders);
-                                if (p == null) {
-                                    p = new Provider();
-                                    p.info = new AppWidgetProviderInfo();
-                                    p.info.provider = cn;
-                                    p.zombie = true;
-                                    mInstalledProviders.add(p);
-                                }
-                                if (DEBUG_BACKUP) {
-                                    Slog.i(TAG, "   provider " + cn);
-                                }
-                                restoredProviders.add(p);
-                            } else if ("h".equals(tag)) {
-                                // The host app may not yet exist on the device.  If it's here we
-                                // just use the existing Host entry, otherwise we create a
-                                // placeholder whose uid will be fixed up at PACKAGE_ADDED time.
-                                String pkg = parser.getAttributeValue(null, "pkg");
-                                int uid;
-                                try {
-                                    uid = getUidForPackage(pkg);
-                                } catch (NameNotFoundException e) {
-                                    uid = -1;
-                                }
-                                int hostId = Integer.parseInt(
-                                        parser.getAttributeValue(null, "id"), 16);
-                                Host h = lookupOrAddHostLocked(uid, pkg, hostId);
-                                if (DEBUG_BACKUP) {
-                                    Slog.i(TAG, "   host[" + restoredHosts.size()
-                                            + "]: {" + h.packageName + ":" + h.hostId + "}");
-                                }
-                                restoredHosts.add(h);
-                            } else if ("g".equals(tag)) {
-                                int restoredId = Integer.parseInt(
-                                        parser.getAttributeValue(null, "id"), 16);
-                                int hostIndex = Integer.parseInt(
-                                        parser.getAttributeValue(null, "h"), 16);
-                                Host host = restoredHosts.get(hostIndex);
-                                Provider p = null;
-                                String prov = parser.getAttributeValue(null, "p");
-                                if (prov != null) {
-                                    // could have been null if the app had allocated an id
-                                    // but not yet established a binding under that id
-                                    int which = Integer.parseInt(prov, 16);
-                                    p = restoredProviders.get(which);
-                                }
-
-                                // We'll be restoring widget state for both the host and
-                                // provider sides of this widget ID, so make sure we are
-                                // beginning from a clean slate on both fronts.
-                                pruneWidgetStateLr(host.packageName);
-                                if (p != null) {
-                                    pruneWidgetStateLr(p.info.provider.getPackageName());
-                                }
-
-                                // Have we heard about this ancestral widget instance before?
-                                AppWidgetId id = findRestoredWidgetLocked(restoredId, host, p);
-                                if (id == null) {
-                                    id = new AppWidgetId();
-                                    id.appWidgetId = mNextAppWidgetId++;
-                                    id.restoredId = restoredId;
-                                    id.options = parseWidgetIdOptions(parser);
-                                    id.host = host;
-                                    id.host.instances.add(id);
-                                    id.provider = p;
-                                    if (id.provider != null) {
-                                        id.provider.instances.add(id);
-                                    }
-                                    if (DEBUG_BACKUP) {
-                                        Slog.i(TAG, "New restored id " + restoredId
-                                                + " now " + id);
-                                    }
-                                    mAppWidgetIds.add(id);
-                                }
-                                if (id.provider.info != null) {
-                                    stashProviderRestoreUpdateLr(id.provider,
-                                            restoredId, id.appWidgetId);
-                                } else {
-                                    Slog.w(TAG, "Missing provider for restored widget " + id);
-                                }
-                                stashHostRestoreUpdateLr(id.host, restoredId, id.appWidgetId);
-
-                                if (DEBUG_BACKUP) {
-                                    Slog.i(TAG, "   instance: " + restoredId
-                                            + " -> " + id.appWidgetId
-                                            + " :: p=" + id.provider);
-                                }
-                            }
-                        }
-                    } while (type != XmlPullParser.END_DOCUMENT);
-
-                    // We've updated our own bookkeeping.  We'll need to notify the hosts and
-                    // providers about the changes, but we can't do that yet because the restore
-                    // target is not necessarily fully live at this moment.  Set aside the
-                    // information for now; the backup manager will call us once more at the
-                    // end of the process when all of the targets are in a known state, and we
-                    // will update at that point.
-                }
-            }
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, "Unable to restore widget state for " + packageName);
-        } catch (IOException e) {
-            Slog.w(TAG, "Unable to restore widget state for " + packageName);
-        } finally {
-            saveStateAsync();
-        }
-    }
-
-    // Called once following the conclusion of a restore operation.  This is when we
-    // send out updates to apps involved in widget-state restore telling them about
-    // the new widget ID space.
-    public void restoreFinished() {
-        if (DEBUG_BACKUP) {
-            Slog.i(TAG, "restoreFinished for " + mUserId);
-        }
-
-        final UserHandle userHandle = new UserHandle(mUserId);
-        synchronized (mRestoredWidgetIds) {
-            // Build the providers' broadcasts and send them off
-            Set<Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
-                    = mUpdatesByProvider.entrySet();
-            for (Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
-                // For each provider there's a list of affected IDs
-                Provider provider = e.getKey();
-                ArrayList<RestoreUpdateRecord> updates = e.getValue();
-                final int pending = countPendingUpdates(updates);
-                if (DEBUG_BACKUP) {
-                    Slog.i(TAG, "Provider " + provider + " pending: " + pending);
-                }
-                if (pending > 0) {
-                    int[] oldIds = new int[pending];
-                    int[] newIds = new int[pending];
-                    final int N = updates.size();
-                    int nextPending = 0;
-                    for (int i = 0; i < N; i++) {
-                        RestoreUpdateRecord r = updates.get(i);
-                        if (!r.notified) {
-                            r.notified = true;
-                            oldIds[nextPending] = r.oldId;
-                            newIds[nextPending] = r.newId;
-                            nextPending++;
-                            if (DEBUG_BACKUP) {
-                                Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
-                            }
-                        }
-                    }
-                    sendWidgetRestoreBroadcast(AppWidgetManager.ACTION_APPWIDGET_RESTORED,
-                            provider, null, oldIds, newIds, userHandle);
-                }
-            }
-
-            // same thing per host
-            Set<Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
-                    = mUpdatesByHost.entrySet();
-            for (Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
-                Host host = e.getKey();
-                if (host.uid > 0) {
-                    ArrayList<RestoreUpdateRecord> updates = e.getValue();
-                    final int pending = countPendingUpdates(updates);
-                    if (DEBUG_BACKUP) {
-                        Slog.i(TAG, "Host " + host + " pending: " + pending);
-                    }
-                    if (pending > 0) {
-                        int[] oldIds = new int[pending];
-                        int[] newIds = new int[pending];
-                        final int N = updates.size();
-                        int nextPending = 0;
-                        for (int i = 0; i < N; i++) {
-                            RestoreUpdateRecord r = updates.get(i);
-                            if (!r.notified) {
-                                r.notified = true;
-                                oldIds[nextPending] = r.oldId;
-                                newIds[nextPending] = r.newId;
-                                nextPending++;
-                                if (DEBUG_BACKUP) {
-                                    Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
-                                }
-                            }
-                        }
-                        sendWidgetRestoreBroadcast(AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
-                                null, host, oldIds, newIds, userHandle);
-                    }
-                }
-            }
-        }
-    }
-
-    private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
-        int pending = 0;
-        final int N = updates.size();
-        for (int i = 0; i < N; i++) {
-            RestoreUpdateRecord r = updates.get(i);
-            if (!r.notified) {
-                pending++;
-            }
-        }
-        return pending;
-    }
-
-    void sendWidgetRestoreBroadcast(String action, Provider provider, Host host,
-            int[] oldIds, int[] newIds, UserHandle userHandle) {
-        Intent intent = new Intent(action);
-        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds);
-        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds);
-        if (provider != null) {
-            intent.setComponent(provider.info.provider);
-            mContext.sendBroadcastAsUser(intent, userHandle);
-        }
-        if (host != null) {
-            intent.setComponent(null);
-            intent.setPackage(host.packageName);
-            intent.putExtra(AppWidgetManager.EXTRA_HOST_ID, host.hostId);
-            mContext.sendBroadcastAsUser(intent, userHandle);
-        }
-    }
-
-    private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
-        Provider p = null;
+    @SuppressWarnings("deprecation")
+    private Provider parseProviderInfoXml(ProviderId providerId, ResolveInfo ri) {
+        Provider provider = null;
 
         ActivityInfo activityInfo = ri.activityInfo;
         XmlResourceParser parser = null;
@@ -2165,7 +2107,7 @@
                     AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
             if (parser == null) {
                 Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
-                        + " meta-data for " + "AppWidget provider '" + component + '\'');
+                        + " meta-data for " + "AppWidget provider '" + providerId + '\'');
                 return null;
             }
 
@@ -2180,19 +2122,28 @@
             String nodeName = parser.getName();
             if (!"appwidget-provider".equals(nodeName)) {
                 Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
-                        + " AppWidget provider '" + component + '\'');
+                        + " AppWidget provider " + providerId.componentName
+                        + " for user " + providerId.uid);
                 return null;
             }
 
-            p = new Provider();
-            AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
-            info.provider = component;
-            p.uid = activityInfo.applicationInfo.uid;
+            provider = new Provider();
+            provider.id = providerId;
+            AppWidgetProviderInfo info = provider.info = new AppWidgetProviderInfo();
+            info.provider = providerId.componentName;
+            info.providerInfo = activityInfo;
 
-            Resources res = mContext.getPackageManager()
-                    .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
+            final Resources resources;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                resources = mContext.getPackageManager()
+                        .getResourcesForApplicationAsUser(activityInfo.packageName,
+                                UserHandle.getUserId(providerId.uid));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
 
-            TypedArray sa = res.obtainAttributes(attrs,
+            TypedArray sa = resources.obtainAttributes(attrs,
                     com.android.internal.R.styleable.AppWidgetProviderInfo);
 
             // These dimensions has to be resolved in the application's context.
@@ -2215,10 +2166,12 @@
                     com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
             info.initialKeyguardLayout = sa.getResourceId(com.android.internal.R.styleable.
                     AppWidgetProviderInfo_initialKeyguardLayout, 0);
+
             String className = sa
                     .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
             if (className != null) {
-                info.configure = new ComponentName(component.getPackageName(), className);
+                info.configure = new ComponentName(providerId.componentName.getPackageName(),
+                        className);
             }
             info.label = activityInfo.loadLabel(mContext.getPackageManager()).toString();
             info.icon = ri.getIconResource();
@@ -2234,111 +2187,224 @@
                     AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
 
             sa.recycle();
-        } catch (Exception e) {
+        } catch (IOException | PackageManager.NameNotFoundException | XmlPullParserException e) {
             // Ok to catch Exception here, because anything going wrong because
             // of what a client process passes to us should not be fatal for the
             // system process.
-            Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
+            Slog.w(TAG, "XML parsing failed for AppWidget provider "
+                    + providerId.componentName + " for user " + providerId.uid, e);
             return null;
         } finally {
-            if (parser != null)
+            if (parser != null) {
                 parser.close();
+            }
         }
-        return p;
+        return provider;
     }
 
-    int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
+    private int getUidForPackage(String packageName, int userId) {
         PackageInfo pkgInfo = null;
+
+        final long identity = Binder.clearCallingIdentity();
         try {
-            pkgInfo = mPm.getPackageInfo(packageName, 0, mUserId);
+            pkgInfo = mPackageManager.getPackageInfo(packageName, 0, userId);
         } catch (RemoteException re) {
             // Shouldn't happen, local call
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
+
         if (pkgInfo == null || pkgInfo.applicationInfo == null) {
-            throw new PackageManager.NameNotFoundException();
+            return -1;
         }
+
         return pkgInfo.applicationInfo.uid;
     }
 
-    int enforceSystemOrCallingUid(String packageName) throws IllegalArgumentException {
-        int callingUid = Binder.getCallingUid();
-        if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID || callingUid == 0) {
-            return callingUid;
+    private ActivityInfo getProviderInfo(ComponentName componentName, int userId) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+        intent.setComponent(componentName);
+
+        List<ResolveInfo> receivers = queryIntentReceivers(intent, userId);
+        // We are setting component, so there is only one or none.
+        if (!receivers.isEmpty()) {
+            return receivers.get(0).activityInfo;
         }
-        return enforceCallingUid(packageName);
+
+        return null;
     }
 
-    int enforceCallingUid(String packageName) throws IllegalArgumentException {
-        int callingUid = Binder.getCallingUid();
-        int packageUid;
+    private List<ResolveInfo> queryIntentReceivers(Intent intent, int userId) {
+        final long identity = Binder.clearCallingIdentity();
         try {
-            packageUid = getUidForPackage(packageName);
-        } catch (PackageManager.NameNotFoundException ex) {
-            throw new IllegalArgumentException("packageName and uid don't match packageName="
-                    + packageName);
+            return mPackageManager.queryIntentReceivers(intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    PackageManager.GET_META_DATA, userId);
+        } catch (RemoteException re) {
+            return Collections.emptyList();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        if (!UserHandle.isSameApp(callingUid, packageUid)) {
-            throw new IllegalArgumentException("packageName and uid don't match packageName="
-                    + packageName);
-        }
-        return callingUid;
     }
 
-    void sendInitialBroadcasts() {
-        synchronized (mAppWidgetIds) {
-            ensureStateLoadedLocked();
-            final int N = mInstalledProviders.size();
+    private void onUserStarted(int userId) {
+        synchronized (mLock) {
+            ensureGroupStateLoadedLocked(userId);
+
+            final int N = mProviders.size();
             for (int i = 0; i < N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (p.instances.size() > 0) {
-                    sendEnableIntentLocked(p);
-                    int[] appWidgetIds = getAppWidgetIds(p);
-                    sendUpdateIntentLocked(p, appWidgetIds);
-                    registerForBroadcastsLocked(p, appWidgetIds);
+                Provider provider = mProviders.get(i);
+
+                // Send broadcast only to the providers of the user.
+                if (provider.getUserId() != userId) {
+                    continue;
+                }
+
+                if (provider.widgets.size() > 0) {
+                    sendEnableIntentLocked(provider);
+                    int[] appWidgetIds = getWidgetIds(provider.widgets);
+                    sendUpdateIntentLocked(provider, appWidgetIds);
+                    registerForBroadcastsLocked(provider, appWidgetIds);
                 }
             }
         }
     }
 
     // only call from initialization -- it assumes that the data structures are all empty
-    void loadStateLocked() {
-        AtomicFile file = savedStateFile();
-        try {
-            FileInputStream stream = file.openRead();
-            readStateFromFileLocked(stream);
+    private void loadGroupStateLocked(int[] profileIds) {
+        // We can bind the widgets to host and providers only after
+        // reading the host and providers for all users since a widget
+        // can have a host and a provider in different users.
+        List<LoadedWidgetState> loadedWidgets = new ArrayList<>();
 
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e) {
-                    Slog.w(TAG, "Failed to close state FileInputStream " + e);
+        int version = 0;
+
+        final int profileIdCount = profileIds.length;
+        for (int i = 0; i < profileIdCount; i++) {
+            final int profileId = profileIds[i];
+
+            // No file written for this user - nothing to do.
+            AtomicFile file = getSavedStateFile(profileId);
+            try {
+                FileInputStream stream = file.openRead();
+                version = readProfileStateFromFileLocked(stream, profileId, loadedWidgets);
+                IoUtils.closeQuietly(stream);
+            } catch (FileNotFoundException e) {
+                Slog.w(TAG, "Failed to read state: " + e);
+            }
+        }
+
+        if (version >= 0) {
+            // Hooke'm up...
+            bindLoadedWidgets(loadedWidgets);
+
+            // upgrade the database if needed
+            performUpgradeLocked(version);
+        } else {
+            // failed reading, clean up
+            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
+            mWidgets.clear();
+            mHosts.clear();
+            final int N = mProviders.size();
+            for (int i = 0; i < N; i++) {
+                mProviders.get(i).widgets.clear();
+            }
+        }
+    }
+
+    private void bindLoadedWidgets(List<LoadedWidgetState> loadedWidgets) {
+        final int loadedWidgetCount = loadedWidgets.size();
+        for (int i = loadedWidgetCount - 1; i >= 0; i--) {
+            LoadedWidgetState loadedWidget = loadedWidgets.remove(i);
+            Widget widget = loadedWidget.widget;
+
+            widget.provider = findProviderByTag(loadedWidget.providerTag);
+            if (widget.provider == null) {
+                // This provider is gone. We just let the host figure out
+                // that this happened when it fails to load it.
+                continue;
+            }
+
+            widget.host = findHostByTag(loadedWidget.hostTag);
+            if (widget.host == null) {
+                // This host is gone.
+                continue;
+            }
+
+            widget.provider.widgets.add(widget);
+            widget.host.widgets.add(widget);
+            mWidgets.add(widget);
+        }
+    }
+
+    private Provider findProviderByTag(int tag) {
+        if (tag < 0) {
+            return null;
+        }
+        final int providerCount = mProviders.size();
+        for (int i = 0; i < providerCount; i++) {
+            Provider provider = mProviders.get(i);
+            if (provider.tag == tag) {
+                return provider;
+            }
+        }
+        return null;
+    }
+
+    private Host findHostByTag(int tag) {
+        if (tag < 0) {
+            return null;
+        }
+        final int hostCount = mHosts.size();
+        for (int i = 0; i < hostCount; i++) {
+            Host host = mHosts.get(i);
+            if (host.tag == tag) {
+                return host;
+            }
+        }
+        return null;
+    }
+
+    private void saveStateLocked(int userId) {
+        tagProvidersAndHosts();
+
+        final int[] profileIds = mSecurityPolicy.getEnabledGroupProfileIds(userId);
+
+        final int profileCount = profileIds.length;
+        for (int i = 0; i < profileCount; i++) {
+            final int profileId = profileIds[i];
+
+            AtomicFile file = getSavedStateFile(profileId);
+            FileOutputStream stream;
+            try {
+                stream = file.startWrite();
+                if (writeProfileStateToFileLocked(stream, profileId)) {
+                    file.finishWrite(stream);
+                } else {
+                    file.failWrite(stream);
+                    Slog.w(TAG, "Failed to save state, restoring backup.");
                 }
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed open state file for write: " + e);
             }
-        } catch (FileNotFoundException e) {
-            Slog.w(TAG, "Failed to read state: " + e);
         }
     }
 
-    void saveStateLocked() {
-        if (!mHasFeature) {
-            return;
+    private void tagProvidersAndHosts() {
+        final int providerCount = mProviders.size();
+        for (int i = 0; i < providerCount; i++) {
+            Provider provider = mProviders.get(i);
+            provider.tag = i;
         }
-        AtomicFile file = savedStateFile();
-        FileOutputStream stream;
-        try {
-            stream = file.startWrite();
-            if (writeStateToFileLocked(stream)) {
-                file.finishWrite(stream);
-            } else {
-                file.failWrite(stream);
-                Slog.w(TAG, "Failed to save state, restoring backup.");
-            }
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed open state file for write: " + e);
+
+        final int hostCount = mHosts.size();
+        for (int i = 0; i < hostCount; i++) {
+            Host host = mHosts.get(i);
+            host.tag = i;
         }
     }
 
-    boolean writeStateToFileLocked(FileOutputStream stream) {
+    private boolean writeProfileStateToFileLocked(FileOutputStream stream, int userId) {
         int N;
 
         try {
@@ -2347,39 +2413,52 @@
             out.startDocument(null, true);
             out.startTag(null, "gs");
             out.attribute(null, "version", String.valueOf(CURRENT_VERSION));
-            int providerIndex = 0;
-            N = mInstalledProviders.size();
+
+            N = mProviders.size();
             for (int i = 0; i < N; i++) {
-                Provider p = mInstalledProviders.get(i);
-                if (p.instances.size() > 0) {
-                    serializeProvider(out, p);
-                    p.tag = providerIndex;
-                    providerIndex++;
+                Provider provider = mProviders.get(i);
+                // Save only providers for the user.
+                if (provider.getUserId() != userId) {
+                    continue;
+                }
+                if (provider.widgets.size() > 0) {
+                    serializeProvider(out, provider);
                 }
             }
 
             N = mHosts.size();
             for (int i = 0; i < N; i++) {
                 Host host = mHosts.get(i);
+                // Save only hosts for the user.
+                if (host.getUserId() != userId) {
+                    continue;
+                }
                 serializeHost(out, host);
-                host.tag = i;
             }
 
-            N = mAppWidgetIds.size();
+            N = mWidgets.size();
             for (int i = 0; i < N; i++) {
-                AppWidgetId id = mAppWidgetIds.get(i);
-                serializeAppWidgetId(out, id);
+                Widget widget = mWidgets.get(i);
+                // Save only widgets hosted by the user.
+                if (widget.host.getUserId() != userId) {
+                    continue;
+                }
+                serializeAppWidget(out, widget);
             }
 
-            Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
+            Iterator<Pair<Integer, String>> it = mPackagesWithBindWidgetPermission.iterator();
             while (it.hasNext()) {
+                Pair<Integer, String> binding = it.next();
+                // Save only white listings for the user.
+                if (binding.first != userId) {
+                    continue;
+                }
                 out.startTag(null, "b");
-                out.attribute(null, "packageName", it.next());
+                out.attribute(null, "packageName", binding.second);
                 out.endTag(null, "b");
             }
 
             out.endTag(null, "gs");
-
             out.endDocument();
             return true;
         } catch (IOException e) {
@@ -2388,17 +2467,16 @@
         }
     }
 
-    @SuppressWarnings("unused")
-    void readStateFromFileLocked(FileInputStream stream) {
-        boolean success = false;
-        int version = 0;
+    private int readProfileStateFromFileLocked(FileInputStream stream, int userId,
+            List<LoadedWidgetState> outLoadedWidgets) {
+        int version = -1;
         try {
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(stream, null);
 
+            int legacyProviderIndex = -1;
+            int legacyHostIndex = -1;
             int type;
-            int providerIndex = 0;
-            HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
             do {
                 type = parser.next();
                 if (type == XmlPullParser.START_TAG) {
@@ -2411,67 +2489,89 @@
                             version = 0;
                         }
                     } else if ("p".equals(tag)) {
+                        legacyProviderIndex++;
                         // TODO: do we need to check that this package has the same signature
                         // as before?
                         String pkg = parser.getAttributeValue(null, "pkg");
                         String cl = parser.getAttributeValue(null, "cl");
 
-                        final IPackageManager packageManager = AppGlobals.getPackageManager();
-                        try {
-                            packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
-                        } catch (RemoteException e) {
-                            String[] pkgs = mContext.getPackageManager()
-                                    .currentToCanonicalPackageNames(new String[] { pkg });
-                            pkg = pkgs[0];
+                        pkg = getCanonicalPackageName(pkg, cl, userId);
+                        if (pkg == null) {
+                            continue;
                         }
 
-                        Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
-                        if (p == null && mSafeMode) {
+                        final int uid = getUidForPackage(pkg, userId);
+                        if (uid < 0) {
+                            continue;
+                        }
+
+                        ComponentName componentName = new ComponentName(pkg, cl);
+
+                        ActivityInfo providerInfo = getProviderInfo(componentName, userId);
+                        if (providerInfo == null) {
+                            continue;
+                        }
+
+                        ProviderId providerId = new ProviderId(uid, componentName);
+                        Provider provider = lookupProviderLocked(providerId);
+
+                        if (provider == null && mSafeMode) {
                             // if we're in safe mode, make a temporary one
-                            p = new Provider();
-                            p.info = new AppWidgetProviderInfo();
-                            p.info.provider = new ComponentName(pkg, cl);
-                            p.zombie = true;
-                            mInstalledProviders.add(p);
+                            provider = new Provider();
+                            provider.info = new AppWidgetProviderInfo();
+                            provider.info.provider = providerId.componentName;
+                            provider.info.providerInfo = providerInfo;
+                            provider.zombie = true;
+                            provider.id = providerId;
+                            mProviders.add(provider);
                         }
-                        if (p != null) {
-                            // if it wasn't uninstalled or something
-                            loadedProviders.put(providerIndex, p);
-                        }
-                        providerIndex++;
-                    } else if ("h".equals(tag)) {
-                        Host host = new Host();
 
+                        String tagAttribute = parser.getAttributeValue(null, "tag");
+                        final int providerTag = !TextUtils.isEmpty(tagAttribute)
+                                ? Integer.parseInt(tagAttribute, 16) : legacyProviderIndex;
+                        provider.tag = providerTag;
+                    } else if ("h".equals(tag)) {
+                        legacyHostIndex++;
+                        Host host = new Host();
                         // TODO: do we need to check that this package has the same signature
                         // as before?
-                        host.packageName = parser.getAttributeValue(null, "pkg");
-                        try {
-                            host.uid = getUidForPackage(host.packageName);
-                        } catch (PackageManager.NameNotFoundException ex) {
+                        String pkg = parser.getAttributeValue(null, "pkg");
+
+                        final int uid = getUidForPackage(pkg, userId);
+                        if (uid < 0) {
                             host.zombie = true;
                         }
+
                         if (!host.zombie || mSafeMode) {
                             // In safe mode, we don't discard the hosts we don't recognize
                             // so that they're not pruned from our list. Otherwise, we do.
-                            host.hostId = Integer
-                                    .parseInt(parser.getAttributeValue(null, "id"), 16);
+                            final int hostId = Integer.parseInt(parser.getAttributeValue(
+                                    null, "id"), 16);
+
+                            String tagAttribute = parser.getAttributeValue(null, "tag");
+                            final int hostTag = !TextUtils.isEmpty(tagAttribute)
+                                    ? Integer.parseInt(tagAttribute, 16) : legacyHostIndex;
+
+                            host.tag = hostTag;
+                            host.id = new HostId(uid, hostId, pkg);
                             mHosts.add(host);
                         }
                     } else if ("b".equals(tag)) {
                         String packageName = parser.getAttributeValue(null, "packageName");
-                        if (packageName != null) {
-                            mPackagesWithBindWidgetPermission.add(packageName);
+                        final int uid = getUidForPackage(packageName, userId);
+                        if (uid >= 0) {
+                            Pair<Integer, String> packageId = Pair.create(userId, packageName);
+                            mPackagesWithBindWidgetPermission.add(packageId);
                         }
                     } else if ("g".equals(tag)) {
-                        AppWidgetId id = new AppWidgetId();
-                        id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
-                        if (id.appWidgetId >= mNextAppWidgetId) {
-                            mNextAppWidgetId = id.appWidgetId + 1;
-                        }
+                        Widget widget = new Widget();
+                        widget.appWidgetId = Integer.parseInt(parser.getAttributeValue(
+                                null, "id"), 16);
+                        setMinAppWidgetIdLocked(userId, widget.appWidgetId + 1);
 
                         // restored ID is allowed to be absent
                         String restoredIdString = parser.getAttributeValue(null, "rid");
-                        id.restoredId = (restoredIdString == null) ? 0
+                        widget.restoredId = (restoredIdString == null) ? 0
                                 : Integer.parseInt(restoredIdString, 16);
 
                         Bundle options = new Bundle();
@@ -2500,92 +2600,57 @@
                             options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
                                     Integer.parseInt(categoryString, 16));
                         }
-                        id.options = options;
+                        widget.options = options;
 
+                        final int hostTag = Integer.parseInt(parser.getAttributeValue(
+                                null, "h"), 16);
                         String providerString = parser.getAttributeValue(null, "p");
-                        if (providerString != null) {
-                            // there's no provider if it hasn't been bound yet.
-                            // maybe we don't have to save this, but it brings the system
-                            // to the state it was in.
-                            int pIndex = Integer.parseInt(providerString, 16);
-                            id.provider = loadedProviders.get(pIndex);
-                            if (false) {
-                                Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
-                                        + pIndex + " which is " + id.provider);
-                            }
-                            if (id.provider == null) {
-                                // This provider is gone. We just let the host figure out
-                                // that this happened when it fails to load it.
-                                continue;
-                            }
-                        }
+                        final int providerTag = (providerString != null) ? Integer.parseInt(
+                                parser.getAttributeValue(null, "p"), 16) : TAG_UNDEFINED;
 
-                        int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
-                        id.host = mHosts.get(hIndex);
-                        if (id.host == null) {
-                            // This host is gone.
-                            continue;
-                        }
-
-                        if (id.provider != null) {
-                            id.provider.instances.add(id);
-                        }
-                        id.host.instances.add(id);
-                        mAppWidgetIds.add(id);
+                        // We can match widgets with hosts and providers only after hosts
+                        // and providers for all users have been loaded since the widget
+                        // host and provider can be in different user profiles.
+                        LoadedWidgetState loadedWidgets = new LoadedWidgetState(widget,
+                                hostTag, providerTag);
+                        outLoadedWidgets.add(loadedWidgets);
                     }
                 }
             } while (type != XmlPullParser.END_DOCUMENT);
-            success = true;
-        } catch (NullPointerException e) {
+        } catch (NullPointerException
+                | NumberFormatException
+                | XmlPullParserException
+                | IOException
+                | IndexOutOfBoundsException e) {
             Slog.w(TAG, "failed parsing " + e);
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (IOException e) {
-            Slog.w(TAG, "failed parsing " + e);
-        } catch (IndexOutOfBoundsException e) {
-            Slog.w(TAG, "failed parsing " + e);
+            return -1;
         }
 
-        if (success) {
-            // delete any hosts that didn't manage to get connected (should happen)
-            // if it matters, they'll be reconnected.
-            for (int i = mHosts.size() - 1; i >= 0; i--) {
-                pruneHostLocked(mHosts.get(i));
-            }
-            // upgrade the database if needed
-            performUpgrade(version);
-        } else {
-            // failed reading, clean up
-            Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
-
-            mAppWidgetIds.clear();
-            mHosts.clear();
-            final int N = mInstalledProviders.size();
-            for (int i = 0; i < N; i++) {
-                mInstalledProviders.get(i).instances.clear();
-            }
-        }
+        return version;
     }
 
-    private void performUpgrade(int fromVersion) {
+    private void performUpgradeLocked(int fromVersion) {
         if (fromVersion < CURRENT_VERSION) {
-            Slog.v(TAG, "Upgrading widget database from " + fromVersion + " to " + CURRENT_VERSION
-                    + " for user " + mUserId);
+            Slog.v(TAG, "Upgrading widget database from " + fromVersion + " to "
+                    + CURRENT_VERSION);
         }
 
         int version = fromVersion;
 
         // Update 1: keyguard moved from package "android" to "com.android.keyguard"
         if (version == 0) {
-            for (int i = 0; i < mHosts.size(); i++) {
-                Host host = mHosts.get(i);
-                if (host != null && "android".equals(host.packageName)
-                        && host.hostId == KEYGUARD_HOST_ID) {
-                    host.packageName = KEYGUARD_HOST_PACKAGE;
+            HostId oldHostId = new HostId(Process.myUid(),
+                    KEYGUARD_HOST_ID, OLD_KEYGUARD_HOST_PACKAGE);
+
+            Host host = lookupHostLocked(oldHostId);
+            if (host != null) {
+                final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
+                        UserHandle.USER_OWNER);
+                if (uid >= 0) {
+                    host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
                 }
             }
+
             version = 1;
         }
 
@@ -2594,19 +2659,19 @@
         }
     }
 
-    static File getSettingsFile(int userId) {
-        return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
+    private static File getStateFile(int userId) {
+        return new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME);
     }
 
-    AtomicFile savedStateFile() {
-        File dir = Environment.getUserSystemDirectory(mUserId);
-        File settingsFile = getSettingsFile(mUserId);
-        if (!settingsFile.exists() && mUserId == 0) {
+    private static AtomicFile getSavedStateFile(int userId) {
+        File dir = Environment.getUserSystemDirectory(userId);
+        File settingsFile = getStateFile(userId);
+        if (!settingsFile.exists() && userId == UserHandle.USER_OWNER) {
             if (!dir.exists()) {
                 dir.mkdirs();
             }
             // Migrate old data
-            File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
+            File oldFile = new File("/data/system/" + STATE_FILENAME);
             // Method doesn't throw an exception on failure. Ignore any errors
             // in moving the file (like non-existence)
             oldFile.renameTo(settingsFile);
@@ -2614,45 +2679,67 @@
         return new AtomicFile(settingsFile);
     }
 
-    void onUserStopping() {
-        // prune the ones we don't want to keep
-        int N = mInstalledProviders.size();
-        for (int i = N - 1; i >= 0; i--) {
-            Provider p = mInstalledProviders.get(i);
-            cancelBroadcasts(p);
-        }
-    }
+    private void onUserStopped(int userId) {
+        synchronized (mLock) {
+            // Remove widgets that have both host and provider in the user.
+            final int widgetCount = mWidgets.size();
+            for (int i = widgetCount - 1; i >= 0; i--) {
+                Widget widget = mWidgets.get(i);
 
-    void onUserRemoved() {
-        getSettingsFile(mUserId).delete();
-    }
+                final boolean hostInUser = widget.host.getUserId() == userId;
+                final boolean hasProvider = widget.provider != null;
+                final boolean providerInUser = hasProvider && widget.provider.getUserId() == userId;
 
-    boolean addProvidersForPackageLocked(String pkgName) {
-        boolean providersAdded = false;
-        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        intent.setPackage(pkgName);
-        List<ResolveInfo> broadcastReceivers;
-        try {
-            broadcastReceivers = mPm.queryIntentReceivers(intent,
-                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    PackageManager.GET_META_DATA, mUserId);
-        } catch (RemoteException re) {
-            // Shouldn't happen, local call
-            return false;
-        }
-        final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
-        for (int i = 0; i < N; i++) {
-            ResolveInfo ri = broadcastReceivers.get(i);
-            ActivityInfo ai = ri.activityInfo;
-            if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
-                continue;
+                // If both host and provider are in the user, just drop the widgets
+                // as we do not want to make host callbacks and provider broadcasts
+                // as the host and the provider will be killed.
+                if (hostInUser && (!hasProvider || providerInUser)) {
+                    mWidgets.remove(i);
+                    widget.host.widgets.remove(widget);
+                    widget.host = null;
+                    if (hasProvider) {
+                        widget.provider.widgets.remove(widget);
+                        widget.provider = null;
+                    }
+                }
             }
-            if (pkgName.equals(ai.packageName)) {
-                providersAdded = addProviderLocked(ri);
-            }
-        }
 
-        return providersAdded;
+            // Remove hosts and notify providers in other profiles.
+            final int hostCount = mHosts.size();
+            for (int i = hostCount - 1; i >= 0; i--) {
+                Host host = mHosts.get(i);
+                if (host.getUserId() == userId) {
+                    deleteHostLocked(host);
+                }
+            }
+
+            // Remove the providers and notify hosts in other profiles.
+            final int providerCount = mProviders.size();
+            for (int i = providerCount - 1; i >= 0; i--) {
+                Provider provider = mProviders.get(i);
+                if (provider.getUserId() == userId) {
+                    deleteProviderLocked(provider);
+                }
+            }
+
+            // Remove grants for this user.
+            final int grantCount = mPackagesWithBindWidgetPermission.size();
+            for (int i = grantCount - 1; i >= 0; i--) {
+                Pair<Integer, String> packageId = mPackagesWithBindWidgetPermission.valueAt(i);
+                if (packageId.first == userId) {
+                    mPackagesWithBindWidgetPermission.removeAt(i);
+                }
+            }
+
+            // Take a note we no longer have state for this user.
+            final int index = mLoadedUserIds.indexOfKey(userId);
+            if (index >= 0) {
+                mLoadedUserIds.removeAt(index);
+            }
+
+            // Remove the widget id counter.
+            mNextAppWidgetIds.removeAt(userId);
+        }
     }
 
     /**
@@ -2661,71 +2748,59 @@
      *
      * @return whether any providers were updated
      */
-    boolean updateProvidersForPackageLocked(String pkgName, Set<ComponentName> removedProviders) {
+    private boolean updateProvidersForPackageLocked(String packageName, int userId,
+            Set<ProviderId> removedProviders) {
         boolean providersUpdated = false;
-        HashSet<String> keep = new HashSet<String>();
+
+        HashSet<ProviderId> keep = new HashSet<>();
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
-        intent.setPackage(pkgName);
-        List<ResolveInfo> broadcastReceivers;
-        try {
-            broadcastReceivers = mPm.queryIntentReceivers(intent,
-                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                PackageManager.GET_META_DATA, mUserId);
-        } catch (RemoteException re) {
-            // Shouldn't happen, local call
-            return false;
-        }
+        intent.setPackage(packageName);
+        List<ResolveInfo> broadcastReceivers = queryIntentReceivers(intent, userId);
 
         // add the missing ones and collect which ones to keep
         int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
         for (int i = 0; i < N; i++) {
             ResolveInfo ri = broadcastReceivers.get(i);
             ActivityInfo ai = ri.activityInfo;
+
             if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
                 continue;
             }
-            if (pkgName.equals(ai.packageName)) {
-                ComponentName component = new ComponentName(ai.packageName, ai.name);
-                Provider p = lookupProviderLocked(component);
-                if (p == null) {
+
+            if (packageName.equals(ai.packageName)) {
+                ProviderId providerId = new ProviderId(ai.applicationInfo.uid,
+                        new ComponentName(ai.packageName, ai.name));
+
+                Provider provider = lookupProviderLocked(providerId);
+                if (provider == null) {
                     if (addProviderLocked(ri)) {
-                        keep.add(ai.name);
+                        keep.add(providerId);
                         providersUpdated = true;
                     }
                 } else {
-                    Provider parsed = parseProviderInfoXml(component, ri);
+                    Provider parsed = parseProviderInfoXml(providerId, ri);
                     if (parsed != null) {
-                        keep.add(ai.name);
+                        keep.add(providerId);
                         // Use the new AppWidgetProviderInfo.
-                        p.info = parsed.info;
+                        provider.info = parsed.info;
                         // If it's enabled
-                        final int M = p.instances.size();
+                        final int M = provider.widgets.size();
                         if (M > 0) {
-                            int[] appWidgetIds = getAppWidgetIds(p);
+                            int[] appWidgetIds = getWidgetIds(provider.widgets);
                             // Reschedule for the new updatePeriodMillis (don't worry about handling
                             // it specially if updatePeriodMillis didn't change because we just sent
                             // an update, and the next one will be updatePeriodMillis from now).
-                            cancelBroadcasts(p);
-                            registerForBroadcastsLocked(p, appWidgetIds);
+                            cancelBroadcasts(provider);
+                            registerForBroadcastsLocked(provider, appWidgetIds);
                             // If it's currently showing, call back with the new
                             // AppWidgetProviderInfo.
                             for (int j = 0; j < M; j++) {
-                                AppWidgetId id = p.instances.get(j);
-                                id.views = null;
-                                if (id.host != null && id.host.callbacks != null) {
-                                    try {
-                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info,
-                                                mUserId);
-                                    } catch (RemoteException ex) {
-                                        // It failed; remove the callback. No need to prune because
-                                        // we know that this host is still referenced by this
-                                        // instance.
-                                        id.host.callbacks = null;
-                                    }
-                                }
+                                Widget widget = provider.widgets.get(j);
+                                widget.views = null;
+                                scheduleNotifyProviderChangedLocked(widget);
                             }
                             // Now that we've told the host, push out an update.
-                            sendUpdateIntentLocked(p, appWidgetIds);
+                            sendUpdateIntentLocked(provider, appWidgetIds);
                             providersUpdated = true;
                         }
                     }
@@ -2734,15 +2809,16 @@
         }
 
         // prune the ones we don't want to keep
-        N = mInstalledProviders.size();
+        N = mProviders.size();
         for (int i = N - 1; i >= 0; i--) {
-            Provider p = mInstalledProviders.get(i);
-            if (pkgName.equals(p.info.provider.getPackageName())
-                    && !keep.contains(p.info.provider.getClassName())) {
+            Provider provider = mProviders.get(i);
+            if (packageName.equals(provider.info.provider.getPackageName())
+                    && provider.getUserId() == userId
+                    && !keep.contains(provider.id)) {
                 if (removedProviders != null) {
-                    removedProviders.add(p.info.provider);
+                    removedProviders.add(provider.id);
                 }
-                removeProviderLocked(i, p);
+                deleteProviderLocked(provider);
                 providersUpdated = true;
             }
         }
@@ -2750,46 +2826,1249 @@
         return providersUpdated;
     }
 
-    boolean removeProvidersForPackageLocked(String pkgName) {
-        boolean providersRemoved = false;
-        int N = mInstalledProviders.size();
+    private boolean removeHostsAndProvidersForPackageLocked(String pkgName, int userId) {
+        boolean removed = false;
+
+        int N = mProviders.size();
         for (int i = N - 1; i >= 0; i--) {
-            Provider p = mInstalledProviders.get(i);
-            if (pkgName.equals(p.info.provider.getPackageName())) {
-                removeProviderLocked(i, p);
-                providersRemoved = true;
+            Provider provider = mProviders.get(i);
+            if (pkgName.equals(provider.info.provider.getPackageName())
+                    && provider.getUserId() == userId) {
+                deleteProviderLocked(provider);
+                removed = true;
             }
         }
 
         // Delete the hosts for this package too
-        //
         // By now, we have removed any AppWidgets that were in any hosts here,
         // so we don't need to worry about sending DISABLE broadcasts to them.
         N = mHosts.size();
         for (int i = N - 1; i >= 0; i--) {
             Host host = mHosts.get(i);
-            if (pkgName.equals(host.packageName)) {
+            if (pkgName.equals(host.id.packageName)
+                    && host.getUserId() == userId) {
                 deleteHostLocked(host);
+                removed = true;
             }
         }
 
-        return providersRemoved;
+        return removed;
     }
 
-    void notifyHostsForProvidersChangedLocked() {
-        final int N = mHosts.size();
-        for (int i = N - 1; i >= 0; i--) {
-            Host host = mHosts.get(i);
+    private String getCanonicalPackageName(String packageName, String className, int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
             try {
-                if (host.callbacks != null) {
-                    host.callbacks.providersChanged(mUserId);
+                AppGlobals.getPackageManager().getReceiverInfo(new ComponentName(packageName,
+                        className), 0, userId);
+                return packageName;
+            } catch (RemoteException re) {
+                String[] packageNames = mContext.getPackageManager()
+                        .currentToCanonicalPackageNames(new String[]{packageName});
+                if (packageNames != null && packageNames.length > 0) {
+                    return packageNames[0];
                 }
-            } catch (RemoteException ex) {
-                // It failed; remove the callback. No need to prune because
-                // we know that this host is still referenced by this
-                // instance.
-                host.callbacks = null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return null;
+    }
+
+    private void sendBroadcastAsUser(Intent intent, UserHandle userHandle) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mContext.sendBroadcastAsUser(intent, userHandle);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void bindService(Intent intent, ServiceConnection connection,
+            UserHandle userHandle) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
+                    userHandle);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void unbindService(ServiceConnection connection) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mContext.unbindService(connection);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private final class CallbackHandler extends Handler {
+        public static final int MSG_NOTIFY_UPDATE_APP_WIDGET = 1;
+        public static final int MSG_NOTIFY_PROVIDER_CHANGED = 2;
+        public static final int MSG_NOTIFY_PROVIDERS_CHANGED = 3;
+        public static final int MSG_NOTIFY_VIEW_DATA_CHANGED = 4;
+
+        public CallbackHandler(Looper looper) {
+            super(looper, null, false);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MSG_NOTIFY_UPDATE_APP_WIDGET: {
+                    SomeArgs args = (SomeArgs) message.obj;
+                    Host host = (Host) args.arg1;
+                    IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
+                    RemoteViews views = (RemoteViews) args.arg3;
+                    final int appWidgetId = args.argi1;
+                    args.recycle();
+
+                    handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views);
+                } break;
+
+                case MSG_NOTIFY_PROVIDER_CHANGED: {
+                    SomeArgs args = (SomeArgs) message.obj;
+                    Host host = (Host) args.arg1;
+                    IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
+                    AppWidgetProviderInfo info = (AppWidgetProviderInfo)args.arg3;
+                    final int appWidgetId = args.argi1;
+                    args.recycle();
+
+                    handleNotifyProviderChanged(host, callbacks, appWidgetId, info);
+                } break;
+
+                case MSG_NOTIFY_PROVIDERS_CHANGED: {
+                    SomeArgs args = (SomeArgs) message.obj;
+                    Host host = (Host) args.arg1;
+                    IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
+                    args.recycle();
+
+                    handleNotifyProvidersChanged(host, callbacks);
+                } break;
+
+                case MSG_NOTIFY_VIEW_DATA_CHANGED: {
+                    SomeArgs args = (SomeArgs) message.obj;
+                    Host host = (Host) args.arg1;
+                    IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
+                    final int appWidgetId = args.argi1;
+                    final int viewId = args.argi2;
+                    args.recycle();
+
+                    handleNotifyAppWidgetViewDataChanged(host, callbacks, appWidgetId, viewId);
+                } break;
             }
         }
     }
-}
+
+    private final class SecurityPolicy {
+
+        public int[] resolveCallerEnabledGroupProfiles(int[] profileIds) {
+            final int parentId = UserHandle.getCallingUserId();
+
+            int enabledProfileCount = 0;
+            final int profileCount = profileIds.length;
+            for (int i = 0; i < profileCount; i++) {
+                final int profileId = profileIds[i];
+                if (!isParentOrProfile(parentId, profileId)) {
+                    throw new SecurityException("Not the current user or"
+                            + " a child profile: " + profileId);
+                }
+                if (!isProfileEnabled(profileId)) {
+                    profileIds[i] = DISABLED_PROFILE;
+                } else {
+                    enabledProfileCount++;
+                }
+            }
+
+            int resolvedProfileIndex = 0;
+            final int[] resolvedProfiles = new int[enabledProfileCount];
+            for (int i = 0; i < profileCount; i++) {
+                final int profileId = profileIds[i];
+                if (profileId != DISABLED_PROFILE) {
+                    resolvedProfiles[resolvedProfileIndex] = profileId;
+                    resolvedProfileIndex++;
+                }
+            }
+
+            return resolvedProfiles;
+        }
+
+        public int[] getEnabledGroupProfileIds(int userId) {
+            final int parentId = getGroupParent(userId);
+
+            final List<UserInfo> profiles;
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                profiles = mUserManager.getProfiles(parentId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+
+            int enabledProfileCount = 0;
+            final int profileCount = profiles.size();
+            for (int i = 0; i < profileCount; i++) {
+                if (profiles.get(i).isEnabled()) {
+                    enabledProfileCount++;
+                }
+            }
+
+            int enabledProfileIndex = 0;
+            final int[] profileIds = new int[enabledProfileCount];
+            for (int i = 0; i < profileCount; i++) {
+                UserInfo profile = profiles.get(i);
+                if (profile.isEnabled()) {
+                    profileIds[enabledProfileIndex] = profile.getUserHandle().getIdentifier();
+                    enabledProfileIndex++;
+                }
+            }
+
+            return profileIds;
+        }
+
+        public void enforceServiceExistsAndRequiresBindRemoteViewsPermission(
+                ComponentName componentName, int userId) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                ServiceInfo serviceInfo = mPackageManager.getServiceInfo(componentName,
+                        PackageManager.GET_PERMISSIONS, userId);
+                if (serviceInfo == null) {
+                    throw new SecurityException("Service " + componentName
+                            + " not installed for user " + userId);
+                }
+                if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(serviceInfo.permission)) {
+                    throw new SecurityException("Service " + componentName
+                            + " in user " + userId + "does not require "
+                            + android.Manifest.permission.BIND_REMOTEVIEWS);
+                }
+            } catch (RemoteException re) {
+                // Local call - shouldn't happen.
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        public void enforceModifyAppWidgetBindPermissions(String packageName) {
+            mContext.enforceCallingPermission(
+                    android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
+                    "hasBindAppWidgetPermission packageName=" + packageName);
+        }
+
+        public void enforceCallFromPackage(String packageName) {
+            if (!isCallFromPackage(packageName)) {
+                throw new SecurityException("Package " + packageName
+                        + " not running under user " + UserHandle.getCallingUserId());
+            }
+        }
+
+        public boolean isCallFromPackage(String packageName) {
+            // System and root call all from anywhere they want.
+            final int callingUid = Binder.getCallingUid();
+            if (callingUid == Process.SYSTEM_UID || callingUid == 0 /* root */) {
+                return true;
+            }
+            // Check if the package is present for the given profile.
+            final int packageUid = getUidForPackage(packageName,
+                    UserHandle.getUserId(callingUid));
+            if (packageUid < 0) {
+                return false;
+            }
+            // Check if the call for a package is coming from that package.
+            return UserHandle.isSameApp(callingUid, packageUid);
+        }
+
+        public boolean hasCallerBindPermissionOrBindWhiteListedLocked(String packageName) {
+            try {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.BIND_APPWIDGET, null);
+            } catch (SecurityException se) {
+                if (!isCallerBindAppWidgetWhiteListedLocked(packageName)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private boolean isCallerBindAppWidgetWhiteListedLocked(String packageName) {
+            final int userId = UserHandle.getCallingUserId();
+            final int packageUid = getUidForPackage(packageName, userId);
+            if (packageUid < 0) {
+                throw new IllegalArgumentException("No package " + packageName
+                        + " for user " + userId);
+            }
+            synchronized (mLock) {
+                ensureGroupStateLoadedLocked(userId);
+
+                Pair<Integer, String> packageId = Pair.create(userId, packageName);
+                if (mPackagesWithBindWidgetPermission.contains(packageId)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public boolean canAccessAppWidget(Widget widget, int uid, String packageName) {
+            if (isHostInPackageForUid(widget.host, uid, packageName)) {
+                // Apps hosting the AppWidget have access to it.
+                return true;
+            }
+            if (isProviderInPackageForUid(widget.provider, uid, packageName)) {
+                // Apps providing the AppWidget have access to it.
+                return true;
+            }
+            if (isHostAccessingProvider(widget.host, widget.provider, uid, packageName)) {
+                // Apps hosting the AppWidget get to bind to a remote view service in the provider.
+                return true;
+            }
+            if (mContext.checkCallingPermission(android.Manifest.permission.BIND_APPWIDGET)
+                    == PackageManager.PERMISSION_GRANTED) {
+                // Apps that can bind have access to all appWidgetIds.
+                return true;
+            }
+            return false;
+        }
+
+        private boolean isParentOrProfile(int parentId, int profileId) {
+            if (parentId == profileId) {
+                return true;
+            }
+            return getProfileParent(profileId) == parentId;
+        }
+
+        public boolean isProviderInCallerOrInProfileAndWhitelListed(String packageName,
+                int profileId) {
+            final int callerId = UserHandle.getCallingUserId();
+            if (profileId == callerId) {
+                return true;
+            }
+            final int parentId = getProfileParent(profileId);
+            if (parentId != callerId) {
+                return false;
+            }
+            return isProviderWhitelListed(packageName, profileId);
+        }
+
+        public boolean isProviderWhitelListed(String packageName, int profileId) {
+            DevicePolicyManagerInternal devicePolicyManager = LocalServices.getService(
+                    DevicePolicyManagerInternal.class);
+
+            // If the policy manager is not available on the device we deny it all.
+            if (devicePolicyManager == null) {
+                return false;
+            }
+
+            List<String> crossProfilePackages = devicePolicyManager
+                    .getCrossProfileWidgetProviders(profileId);
+
+            return crossProfilePackages.contains(packageName);
+        }
+
+        public int getProfileParent(int profileId) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                UserInfo parent = mUserManager.getProfileParent(profileId);
+                if (parent != null) {
+                    return parent.getUserHandle().getIdentifier();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return UNKNOWN_USER_ID;
+        }
+
+        public int getGroupParent(int profileId) {
+            final int parentId = mSecurityPolicy.getProfileParent(profileId);
+            return (parentId != UNKNOWN_USER_ID) ? parentId : profileId;
+        }
+
+        public boolean isHostInPackageForUid(Host host, int uid, String packageName) {
+            if (UserHandle.getAppId(uid) == Process.myUid()) {
+                // For a host that's in the system process, ignore the user id.
+                return UserHandle.isSameApp(host.id.uid, uid)
+                        && host.id.packageName.equals(packageName);
+            } else {
+                return host.id.uid == uid
+                        && host.id.packageName.equals(packageName);
+            }
+        }
+
+        public boolean isProviderInPackageForUid(Provider provider, int uid,
+                String packageName) {
+            // Packages providing the AppWidget have access to it.
+            return provider != null && provider.id.uid == uid
+                    && provider.id.componentName.getPackageName().equals(packageName);
+        }
+
+        public boolean isHostAccessingProvider(Host host, Provider provider, int uid,
+                String packageName) {
+            // The host creates a package context to bind to remote views service in the provider.
+            return host.id.uid == uid && provider != null
+                    && provider.id.componentName.getPackageName().equals(packageName);
+        }
+
+        private boolean isProfileEnabled(int profileId) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                UserInfo userInfo = mUserManager.getUserInfo(profileId);
+                if (userInfo == null || !userInfo.isEnabled()) {
+                    return false;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return true;
+        }
+    }
+
+    private static final class Provider {
+        ProviderId id;
+        AppWidgetProviderInfo info;
+        ArrayList<Widget> widgets = new ArrayList<>();
+        PendingIntent broadcast;
+        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+        int tag = TAG_UNDEFINED; // for use while saving state (the index)
+
+        public int getUserId() {
+            return UserHandle.getUserId(id.uid);
+        }
+
+        public boolean isInPackageForUser(String packageName, int userId) {
+            return getUserId() == userId
+                    && id.componentName.getPackageName().equals(packageName);
+        }
+
+        // is there an instance of this provider hosted by the given app?
+        public boolean hostedByPackageForUser(String packageName, int userId) {
+            final int N = widgets.size();
+            for (int i = 0; i < N; i++) {
+                Widget widget = widgets.get(i);
+                if (packageName.equals(widget.host.id.packageName)
+                        && widget.host.getUserId() == userId) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "Provider{" + id + (zombie ? " Z" : "") + '}';
+        }
+    }
+
+    private static final class ProviderId {
+        final int uid;
+        final ComponentName componentName;
+
+        private ProviderId(int uid, ComponentName componentName) {
+            this.uid = uid;
+            this.componentName = componentName;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            ProviderId other = (ProviderId) obj;
+            if (uid != other.uid)  {
+                return false;
+            }
+            if (componentName == null) {
+                if (other.componentName != null) {
+                    return false;
+                }
+            } else if (!componentName.equals(other.componentName)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = uid;
+            result = 31 * result + ((componentName != null)
+                    ? componentName.hashCode() : 0);
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "ProviderId{user:" + UserHandle.getUserId(uid) + ", app:"
+                    + UserHandle.getAppId(uid) + ", cmp:" + componentName + '}';
+        }
+    }
+
+    private static final class Host {
+        HostId id;
+        ArrayList<Widget> widgets = new ArrayList<>();
+        IAppWidgetHost callbacks;
+        boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+        int tag = TAG_UNDEFINED; // for use while saving state (the index)
+
+        public int getUserId() {
+            return UserHandle.getUserId(id.uid);
+        }
+
+        public boolean isInPackageForUser(String packageName, int userId) {
+            return getUserId() == userId && id.packageName.equals(packageName);
+        }
+
+        private boolean hostsPackageForUser(String pkg, int userId) {
+            final int N = widgets.size();
+            for (int i = 0; i < N; i++) {
+                Provider provider = widgets.get(i).provider;
+                if (provider != null && provider.getUserId() == userId && provider.info != null
+                        && pkg.equals(provider.info.provider.getPackageName())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "Host{" + id + (zombie ? " Z" : "") + '}';
+        }
+    }
+
+    private static final class HostId {
+        final int uid;
+        final int hostId;
+        final String packageName;
+
+        public HostId(int uid, int hostId, String packageName) {
+            this.uid = uid;
+            this.hostId = hostId;
+            this.packageName = packageName;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            HostId other = (HostId) obj;
+            if (uid != other.uid)  {
+                return false;
+            }
+            if (hostId != other.hostId) {
+                return false;
+            }
+            if (packageName == null) {
+                if (other.packageName != null) {
+                    return false;
+                }
+            } else if (!packageName.equals(other.packageName)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = uid;
+            result = 31 * result + hostId;
+            result = 31 * result + ((packageName != null)
+                    ? packageName.hashCode() : 0);
+            return result;
+        }
+
+        @Override
+        public String toString() {
+            return "HostId{user:" + UserHandle.getUserId(uid) + ", app:"
+                    + UserHandle.getAppId(uid) + ", hostId:" + hostId
+                    + ", pkg:" + packageName + '}';
+        }
+    }
+
+    private static final class Widget {
+        int appWidgetId;
+        int restoredId;  // tracking & remapping any restored state
+        Provider provider;
+        RemoteViews views;
+        Bundle options;
+        Host host;
+
+        @Override
+        public String toString() {
+            return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}';
+        }
+    }
+
+    /**
+     * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
+     * needs to be a static inner class since a reference to the ServiceConnection is held globally
+     * and may lead us to leak AppWidgetService instances (if there were more than one).
+     */
+    private static final class ServiceConnectionProxy implements ServiceConnection {
+        private final IRemoteViewsAdapterConnection mConnectionCb;
+
+        ServiceConnectionProxy(IBinder connectionCb) {
+            mConnectionCb = IRemoteViewsAdapterConnection.Stub
+                    .asInterface(connectionCb);
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                mConnectionCb.onServiceConnected(service);
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Error passing service interface", re);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            disconnect();
+        }
+
+        public void disconnect() {
+            try {
+                mConnectionCb.onServiceDisconnected();
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Error clearing service interface", re);
+            }
+        }
+    }
+
+    private class LoadedWidgetState {
+        final Widget widget;
+        final int hostTag;
+        final int providerTag;
+
+        public LoadedWidgetState(Widget widget, int hostTag, int providerTag) {
+            this.widget = widget;
+            this.hostTag = hostTag;
+            this.providerTag = providerTag;
+        }
+    }
+
+    private final class SaveStateRunnable implements Runnable {
+        final int mUserId;
+
+        public SaveStateRunnable(int userId) {
+            mUserId = userId;
+        }
+
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                ensureGroupStateLoadedLocked(mUserId);
+                saveStateLocked(mUserId);
+            }
+        }
+    }
+
+    /**
+     * This class encapsulates the backup and restore logic for a user group state.
+     */
+    private final class BackupRestoreController {
+        private static final String TAG = "BackupRestoreController";
+
+        private static final boolean DEBUG = true;
+
+        // Version of backed-up widget state.
+        private static final int WIDGET_STATE_VERSION = 2;
+
+        // We need to make sure to wipe the pre-restore widget state only once for
+        // a given package.  Keep track of what we've done so far here; the list is
+        // cleared at the start of every system restore pass, but preserved through
+        // any install-time restore operations.
+        private final HashSet<String> mPrunedApps = new HashSet<>();
+
+        private final HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider =
+                new HashMap<>();
+        private final HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost =
+                new HashMap<>();
+
+        public List<String> getWidgetParticipants(int userId) {
+            if (DEBUG) {
+                Slog.i(TAG, "Getting widget participants for user: " + userId);
+            }
+
+            HashSet<String> packages = new HashSet<>();
+            synchronized (mLock) {
+                final int N = mWidgets.size();
+                for (int i = 0; i < N; i++) {
+                    Widget widget = mWidgets.get(i);
+
+                    // Skip cross-user widgets.
+                    if (!isProviderAndHostInUser(widget, userId)) {
+                        continue;
+                    }
+
+                    packages.add(widget.host.id.packageName);
+                    Provider provider = widget.provider;
+                    if (provider != null) {
+                        packages.add(provider.id.componentName.getPackageName());
+                    }
+                }
+            }
+            return new ArrayList<>(packages);
+        }
+
+        public byte[] getWidgetState(String backedupPackage, int userId) {
+            if (DEBUG) {
+                Slog.i(TAG, "Getting widget state for user: " + userId);
+            }
+
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+            synchronized (mLock) {
+                // Preflight: if this app neither hosts nor provides any live widgets
+                // we have no work to do.
+                if (!packageNeedsWidgetBackupLocked(backedupPackage, userId)) {
+                    return null;
+                }
+
+                try {
+                    XmlSerializer out = new FastXmlSerializer();
+                    out.setOutput(stream, "utf-8");
+                    out.startDocument(null, true);
+                    out.startTag(null, "ws");      // widget state
+                    out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION));
+                    out.attribute(null, "pkg", backedupPackage);
+
+                    // Remember all the providers that are currently hosted or published
+                    // by this package: that is, all of the entities related to this app
+                    // which will need to be told about id remapping.
+                    int index = 0;
+                    int N = mProviders.size();
+                    for (int i = 0; i < N; i++) {
+                        Provider provider = mProviders.get(i);
+
+                        if (!provider.widgets.isEmpty()
+                                && (provider.isInPackageForUser(backedupPackage, userId)
+                                || provider.hostedByPackageForUser(backedupPackage, userId))) {
+                            provider.tag = index;
+                            serializeProvider(out, provider);
+                            index++;
+                        }
+                    }
+
+                    N = mHosts.size();
+                    index = 0;
+                    for (int i = 0; i < N; i++) {
+                        Host host = mHosts.get(i);
+
+                        if (!host.widgets.isEmpty()
+                                && (host.isInPackageForUser(backedupPackage, userId)
+                                || host.hostsPackageForUser(backedupPackage, userId))) {
+                            host.tag = index;
+                            serializeHost(out, host);
+                            index++;
+                        }
+                    }
+
+                    // All widget instances involving this package,
+                    // either as host or as provider
+                    N = mWidgets.size();
+                    for (int i = 0; i < N; i++) {
+                        Widget widget = mWidgets.get(i);
+
+                        Provider provider = widget.provider;
+                        if (widget.host.isInPackageForUser(backedupPackage, userId)
+                                || (provider != null
+                                &&  provider.isInPackageForUser(backedupPackage, userId))) {
+                            serializeAppWidget(out, widget);
+                        }
+                    }
+
+                    out.endTag(null, "ws");
+                    out.endDocument();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Unable to save widget state for " + backedupPackage);
+                    return null;
+                }
+            }
+
+            return stream.toByteArray();
+        }
+
+        public void restoreStarting(int userId) {
+            if (DEBUG) {
+                Slog.i(TAG, "Restore starting for user: " + userId);
+            }
+
+            synchronized (mLock) {
+                // We're starting a new "system" restore operation, so any widget restore
+                // state that we see from here on is intended to replace the current
+                // widget configuration of any/all of the affected apps.
+                mPrunedApps.clear();
+                mUpdatesByProvider.clear();
+                mUpdatesByHost.clear();
+            }
+        }
+
+        public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+            if (DEBUG) {
+                Slog.i(TAG, "Restoring widget state for user:" + userId
+                        + " package: " + packageName);
+            }
+
+            ByteArrayInputStream stream = new ByteArrayInputStream(restoredState);
+            try {
+                // Providers mentioned in the widget dataset by ordinal
+                ArrayList<Provider> restoredProviders = new ArrayList<>();
+
+                // Hosts mentioned in the widget dataset by ordinal
+                ArrayList<Host> restoredHosts = new ArrayList<>();
+
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(stream, null);
+
+                synchronized (mLock) {
+                    int type;
+                    do {
+                        type = parser.next();
+                        if (type == XmlPullParser.START_TAG) {
+                            final String tag = parser.getName();
+                            if ("ws".equals(tag)) {
+                                String version = parser.getAttributeValue(null, "version");
+
+                                final int versionNumber = Integer.parseInt(version);
+                                if (versionNumber > WIDGET_STATE_VERSION) {
+                                    Slog.w(TAG, "Unable to process state version " + version);
+                                    return;
+                                }
+
+                                // TODO: fix up w.r.t. canonical vs current package names
+                                String pkg = parser.getAttributeValue(null, "pkg");
+                                if (!packageName.equals(pkg)) {
+                                    Slog.w(TAG, "Package mismatch in ws");
+                                    return;
+                                }
+                            } else if ("p".equals(tag)) {
+                                String pkg = parser.getAttributeValue(null, "pkg");
+                                String cl = parser.getAttributeValue(null, "cl");
+
+                                // hostedProviders index will match 'p' attribute in widget's
+                                // entry in the xml file being restored
+                                // If there's no live entry for this provider, add an inactive one
+                                // so that widget IDs referring to them can be properly allocated
+
+                                // Backup and resotre only for the parent profile.
+                                ComponentName componentName = new ComponentName(pkg, cl);
+
+                                Provider p = findProviderLocked(componentName, userId);
+                                if (p == null) {
+                                    p = new Provider();
+                                    p.id = new ProviderId(UNKNOWN_UID, componentName);
+                                    p.info = new AppWidgetProviderInfo();
+                                    p.info.provider = componentName;
+                                    p.zombie = true;
+                                    mProviders.add(p);
+                                }
+                                if (DEBUG) {
+                                    Slog.i(TAG, "   provider " + p.id);
+                                }
+                                restoredProviders.add(p);
+                            } else if ("h".equals(tag)) {
+                                // The host app may not yet exist on the device.  If it's here we
+                                // just use the existing Host entry, otherwise we create a
+                                // placeholder whose uid will be fixed up at PACKAGE_ADDED time.
+                                String pkg = parser.getAttributeValue(null, "pkg");
+
+                                final int uid = getUidForPackage(pkg, userId);
+                                final int hostId = Integer.parseInt(
+                                        parser.getAttributeValue(null, "id"), 16);
+
+                                HostId id = new HostId(uid, hostId, pkg);
+                                Host h = lookupOrAddHostLocked(id);
+                                restoredHosts.add(h);
+
+                                if (DEBUG) {
+                                    Slog.i(TAG, "   host[" + restoredHosts.size()
+                                            + "]: {" + h.id + "}");
+                                }
+                            } else if ("g".equals(tag)) {
+                                int restoredId = Integer.parseInt(
+                                        parser.getAttributeValue(null, "id"), 16);
+                                int hostIndex = Integer.parseInt(
+                                        parser.getAttributeValue(null, "h"), 16);
+                                Host host = restoredHosts.get(hostIndex);
+                                Provider p = null;
+                                String prov = parser.getAttributeValue(null, "p");
+                                if (prov != null) {
+                                    // could have been null if the app had allocated an id
+                                    // but not yet established a binding under that id
+                                    int which = Integer.parseInt(prov, 16);
+                                    p = restoredProviders.get(which);
+                                }
+
+                                // We'll be restoring widget state for both the host and
+                                // provider sides of this widget ID, so make sure we are
+                                // beginning from a clean slate on both fronts.
+                                pruneWidgetStateLocked(host.id.packageName, userId);
+                                if (p != null) {
+                                    pruneWidgetStateLocked(p.id.componentName.getPackageName(),
+                                            userId);
+                                }
+
+                                // Have we heard about this ancestral widget instance before?
+                                Widget id = findRestoredWidgetLocked(restoredId, host, p);
+                                if (id == null) {
+                                    id = new Widget();
+                                    id.appWidgetId = incrementAndGetAppWidgetIdLocked(userId);
+                                    id.restoredId = restoredId;
+                                    id.options = parseWidgetIdOptions(parser);
+                                    id.host = host;
+                                    id.host.widgets.add(id);
+                                    id.provider = p;
+                                    if (id.provider != null) {
+                                        id.provider.widgets.add(id);
+                                    }
+                                    if (DEBUG) {
+                                        Slog.i(TAG, "New restored id " + restoredId
+                                                + " now " + id);
+                                    }
+                                    mWidgets.add(id);
+                                }
+                                if (id.provider.info != null) {
+                                    stashProviderRestoreUpdateLocked(id.provider,
+                                            restoredId, id.appWidgetId);
+                                } else {
+                                    Slog.w(TAG, "Missing provider for restored widget " + id);
+                                }
+                                stashHostRestoreUpdateLocked(id.host, restoredId, id.appWidgetId);
+
+                                if (DEBUG) {
+                                    Slog.i(TAG, "   instance: " + restoredId
+                                            + " -> " + id.appWidgetId
+                                            + " :: p=" + id.provider);
+                                }
+                            }
+                        }
+                    } while (type != XmlPullParser.END_DOCUMENT);
+
+                    // We've updated our own bookkeeping.  We'll need to notify the hosts and
+                    // providers about the changes, but we can't do that yet because the restore
+                    // target is not necessarily fully live at this moment.  Set aside the
+                    // information for now; the backup manager will call us once more at the
+                    // end of the process when all of the targets are in a known state, and we
+                    // will update at that point.
+                }
+            } catch (XmlPullParserException | IOException e) {
+                Slog.w(TAG, "Unable to restore widget state for " + packageName);
+            } finally {
+                saveGroupStateAsync(userId);
+            }
+        }
+
+        // Called once following the conclusion of a restore operation.  This is when we
+        // send out updates to apps involved in widget-state restore telling them about
+        // the new widget ID space.
+        public void restoreFinished(int userId) {
+            if (DEBUG) {
+                Slog.i(TAG, "restoreFinished for " + userId);
+            }
+
+            final UserHandle userHandle = new UserHandle(userId);
+            synchronized (mLock) {
+                // Build the providers' broadcasts and send them off
+                Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
+                        = mUpdatesByProvider.entrySet();
+                for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
+                    // For each provider there's a list of affected IDs
+                    Provider provider = e.getKey();
+                    ArrayList<RestoreUpdateRecord> updates = e.getValue();
+                    final int pending = countPendingUpdates(updates);
+                    if (DEBUG) {
+                        Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+                    }
+                    if (pending > 0) {
+                        int[] oldIds = new int[pending];
+                        int[] newIds = new int[pending];
+                        final int N = updates.size();
+                        int nextPending = 0;
+                        for (int i = 0; i < N; i++) {
+                            RestoreUpdateRecord r = updates.get(i);
+                            if (!r.notified) {
+                                r.notified = true;
+                                oldIds[nextPending] = r.oldId;
+                                newIds[nextPending] = r.newId;
+                                nextPending++;
+                                if (DEBUG) {
+                                    Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
+                                }
+                            }
+                        }
+                        sendWidgetRestoreBroadcastLocked(
+                                AppWidgetManager.ACTION_APPWIDGET_RESTORED,
+                                provider, null, oldIds, newIds, userHandle);
+                    }
+                }
+
+                // same thing per host
+                Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
+                        = mUpdatesByHost.entrySet();
+                for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
+                    Host host = e.getKey();
+                    if (host.id.uid != UNKNOWN_UID) {
+                        ArrayList<RestoreUpdateRecord> updates = e.getValue();
+                        final int pending = countPendingUpdates(updates);
+                        if (DEBUG) {
+                            Slog.i(TAG, "Host " + host + " pending: " + pending);
+                        }
+                        if (pending > 0) {
+                            int[] oldIds = new int[pending];
+                            int[] newIds = new int[pending];
+                            final int N = updates.size();
+                            int nextPending = 0;
+                            for (int i = 0; i < N; i++) {
+                                RestoreUpdateRecord r = updates.get(i);
+                                if (!r.notified) {
+                                    r.notified = true;
+                                    oldIds[nextPending] = r.oldId;
+                                    newIds[nextPending] = r.newId;
+                                    nextPending++;
+                                    if (DEBUG) {
+                                        Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
+                                    }
+                                }
+                            }
+                            sendWidgetRestoreBroadcastLocked(
+                                    AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
+                                    null, host, oldIds, newIds, userHandle);
+                        }
+                    }
+                }
+            }
+        }
+
+        private Provider findProviderLocked(ComponentName componentName, int userId) {
+            final int providerCount = mProviders.size();
+            for (int i = 0; i < providerCount; i++) {
+                Provider provider = mProviders.get(i);
+                if (provider.getUserId() == userId
+                        && provider.id.componentName.equals(componentName)) {
+                    return provider;
+                }
+            }
+            return null;
+        }
+
+        private Widget findRestoredWidgetLocked(int restoredId, Host host, Provider p) {
+            if (DEBUG) {
+                Slog.i(TAG, "Find restored widget: id=" + restoredId
+                        + " host=" + host + " provider=" + p);
+            }
+
+            if (p == null || host == null) {
+                return null;
+            }
+
+            final int N = mWidgets.size();
+            for (int i = 0; i < N; i++) {
+                Widget widget = mWidgets.get(i);
+                if (widget.restoredId == restoredId
+                        && widget.host.id.equals(host.id)
+                        && widget.provider.id.equals(p.id)) {
+                    if (DEBUG) {
+                        Slog.i(TAG, "   Found at " + i + " : " + widget);
+                    }
+                    return widget;
+                }
+            }
+            return null;
+        }
+
+        private boolean packageNeedsWidgetBackupLocked(String packageName, int userId) {
+            int N = mWidgets.size();
+            for (int i = 0; i < N; i++) {
+                Widget widget = mWidgets.get(i);
+
+                // Skip cross-user widgets.
+                if (!isProviderAndHostInUser(widget, userId)) {
+                    continue;
+                }
+
+                if (widget.host.isInPackageForUser(packageName, userId)) {
+                    // this package is hosting widgets, so it knows widget IDs.
+                    return true;
+                }
+
+                Provider provider = widget.provider;
+                if (provider != null && provider.isInPackageForUser(packageName, userId)) {
+                    // someone is hosting this app's widgets, so it knows widget IDs.
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private void stashProviderRestoreUpdateLocked(Provider provider, int oldId, int newId) {
+            ArrayList<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider);
+            if (r == null) {
+                r = new ArrayList<>();
+                mUpdatesByProvider.put(provider, r);
+            } else {
+                // don't duplicate
+                if (alreadyStashed(r, oldId, newId)) {
+                    if (DEBUG) {
+                        Slog.i(TAG, "ID remap " + oldId + " -> " + newId
+                                + " already stashed for " + provider);
+                    }
+                    return;
+                }
+            }
+            r.add(new RestoreUpdateRecord(oldId, newId));
+        }
+
+        private boolean alreadyStashed(ArrayList<RestoreUpdateRecord> stash,
+                final int oldId, final int newId) {
+            final int N = stash.size();
+            for (int i = 0; i < N; i++) {
+                RestoreUpdateRecord r = stash.get(i);
+                if (r.oldId == oldId && r.newId == newId) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private void stashHostRestoreUpdateLocked(Host host, int oldId, int newId) {
+            ArrayList<RestoreUpdateRecord> r = mUpdatesByHost.get(host);
+            if (r == null) {
+                r = new ArrayList<>();
+                mUpdatesByHost.put(host, r);
+            } else {
+                if (alreadyStashed(r, oldId, newId)) {
+                    if (DEBUG) {
+                        Slog.i(TAG, "ID remap " + oldId + " -> " + newId
+                                + " already stashed for " + host);
+                    }
+                    return;
+                }
+            }
+            r.add(new RestoreUpdateRecord(oldId, newId));
+        }
+
+        private void sendWidgetRestoreBroadcastLocked(String action, Provider provider,
+                Host host, int[] oldIds, int[] newIds, UserHandle userHandle) {
+            Intent intent = new Intent(action);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds);
+            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds);
+            if (provider != null) {
+                intent.setComponent(provider.info.provider);
+                sendBroadcastAsUser(intent, userHandle);
+            }
+            if (host != null) {
+                intent.setComponent(null);
+                intent.setPackage(host.id.packageName);
+                intent.putExtra(AppWidgetManager.EXTRA_HOST_ID, host.id.hostId);
+                sendBroadcastAsUser(intent, userHandle);
+            }
+        }
+
+        // We're restoring widget state for 'pkg', so we start by wiping (a) all widget
+        // instances that are hosted by that app, and (b) all instances in other hosts
+        // for which 'pkg' is the provider.  We assume that we'll be restoring all of
+        // these hosts & providers, so will be reconstructing a correct live state.
+        private void pruneWidgetStateLocked(String pkg, int userId) {
+            if (!mPrunedApps.contains(pkg)) {
+                if (DEBUG) {
+                    Slog.i(TAG, "pruning widget state for restoring package " + pkg);
+                }
+                for (int i = mWidgets.size() - 1; i >= 0; i--) {
+                    Widget widget = mWidgets.get(i);
+
+                    Host host = widget.host;
+                    Provider provider = widget.provider;
+
+                    if (host.hostsPackageForUser(pkg, userId)
+                            || (provider != null && provider.isInPackageForUser(pkg, userId))) {
+                        // 'pkg' is either the host or the provider for this instances,
+                        // so we tear it down in anticipation of it (possibly) being
+                        // reconstructed due to the restore
+                        host.widgets.remove(widget);
+                        provider.widgets.remove(widget);
+                        unbindAppWidgetRemoteViewsServicesLocked(widget);
+                        mWidgets.remove(i);
+                    }
+                }
+                mPrunedApps.add(pkg);
+            } else {
+                if (DEBUG) {
+                    Slog.i(TAG, "already pruned " + pkg + ", continuing normally");
+                }
+            }
+        }
+
+        private boolean isProviderAndHostInUser(Widget widget, int userId) {
+            // Backup only widgets hosted or provided by the owner profile.
+            return widget.host.getUserId() == userId && (widget.provider == null
+                    || widget.provider.getUserId() == userId);
+        }
+
+        private Bundle parseWidgetIdOptions(XmlPullParser parser) {
+            Bundle options = new Bundle();
+            String minWidthString = parser.getAttributeValue(null, "min_width");
+            if (minWidthString != null) {
+                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+                        Integer.parseInt(minWidthString, 16));
+            }
+            String minHeightString = parser.getAttributeValue(null, "min_height");
+            if (minHeightString != null) {
+                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+                        Integer.parseInt(minHeightString, 16));
+            }
+            String maxWidthString = parser.getAttributeValue(null, "max_width");
+            if (maxWidthString != null) {
+                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+                        Integer.parseInt(maxWidthString, 16));
+            }
+            String maxHeightString = parser.getAttributeValue(null, "max_height");
+            if (maxHeightString != null) {
+                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+                        Integer.parseInt(maxHeightString, 16));
+            }
+            String categoryString = parser.getAttributeValue(null, "host_category");
+            if (categoryString != null) {
+                options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                        Integer.parseInt(categoryString, 16));
+            }
+            return options;
+        }
+
+        private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
+            int pending = 0;
+            final int N = updates.size();
+            for (int i = 0; i < N; i++) {
+                RestoreUpdateRecord r = updates.get(i);
+                if (!r.notified) {
+                    pending++;
+                }
+            }
+            return pending;
+        }
+
+        // Accumulate a list of updates that affect the given provider for a final
+        // coalesced notification broadcast once restore is over.
+        private class RestoreUpdateRecord {
+            public int oldId;
+            public int newId;
+            public boolean notified;
+
+            public RestoreUpdateRecord(int theOldId, int theNewId) {
+                oldId = theOldId;
+                newId = theNewId;
+                notified = false;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d434d7a..c77c5b2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1266,10 +1266,13 @@
         ArrayList<FullBackupEntry> schedule = null;
         synchronized (mQueueLock) {
             if (mFullBackupScheduleFile.exists()) {
+                FileInputStream fstream = null;
+                BufferedInputStream bufStream = null;
+                DataInputStream in = null;
                 try {
-                    FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
-                    BufferedInputStream bufStream = new BufferedInputStream(fstream);
-                    DataInputStream in = new DataInputStream(bufStream);
+                    fstream = new FileInputStream(mFullBackupScheduleFile);
+                    bufStream = new BufferedInputStream(fstream);
+                    in = new DataInputStream(bufStream);
 
                     int version = in.readInt();
                     if (version != SCHEDULE_FILE_VERSION) {
@@ -1289,6 +1292,10 @@
                     Slog.e(TAG, "Unable to read backup schedule", e);
                     mFullBackupScheduleFile.delete();
                     schedule = null;
+                } finally {
+                    IoUtils.closeQuietly(in);
+                    IoUtils.closeQuietly(bufStream);
+                    IoUtils.closeQuietly(fstream);
                 }
             }
 
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index d8ee8a1..bffbb4c2 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -36,8 +36,10 @@
 import com.android.server.input.InputManagerService.WiredAccessoryCallbacks;
 import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT;
 import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT;
+import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT;
 import static com.android.server.input.InputManagerService.SW_HEADPHONE_INSERT_BIT;
 import static com.android.server.input.InputManagerService.SW_MICROPHONE_INSERT_BIT;
+import static com.android.server.input.InputManagerService.SW_LINEOUT_INSERT_BIT;
 
 import java.io.File;
 import java.io.FileReader;
@@ -60,9 +62,10 @@
     private static final int BIT_USB_HEADSET_ANLG = (1 << 2);
     private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
     private static final int BIT_HDMI_AUDIO = (1 << 4);
+    private static final int BIT_LINEOUT = (1 << 5);
     private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
                                                    BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL|
-                                                   BIT_HDMI_AUDIO);
+                                                   BIT_HDMI_AUDIO|BIT_LINEOUT);
 
     private static final String NAME_H2W = "h2w";
     private static final String NAME_USB_AUDIO = "usb_audio";
@@ -108,8 +111,11 @@
             if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_MICROPHONE_INSERT) == 1) {
                 switchValues |= SW_MICROPHONE_INSERT_BIT;
             }
+            if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_LINEOUT_INSERT) == 1) {
+                switchValues |= SW_LINEOUT_INSERT_BIT;
+            }
             notifyWiredAccessoryChanged(0, switchValues,
-                    SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT);
+                    SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT);
         }
 
         mObserver.init();
@@ -124,7 +130,8 @@
         synchronized (mLock) {
             int headset;
             mSwitchValues = (mSwitchValues & ~switchMask) | switchValues;
-            switch (mSwitchValues & (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT)) {
+            switch (mSwitchValues &
+                (SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_LINEOUT_INSERT_BIT)) {
                 case 0:
                     headset = 0;
                     break;
@@ -133,6 +140,10 @@
                     headset = BIT_HEADSET_NO_MIC;
                     break;
 
+                case SW_LINEOUT_INSERT_BIT:
+                    headset = BIT_LINEOUT;
+                    break;
+
                 case SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT:
                     headset = BIT_HEADSET;
                     break;
@@ -146,7 +157,8 @@
                     break;
             }
 
-            updateLocked(NAME_H2W, (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)) | headset);
+            updateLocked(NAME_H2W,
+                (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) | headset);
         }
     }
 
@@ -174,7 +186,7 @@
         int headsetState = newState & SUPPORTED_HEADSETS;
         int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
         int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
-        int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC);
+        int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT);
         boolean h2wStateChange = true;
         boolean usbStateChange = true;
         if (LOG) Slog.v(TAG, "newName=" + newName
@@ -190,7 +202,7 @@
         // reject all suspect transitions: only accept state changes from:
         // - a: 0 headset to 1 headset
         // - b: 1 headset to 0 headset
-        if (h2w_headset == (BIT_HEADSET | BIT_HEADSET_NO_MIC)) {
+        if (h2w_headset == (BIT_HEADSET | BIT_HEADSET_NO_MIC | BIT_LINEOUT)) {
             Log.e(TAG, "Invalid combination, unsetting h2w flag");
             h2wStateChange = false;
         }
@@ -261,6 +273,8 @@
                 inDevice = AudioManager.DEVICE_IN_WIRED_HEADSET;
             } else if (headset == BIT_HEADSET_NO_MIC){
                 outDevice = AudioManager.DEVICE_OUT_WIRED_HEADPHONE;
+            } else if (headset == BIT_LINEOUT){
+                outDevice = AudioManager.DEVICE_OUT_LINE;
             } else if (headset == BIT_USB_HEADSET_ANLG) {
                 outDevice = AudioManager.DEVICE_OUT_ANLG_DOCK_HEADSET;
             } else if (headset == BIT_USB_HEADSET_DGTL) {
@@ -345,7 +359,7 @@
 
             // Monitor h2w
             if (!mUseDevInputEventForAudioJack) {
-                uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC);
+                uei = new UEventInfo(NAME_H2W, BIT_HEADSET, BIT_HEADSET_NO_MIC, BIT_LINEOUT);
                 if (uei.checkSwitchExists()) {
                     retVal.add(uei);
                 } else {
@@ -354,7 +368,7 @@
             }
 
             // Monitor USB
-            uei = new UEventInfo(NAME_USB_AUDIO, BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL);
+            uei = new UEventInfo(NAME_USB_AUDIO, BIT_USB_HEADSET_ANLG, BIT_USB_HEADSET_DGTL, 0);
             if (uei.checkSwitchExists()) {
                 retVal.add(uei);
             } else {
@@ -369,11 +383,11 @@
             //
             // If the kernel does not have an "hdmi_audio" switch, just fall back on the older
             // "hdmi" switch instead.
-            uei = new UEventInfo(NAME_HDMI_AUDIO, BIT_HDMI_AUDIO, 0);
+            uei = new UEventInfo(NAME_HDMI_AUDIO, BIT_HDMI_AUDIO, 0, 0);
             if (uei.checkSwitchExists()) {
                 retVal.add(uei);
             } else {
-                uei = new UEventInfo(NAME_HDMI, BIT_HDMI_AUDIO, 0);
+                uei = new UEventInfo(NAME_HDMI, BIT_HDMI_AUDIO, 0, 0);
                 if (uei.checkSwitchExists()) {
                     retVal.add(uei);
                 } else {
@@ -414,11 +428,13 @@
             private final String mDevName;
             private final int mState1Bits;
             private final int mState2Bits;
+            private final int mStateNbits;
 
-            public UEventInfo(String devName, int state1Bits, int state2Bits) {
+            public UEventInfo(String devName, int state1Bits, int state2Bits, int stateNbits) {
                 mDevName = devName;
                 mState1Bits = state1Bits;
                 mState2Bits = state2Bits;
+                mStateNbits = stateNbits;
             }
 
             public String getDevName() { return mDevName; }
@@ -437,9 +453,10 @@
             }
 
             public int computeNewHeadsetState(int headsetState, int switchState) {
-                int preserveMask = ~(mState1Bits | mState2Bits);
+                int preserveMask = ~(mState1Bits | mState2Bits | mStateNbits);
                 int setBits = ((switchState == 1) ? mState1Bits :
-                              ((switchState == 2) ? mState2Bits : 0));
+                              ((switchState == 2) ? mState2Bits :
+                              ((switchState == mStateNbits) ? mStateNbits : 0)));
 
                 return ((headsetState & preserveMask) | setBits);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 29fd984..ae2ef06 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13831,6 +13831,10 @@
             }
         } else if ("system".equals(componentProcessName)) {
             result = true;
+        } else if (UserHandle.isSameApp(aInfo.uid, Process.PHONE_UID)
+                && (flags & ServiceInfo.FLAG_SINGLE_USER) != 0) {
+            // Phone app is allowed to export singleuser providers.
+            result = true;
         } else {
             // App with pre-defined UID, check if it's a persistent app
             result = (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b9acea5..7d3738c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -523,6 +523,17 @@
         return -1;
     }
 
+    private void resizeVirtualDisplayInternal(IBinder appToken,
+            int width, int height, int densityDpi) {
+        synchronized (mSyncRoot) {
+            if (mVirtualDisplayAdapter == null) {
+                return;
+            }
+
+            mVirtualDisplayAdapter.resizeVirtualDisplayLocked(appToken, width, height, densityDpi);
+        }
+    }
+
     private void setVirtualDisplaySurfaceInternal(IBinder appToken, Surface surface) {
         synchronized (mSyncRoot) {
             if (mVirtualDisplayAdapter == null) {
@@ -1304,6 +1315,17 @@
         }
 
         @Override // Binder call
+        public void resizeVirtualDisplay(IVirtualDisplayCallbacks callbacks,
+                int width, int height, int densityDpi) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                resizeVirtualDisplayInternal(callbacks.asBinder(), width, height, densityDpi);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void setVirtualDisplaySurface(IVirtualDisplayCallbacks callbacks, Surface surface) {
             final long token = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 1032081..0ebd2de 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -83,6 +83,15 @@
         return device;
     }
 
+    public void resizeVirtualDisplayLocked(IBinder appToken,
+            int width, int height, int densityDpi) {
+        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+        if (device != null) {
+            device.resizeLocked(width, height, densityDpi);
+        }
+    }
+
+
     public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
         if (device != null) {
@@ -122,20 +131,24 @@
     }
 
     private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
+        private static final int PENDING_SURFACE_CHANGE = 0x01;
+        private static final int PENDING_RESIZE = 0x02;
+
         private final IBinder mAppToken;
         private final int mOwnerUid;
         final String mOwnerPackageName;
         final String mName;
-        private final int mWidth;
-        private final int mHeight;
-        private final int mDensityDpi;
         private final int mFlags;
         private final Callbacks mCallbacks;
 
+        private int mWidth;
+        private int mHeight;
+        private int mDensityDpi;
         private Surface mSurface;
         private DisplayDeviceInfo mInfo;
-        private int mState;
+        private int mDisplayState;
         private boolean mStopped;
+        private int mPendingChanges;
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName,
@@ -152,7 +165,8 @@
             mSurface = surface;
             mFlags = flags;
             mCallbacks = callbacks;
-            mState = Display.STATE_UNKNOWN;
+            mDisplayState = Display.STATE_UNKNOWN;
+            mPendingChanges |= PENDING_SURFACE_CHANGE;
         }
 
         @Override
@@ -175,8 +189,8 @@
 
         @Override
         public void requestDisplayStateLocked(int state) {
-            if (state != mState) {
-                mState = state;
+            if (state != mDisplayState) {
+                mDisplayState = state;
                 if (state == Display.STATE_OFF) {
                     mCallbacks.dispatchDisplayPaused();
                 } else {
@@ -187,7 +201,13 @@
 
         @Override
         public void performTraversalInTransactionLocked() {
-            setSurfaceInTransactionLocked(mSurface);
+            if ((mPendingChanges & PENDING_RESIZE) != 0) {
+                SurfaceControl.setDisplaySize(getDisplayTokenLocked(), mWidth, mHeight);
+            }
+            if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) {
+                setSurfaceInTransactionLocked(mSurface);
+            }
+            mPendingChanges = 0;
         }
 
         public void setSurfaceLocked(Surface surface) {
@@ -198,6 +218,19 @@
                 sendTraversalRequestLocked();
                 mSurface = surface;
                 mInfo = null;
+                mPendingChanges |= PENDING_SURFACE_CHANGE;
+            }
+        }
+
+        public void resizeLocked(int width, int height, int densityDpi) {
+            if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) {
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+                sendTraversalRequestLocked();
+                mWidth = width;
+                mHeight = height;
+                mDensityDpi = densityDpi;
+                mInfo = null;
+                mPendingChanges |= PENDING_RESIZE;
             }
         }
 
@@ -210,7 +243,7 @@
         public void dumpLocked(PrintWriter pw) {
             super.dumpLocked(pw);
             pw.println("mFlags=" + mFlags);
-            pw.println("mState=" + Display.stateToString(mState));
+            pw.println("mDisplayState=" + Display.stateToString(mDisplayState));
             pw.println("mStopped=" + mStopped);
         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 70d108a..b36c176 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -165,6 +165,11 @@
     @ServiceThreadOnly
     void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
         assertRunOnServiceThread();
+        ActiveSource active = getActiveSource();
+        if (active.isValid() && targetAddress == active.logicalAddress) {
+            invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
+            return;
+        }
         if (targetAddress == Constants.ADDR_INTERNAL) {
             handleSelectInternalSource();
             // Switching to internal source is always successful even when CEC control is disabled.
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fe55b01..edadf6d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -207,10 +207,10 @@
     private List<HdmiPortInfo> mPortInfo;
 
     // Map from path(physical address) to port ID.
-    private final SparseIntArray mPortIdMap = new SparseIntArray();
+    private UnmodifiableSparseIntArray mPortIdMap;
 
     // Map from port ID to HdmiPortInfo.
-    private final SparseArray<HdmiPortInfo> mPortInfoMap = new SparseArray<>();
+    private UnmodifiableSparseArray<HdmiPortInfo> mPortInfoMap;
 
     private HdmiCecMessageValidator mMessageValidator;
 
@@ -360,10 +360,14 @@
             return;
         }
 
+        SparseArray<HdmiPortInfo> portInfoMap = new SparseArray<>();
+        SparseIntArray portIdMap = new SparseIntArray();
         for (HdmiPortInfo info : cecPortInfo) {
-            mPortIdMap.put(info.getAddress(), info.getId());
-            mPortInfoMap.put(info.getId(), info);
+            portIdMap.put(info.getAddress(), info.getId());
+            portInfoMap.put(info.getId(), info);
         }
+        mPortIdMap = new UnmodifiableSparseIntArray(portIdMap);
+        mPortInfoMap = new UnmodifiableSparseArray<>(portInfoMap);
 
         if (mMhlController == null) {
             mPortInfo = Collections.unmodifiableList(Arrays.asList(cecPortInfo));
@@ -397,9 +401,7 @@
      * @param portId HDMI port id
      * @return {@link HdmiPortInfo} for the given port
      */
-    @ServiceThreadOnly
     HdmiPortInfo getPortInfo(int portId) {
-        assertRunOnServiceThread();
         return mPortInfoMap.get(portId, null);
     }
 
@@ -407,9 +409,7 @@
      * Returns the routing path (physical address) of the HDMI port for the given
      * port id.
      */
-    @ServiceThreadOnly
     int portIdToPath(int portId) {
-        assertRunOnServiceThread();
         HdmiPortInfo portInfo = getPortInfo(portId);
         if (portInfo == null) {
             Slog.e(TAG, "Cannot find the port info: " + portId);
@@ -424,16 +424,12 @@
      * the port id to be returned is the ID associated with the port address
      * 0x1000 (1.0.0.0) which is the topmost path of the given routing path.
      */
-    @ServiceThreadOnly
     int pathToPortId(int path) {
-        assertRunOnServiceThread();
         int portAddress = path & Constants.ROUTING_PATH_TOP_MASK;
         return mPortIdMap.get(portAddress, Constants.INVALID_PORT_ID);
     }
 
-    @ServiceThreadOnly
     boolean isValidPortId(int portId) {
-        assertRunOnServiceThread();
         return getPortInfo(portId) != null;
     }
 
@@ -490,9 +486,7 @@
     /**
      * Whether a device of the specified physical address is connected to ARC enabled port.
      */
-    @ServiceThreadOnly
     boolean isConnectedToArcPort(int physicalAddress) {
-        assertRunOnServiceThread();
         int portId = mPortIdMap.get(physicalAddress);
         if (portId != Constants.INVALID_PORT_ID) {
             return mPortInfoMap.get(portId).isArcSupported();
diff --git a/services/core/java/com/android/server/hdmi/UnmodifiableSparseArray.java b/services/core/java/com/android/server/hdmi/UnmodifiableSparseArray.java
new file mode 100644
index 0000000..5c0a360
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/UnmodifiableSparseArray.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.util.SparseArray;
+
+/**
+ * Unmodifiable version of {@link SparseArray}.
+ */
+final class UnmodifiableSparseArray<E> {
+    private static final String TAG = "ImmutableSparseArray";
+
+    private final SparseArray<E> mArray;
+
+    public UnmodifiableSparseArray(SparseArray<E> array) {
+       mArray = array;
+    }
+
+    public int size() {
+        return mArray.size();
+    }
+
+    public E get(int key) {
+        return mArray.get(key);
+    }
+
+    public E get(int key, E valueIfKeyNotFound) {
+        return mArray.get(key, valueIfKeyNotFound);
+    }
+
+    public int keyAt(int index) {
+        return mArray.keyAt(index);
+    }
+
+    public E valueAt(int index) {
+        return mArray.valueAt(index);
+    }
+
+    public int indexOfValue(E value) {
+        return mArray.indexOfValue(value);
+    }
+
+    @Override
+    public String toString() {
+        return mArray.toString();
+    }
+}
+
diff --git a/services/core/java/com/android/server/hdmi/UnmodifiableSparseIntArray.java b/services/core/java/com/android/server/hdmi/UnmodifiableSparseIntArray.java
new file mode 100644
index 0000000..cada855
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/UnmodifiableSparseIntArray.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.util.SparseIntArray;
+
+/**
+ * Unmodifiable version of {@link SparseIntArray}.
+ */
+final class UnmodifiableSparseIntArray {
+    private static final String TAG = "ImmutableSparseIntArray";
+
+    private final SparseIntArray mArray;
+
+    public UnmodifiableSparseIntArray(SparseIntArray array) {
+        mArray = array;
+    }
+
+    public int size() {
+        return mArray.size();
+    }
+
+    public int get(int key) {
+        return mArray.get(key);
+    }
+
+    public int get(int key, int valueIfKeyNotFound) {
+        return mArray.get(key, valueIfKeyNotFound);
+    }
+
+    public int keyAt(int index) {
+        return mArray.keyAt(index);
+    }
+
+    public int valueAt(int index) {
+        return mArray.valueAt(index);
+    }
+
+    public int indexOfValue(int value) {
+        return mArray.indexOfValue(value);
+    }
+
+    @Override
+    public String toString() {
+        return mArray.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 0f5805c..7c1681c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -235,6 +235,9 @@
     /** Switch code: Microphone.  When set, microphone is inserted. */
     public static final int SW_MICROPHONE_INSERT = 0x04;
 
+    /** Switch code: Line out.  When set, Line out (hi-Z) is inserted. */
+    public static final int SW_LINEOUT_INSERT = 0x06;
+
     /** Switch code: Headphone/Microphone Jack.  When set, something is inserted. */
     public static final int SW_JACK_PHYSICAL_INSERT = 0x07;
 
@@ -242,9 +245,10 @@
     public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
     public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT;
     public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_INSERT;
+    public static final int SW_LINEOUT_INSERT_BIT = 1 << SW_LINEOUT_INSERT;
     public static final int SW_JACK_PHYSICAL_INSERT_BIT = 1 << SW_JACK_PHYSICAL_INSERT;
     public static final int SW_JACK_BITS =
-            SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT;
+            SW_HEADPHONE_INSERT_BIT | SW_MICROPHONE_INSERT_BIT | SW_JACK_PHYSICAL_INSERT_BIT | SW_LINEOUT_INSERT_BIT;
 
     /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
     final boolean mUseDevInputEventForAudioJack;
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 495d3a9..530ad4b 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -272,8 +272,7 @@
             synchronized (mLocationSinkLock) {
                 // only one sink is allowed at the moment
                 if (mLocationSink != null) {
-                    throw new RuntimeException(
-                            "IFusedLocationHardware does not support multiple sinks");
+                    Log.e(TAG, "Replacing an existing IFusedLocationHardware sink");
                 }
 
                 mLocationSink = eventSink;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 2f1bd60..d71f66a 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -431,9 +431,9 @@
 
     private String getShortMetadataString() {
         int fields = mMetadata == null ? 0 : mMetadata.size();
-        String title = mMetadata == null ? null : mMetadata
-                .getString(MediaMetadata.METADATA_KEY_TITLE);
-        return "size=" + fields + ", title=" + title;
+        MediaMetadata.Description description = mMetadata == null ? null : mMetadata
+                .getDescription();
+        return "size=" + fields + ", description=" + description;
     }
 
     private void pushPlaybackStateUpdate() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2b55bf5..0b07f0a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11410,7 +11410,7 @@
 
     @Override
     public void replacePreferredActivity(IntentFilter filter, int match,
-            ComponentName[] set, ComponentName activity) {
+            ComponentName[] set, ComponentName activity, int userId) {
         if (filter.countActions() != 1) {
             throw new IllegalArgumentException(
                     "replacePreferredActivity expects filter to have only 1 action.");
@@ -11423,11 +11423,15 @@
                     "replacePreferredActivity expects filter to have no data authorities, " +
                     "paths, or types; and at most one scheme.");
         }
+
+        final int callingUid = Binder.getCallingUid();
+        enforceCrossUserPermission(callingUid, userId, true, "replace preferred activity");
+        final int callingUserId = UserHandle.getUserId(callingUid);
         synchronized (mPackages) {
             if (mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
                     != PackageManager.PERMISSION_GRANTED) {
-                if (getUidTargetSdkVersionLockedLPr(Binder.getCallingUid())
+                if (getUidTargetSdkVersionLockedLPr(callingUid)
                         < Build.VERSION_CODES.FROYO) {
                     Slog.w(TAG, "Ignoring replacePreferredActivity() from uid "
                             + Binder.getCallingUid());
@@ -11437,7 +11441,6 @@
                         android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
             }
 
-            final int callingUserId = UserHandle.getCallingUserId();
             PreferredIntentResolver pir = mSettings.mPreferredActivities.get(callingUserId);
             if (pir != null) {
                 Intent intent = new Intent(filter.getAction(0)).addCategory(filter.getCategory(0));
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 263767d..21905f0 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -453,10 +453,10 @@
     }
 
     @Override
-    public void hideRecentApps(boolean triggeredFromAltTab) {
+    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
         if (mBar != null) {
             try {
-                mBar.hideRecentApps(triggeredFromAltTab);
+                mBar.hideRecentApps(triggeredFromAltTab, triggeredFromHomeKey);
             } catch (RemoteException ex) {}
         }
     }
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index ba79fed..08ac210 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -930,6 +930,7 @@
             if (inputId != null) {
                 Intent intent = new Intent(Intent.ACTION_VIEW);
                 intent.setData(TvContract.buildChannelUriForPassthroughTvInput(inputId));
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 mContext.startActivity(intent);
             } else {
                 Slog.w(TAG, "onChanged: InputId cannot be found for :" + device);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e11b6a7..18446ae 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -479,12 +479,13 @@
         // Set up a callback to send the session token.
         ITvInputSessionCallback callback = new ITvInputSessionCallback.Stub() {
             @Override
-            public void onSessionCreated(ITvInputSession session) {
+            public void onSessionCreated(ITvInputSession session, IBinder harewareSessionToken) {
                 if (DEBUG) {
                     Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInfo.getId() + ")");
                 }
                 synchronized (mLock) {
                     sessionState.mSession = session;
+                    sessionState.mHardwareSessionToken = harewareSessionToken;
                     if (session == null) {
                         removeSessionStateLocked(sessionToken, userId);
                         sendSessionTokenToClientLocked(sessionState.mClient,
@@ -533,37 +534,35 @@
             }
 
             @Override
-            public void onTrackInfoChanged(List<TvTrackInfo> tracks) {
+            public void onTracksChanged(List<TvTrackInfo> tracks) {
                 synchronized (mLock) {
                     if (DEBUG) {
-                        Slog.d(TAG, "onTrackInfoChanged(" + tracks + ")");
+                        Slog.d(TAG, "onTracksChanged(" + tracks + ")");
                     }
                     if (sessionState.mSession == null || sessionState.mClient == null) {
                         return;
                     }
                     try {
-                        sessionState.mClient.onTrackInfoChanged(tracks,
-                                sessionState.mSeq);
+                        sessionState.mClient.onTracksChanged(tracks, sessionState.mSeq);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onTrackInfoChanged");
+                        Slog.e(TAG, "error in onTracksChanged");
                     }
                 }
             }
 
             @Override
-            public void onTrackSelectionChanged(List<TvTrackInfo> selectedTracks) {
+            public void onTrackSelected(int type, String trackId) {
                 synchronized (mLock) {
                     if (DEBUG) {
-                        Slog.d(TAG, "onTrackSelectionChanged(" + selectedTracks + ")");
+                        Slog.d(TAG, "onTrackSelected(type=" + type + ", trackId=" + trackId + ")");
                     }
                     if (sessionState.mSession == null || sessionState.mClient == null) {
                         return;
                     }
                     try {
-                        sessionState.mClient.onTrackSelectionChanged(selectedTracks,
-                                sessionState.mSeq);
+                        sessionState.mClient.onTrackSelected(type, trackId, sessionState.mSeq);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "error in onTrackSelectionChanged");
+                        Slog.e(TAG, "error in onTrackSelected");
                     }
                 }
             }
@@ -1109,8 +1108,14 @@
             try {
                 synchronized (mLock) {
                     try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setSurface(
-                                surface);
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        if (sessionState.mHardwareSessionToken == null) {
+                            getSessionLocked(sessionState).setSurface(surface);
+                        } else {
+                            getSessionLocked(sessionState.mHardwareSessionToken,
+                                    Process.SYSTEM_UID, resolvedUserId).setSurface(surface);
+                        }
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in setSurface", e);
                     }
@@ -1134,8 +1139,13 @@
             try {
                 synchronized (mLock) {
                     try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId)
-                                .dispatchSurfaceChanged(format, width, height);
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).dispatchSurfaceChanged(format, width, height);
+                        if (sessionState.mHardwareSessionToken != null) {
+                            getSessionLocked(sessionState.mHardwareSessionToken, Process.SYSTEM_UID,
+                                    resolvedUserId).dispatchSurfaceChanged(format, width, height);
+                        }
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in dispatchSurfaceChanged", e);
                     }
@@ -1147,6 +1157,8 @@
 
         @Override
         public void setVolume(IBinder sessionToken, float volume, int userId) {
+            final float REMOTE_VOLUME_ON = 1.0f;
+            final float REMOTE_VOLUME_OFF = 0f;
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "setVolume");
@@ -1154,8 +1166,16 @@
             try {
                 synchronized (mLock) {
                     try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId).setVolume(
-                                volume);
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).setVolume(volume);
+                        if (sessionState.mHardwareSessionToken != null) {
+                            // Here, we let the hardware session know only whether volume is on or
+                            // off to prevent that the volume is controlled in the both side.
+                            getSessionLocked(sessionState.mHardwareSessionToken,
+                                    Process.SYSTEM_UID, resolvedUserId).setVolume((volume > 0.0f)
+                                            ? REMOTE_VOLUME_ON : REMOTE_VOLUME_OFF);
+                        }
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in setVolume", e);
                     }
@@ -1266,7 +1286,7 @@
         }
 
         @Override
-        public void selectTrack(IBinder sessionToken, TvTrackInfo track, int userId) {
+        public void selectTrack(IBinder sessionToken, int type, String trackId, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "selectTrack");
@@ -1275,7 +1295,7 @@
                 synchronized (mLock) {
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).selectTrack(
-                                track);
+                                type, trackId);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in selectTrack", e);
                     }
@@ -1286,26 +1306,6 @@
         }
 
         @Override
-        public void unselectTrack(IBinder sessionToken, TvTrackInfo track, int userId) {
-            final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "unselectTrack");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId).unselectTrack(
-                                track);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in unselectTrack", e);
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
         public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
                 int userId) {
             final int callingUid = Binder.getCallingUid();
@@ -1479,13 +1479,24 @@
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
                     userId, "captureFrame");
             try {
-                final String wrappedInputId;
+                String hardwareInputId = null;
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(resolvedUserId);
-                    wrappedInputId = userState.wrappedInputMap.get(inputId);
+                    if (userState.inputMap.get(inputId) == null) {
+                        Slog.e(TAG, "Input not found for " + inputId);
+                        return false;
+                    }
+                    for (SessionState sessionState : userState.sessionStateMap.values()) {
+                        if (sessionState.mInfo.getId().equals(inputId)
+                                && sessionState.mHardwareSessionToken != null) {
+                            hardwareInputId = userState.sessionStateMap.get(
+                                    sessionState.mHardwareSessionToken).mInfo.getId();
+                            break;
+                        }
+                    }
                 }
                 return mTvInputHardwareManager.captureFrame(
-                        (wrappedInputId != null) ? wrappedInputId : inputId,
+                        (hardwareInputId != null) ? hardwareInputId : inputId,
                         surface, config, callingUid, resolvedUserId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -1603,6 +1614,7 @@
                         pw.println("mSessionToken: " + session.mSessionToken);
                         pw.println("mSession: " + session.mSession);
                         pw.println("mLogUri: " + session.mLogUri);
+                        pw.println("mHardwareSessionToken: " + session.mHardwareSessionToken);
                         pw.decreaseIndent();
                     }
                     pw.decreaseIndent();
@@ -1692,9 +1704,6 @@
         private final Set<ITvInputManagerCallback> callbackSet =
                 new HashSet<ITvInputManagerCallback>();
 
-        // A mapping from the TV input id to wrapped input id.
-        private final Map<String, String> wrappedInputMap = new HashMap<String, String>();
-
         // The token of a "main" TV input session.
         private IBinder mainSessionToken = null;
 
@@ -1769,9 +1778,11 @@
         private final IBinder mSessionToken;
         private ITvInputSession mSession;
         private Uri mLogUri;
+        // Not null if this session represents an external device connected to a hardware TV input.
+        private IBinder mHardwareSessionToken;
 
-        private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client, int seq,
-                int callingUid, int userId) {
+        private SessionState(IBinder sessionToken, TvInputInfo info, ITvInputClient client,
+                int seq, int callingUid, int userId) {
             mSessionToken = sessionToken;
             mInfo = info;
             mClient = client;
@@ -1791,6 +1802,22 @@
                         Slog.e(TAG, "error in onSessionReleased", e);
                     }
                 }
+                // If there are any other sessions based on this session, they should be released.
+                UserState userState = getUserStateLocked(mUserId);
+                for (SessionState sessionState : userState.sessionStateMap.values()) {
+                    if (mSession != null && mSession == sessionState.mHardwareSessionToken) {
+                        try {
+                            sessionState.mSession.release();
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "error in release", e);
+                        }
+                        try {
+                            sessionState.mClient.onSessionReleased(sessionState.mSeq);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "error in onSessionReleased", e);
+                        }
+                    }
+                }
                 removeSessionStateLocked(mSessionToken, mUserId);
             }
         }
@@ -1892,10 +1919,8 @@
 
                     for (TvInputState inputState : userState.inputMap.values()) {
                         if (inputState.mInfo.getComponent().equals(component)) {
-                            String inputId = inputState.mInfo.getId();
-                            notifyInputStateChangedLocked(userState, inputId,
+                            notifyInputStateChangedLocked(userState, inputState.mInfo.getId(),
                                     INPUT_STATE_DISCONNECTED, null);
-                            userState.wrappedInputMap.remove(inputId);
                         }
                     }
                     updateServiceConnectionLocked(mComponent, mUserId);
@@ -1974,27 +1999,6 @@
                 }
             }
         }
-
-        @Override
-        public void setWrappedInputId(String inputId, String wrappedInputId) {
-            synchronized (mLock) {
-                if (!hasInputIdLocked(inputId)) {
-                    return;
-                }
-                UserState userState = getUserStateLocked(mUserId);
-                userState.wrappedInputMap.put(inputId, wrappedInputId);
-            }
-        }
-
-        private boolean hasInputIdLocked(String inputId) {
-            ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
-            for (TvInputInfo inputInfo : serviceState.mInputList) {
-                if (inputInfo.getId().equals(inputId)) {
-                    return true;
-                }
-            }
-            return false;
-        }
     }
 
     private final class LogHandler extends Handler {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 80dfb91..cdb5e58 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -11258,7 +11258,7 @@
                 final WindowList windows = getDefaultWindowListLocked();
                 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                     final WindowState win = windows.get(winNdx);
-                    if (win.mHasSurface) {
+                    if (win.mHasSurface && win.mAppToken != null) {
                         win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
                         // Force add to mResizingWindows.
                         win.mLastContentInsets.set(-1, -1, -1, -1);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6730bf..899a821 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18,14 +18,15 @@
 
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 
+import android.app.admin.DevicePolicyManagerInternal;
 import com.android.internal.R;
-import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.storage.ExternalStorageFormatter;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.org.conscrypt.TrustedCertificateStore;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import android.app.Activity;
@@ -46,7 +47,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
@@ -80,7 +80,6 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
-import android.service.trust.TrustAgentService;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
@@ -111,13 +110,11 @@
 import java.security.cert.X509Certificate;
 import java.text.DateFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
@@ -289,6 +286,9 @@
         private static final String ATTR_VALUE = "value";
         private static final String TAG_PASSWORD_QUALITY = "password-quality";
         private static final String TAG_POLICIES = "policies";
+        private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
+                "cross-profile-widget-providers";
+        private static final String TAG_PROVIDER = "provider";
 
         final DeviceAdminInfo info;
 
@@ -346,6 +346,8 @@
         String globalProxyExclusionList = null;
         HashMap<String, List<String>> trustAgentFeatures = new HashMap<String, List<String>>();
 
+        List<String> crossProfileWidgetProviders;
+
         ActiveAdmin(DeviceAdminInfo _info) {
             info = _info;
         }
@@ -490,6 +492,17 @@
                 }
                 out.endTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
             }
+            if (crossProfileWidgetProviders != null && !crossProfileWidgetProviders.isEmpty()) {
+                out.startTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS);
+                final int providerCount = crossProfileWidgetProviders.size();
+                for (int i = 0; i < providerCount; i++) {
+                    String provider = crossProfileWidgetProviders.get(i);
+                    out.startTag(null, TAG_PROVIDER);
+                    out.attribute(null, ATTR_VALUE, provider);
+                    out.endTag(null, TAG_PROVIDER);
+                }
+                out.endTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS);
+            }
         }
 
         void readFromXml(XmlPullParser parser)
@@ -571,6 +584,8 @@
                     accountTypesWithManagementDisabled = readDisableAccountInfo(parser, tag);
                 } else if (TAG_MANAGE_TRUST_AGENT_FEATURES.equals(tag)) {
                     trustAgentFeatures = getAllTrustAgentFeatures(parser, tag);
+                } else if (TAG_CROSS_PROFILE_WIDGET_PROVIDERS.equals(tag)) {
+                    crossProfileWidgetProviders = getCrossProfileWidgetProviders(parser, tag);
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                 }
@@ -640,6 +655,30 @@
             return result;
         }
 
+        private List<String> getCrossProfileWidgetProviders(XmlPullParser parser, String tag)
+                throws XmlPullParserException, IOException  {
+            int outerDepthDAM = parser.getDepth();
+            int typeDAM;
+            ArrayList<String> result = null;
+            while ((typeDAM=parser.next()) != END_DOCUMENT
+                    && (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
+                if (typeDAM == END_TAG || typeDAM == TEXT) {
+                    continue;
+                }
+                String tagDAM = parser.getName();
+                if (TAG_PROVIDER.equals(tagDAM)) {
+                    final String provider = parser.getAttributeValue(null, ATTR_VALUE);
+                    if (result == null) {
+                        result = new ArrayList<>();
+                    }
+                    result.add(provider);
+                } else {
+                    Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
+                }
+            }
+            return result;
+        }
+
         void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("uid="); pw.println(getUid());
             pw.print(prefix); pw.println("policies:");
@@ -695,6 +734,8 @@
                     pw.println(disableScreenCapture);
             pw.print(prefix); pw.print("disabledKeyguardFeatures=");
                     pw.println(disabledKeyguardFeatures);
+            pw.print(prefix); pw.print("crossProfileWidgetProviders=");
+                    pw.println(crossProfileWidgetProviders);
         }
     }
 
@@ -752,6 +793,8 @@
         filter.addAction(Intent.ACTION_PACKAGE_ADDED);
         filter.addDataScheme("package");
         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
+
+        LocalServices.addService(DevicePolicyManagerInternal.class, new LocalService());
     }
 
     /**
@@ -1836,6 +1879,49 @@
         }
     }
 
+    @Override
+    public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+        ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
+                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        if (activeAdmin.crossProfileWidgetProviders == null) {
+            activeAdmin.crossProfileWidgetProviders = new ArrayList<>();
+        }
+        if (activeAdmin.crossProfileWidgetProviders.add(packageName)) {
+            saveSettingsLocked(UserHandle.getCallingUserId());
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+        ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
+                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        if (activeAdmin.crossProfileWidgetProviders == null) {
+            return false;
+        }
+        if (activeAdmin.crossProfileWidgetProviders.remove(packageName)) {
+            saveSettingsLocked(UserHandle.getCallingUserId());
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public List<String> getCrossProfileWidgetProviders(ComponentName admin) {
+        ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin,
+                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        if (activeAdmin.crossProfileWidgetProviders == null
+                || activeAdmin.crossProfileWidgetProviders.isEmpty()) {
+            return null;
+        }
+        if (Binder.getCallingUid() == Process.myUid()) {
+            return new ArrayList<>(activeAdmin.crossProfileWidgetProviders);
+        } else {
+            return activeAdmin.crossProfileWidgetProviders;
+        }
+    }
+
     /**
      * Return a single admin's expiration date/time, or the min (soonest) for all admins.
      * Returns 0 if not configured.
@@ -4486,4 +4572,24 @@
             }
         }
     }
+
+    private final class LocalService extends DevicePolicyManagerInternal {
+        @Override
+        public List<String> getCrossProfileWidgetProviders(int profileId) {
+            ComponentName ownerComponent = mDeviceOwner.getProfileOwnerComponent(profileId);
+            if (ownerComponent == null) {
+                return Collections.emptyList();
+            }
+
+            DevicePolicyData policy = getUserData(profileId);
+            ActiveAdmin admin = policy.mAdminMap.get(ownerComponent);
+
+            if (admin.crossProfileWidgetProviders == null
+                    || admin.crossProfileWidgetProviders.isEmpty()) {
+                return Collections.emptyList();
+            }
+
+            return admin.crossProfileWidgetProviders;
+        }
+    }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 8913eb9..b4c221f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -195,7 +195,8 @@
                     Keyphrase[] keyphrases = new Keyphrase[1];
                     keyphrases[0] = new Keyphrase(
                             keyphraseId, recognitionModes, locale, text, users);
-                    return new KeyphraseSoundModel(UUID.fromString(modelUuid), data, keyphrases);
+                    return new KeyphraseSoundModel(UUID.fromString(modelUuid),
+                            null /* FIXME use vendor UUID */, data, keyphrases);
                 }
                 Slog.w(TAG, "No SoundModel available for the given keyphrase");
             } finally {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
index f3ede88..fd36bfc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
@@ -25,6 +25,7 @@
 import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
 import android.hardware.soundtrigger.SoundTriggerModule;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -330,6 +331,23 @@
         }
     }
 
+    public void onSoundModelUpdate(SoundModelEvent event) {
+        if (event == null) {
+            Slog.w(TAG, "Invalid sound model event!");
+            return;
+        }
+
+        if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
+
+        //TODO: implement sound model update
+    }
+
+    public void onServiceStateChange(int state) {
+        if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state);
+
+        //TODO: implement service state update
+    }
+
     @Override
     public void onServiceDied() {
         synchronized (this) {
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index b55f62a..8845821 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -19,9 +19,6 @@
 import android.app.PendingIntent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import com.android.internal.os.SomeArgs;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -33,32 +30,6 @@
  */
 public abstract class Connection {
 
-    private static final int MSG_ADD_CONNECTION_LISTENER = 1;
-    private static final int MSG_REMOVE_CONNECTION_LISTENER = 2;
-    private static final int MSG_SET_AUDIO_STATE = 3;
-    private static final int MSG_SET_PARENT_CONNECTION = 4;
-    private static final int MSG_SET_HANDLE = 5;
-    private static final int MSG_SET_CALLER_DISPLAY_NAME = 6;
-    private static final int MSG_SET_CANCELED = 7;
-    private static final int MSG_SET_FAILED = 8;
-    private static final int MSG_SET_VIDEO_STATE = 9;
-    private static final int MSG_SET_ACTIVE = 10;
-    private static final int MSG_SET_RINGING = 11;
-    private static final int MSG_SET_INITIALIZING = 12;
-    private static final int MSG_SET_INITIALIZED = 13;
-    private static final int MSG_SET_DIALING = 14;
-    private static final int MSG_SET_ON_HOLD = 15;
-    private static final int MSG_SET_VIDEO_CALL_PROVIDER = 16;
-    private static final int MSG_SET_DISCONNECTED = 17;
-    private static final int MSG_SET_POST_DIAL_WAIT = 18;
-    private static final int MSG_SET_REQUESTING_RINGBACK = 19;
-    private static final int MSG_SET_CALL_CAPABILITIES = 20;
-    private static final int MSG_DESTROY = 21;
-    private static final int MSG_SET_SIGNAL = 22;
-    private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 23;
-    private static final int MSG_SET_STATUS_HINTS = 24;
-    private static final int MSG_START_ACTIVITY_FROM_IN_CALL = 25;
-
     /** @hide */
     public abstract static class Listener {
         public void onStateChanged(Connection c, int state) {}
@@ -78,6 +49,7 @@
         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
         public void onStartActivityFromInCall(Connection c, PendingIntent intent) {}
+        public void onFailed(Connection c, int code, String msg) {}
     }
 
     public final class State {
@@ -115,220 +87,6 @@
     private String mFailureMessage;
     private boolean mIsCanceled;
 
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_ADD_CONNECTION_LISTENER: {
-                    Listener listener = (Listener) msg.obj;
-                    mListeners.add(listener);
-                }
-                break;
-                case MSG_REMOVE_CONNECTION_LISTENER: {
-                    Listener listener = (Listener) msg.obj;
-                    mListeners.remove(listener);
-                }
-                break;
-                case MSG_SET_AUDIO_STATE: {
-                    CallAudioState state = (CallAudioState) msg.obj;
-                    mCallAudioState = state;
-                    onSetAudioState(state);
-                }
-                break;
-                case MSG_SET_PARENT_CONNECTION: {
-                    Connection parentConnection = (Connection) msg.obj;
-                    if (mParentConnection != parentConnection) {
-                        if (mParentConnection != null) {
-                            mParentConnection.removeChild(Connection.this);
-                        }
-                        mParentConnection = parentConnection;
-                        if (mParentConnection != null) {
-                            mParentConnection.addChild(Connection.this);
-                            // do something if the child connections goes down to ZERO.
-                        }
-                        for (Listener l : mListeners) {
-                            l.onParentConnectionChanged(Connection.this, mParentConnection);
-                        }
-                    }
-                }
-                break;
-                case MSG_SET_HANDLE: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        Uri handle = (Uri) args.arg1;
-                        int presentation = args.argi1;
-                        mHandle = handle;
-                        mHandlePresentation = presentation;
-                        for (Listener l : mListeners) {
-                            l.onHandleChanged(Connection.this, handle, presentation);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                }
-                break;
-                case MSG_SET_CALLER_DISPLAY_NAME: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        String callerDisplayName = (String) args.arg1;
-                        int presentation = args.argi1;
-                        mCallerDisplayName = callerDisplayName;
-                        mCallerDisplayNamePresentation = presentation;
-                        for (Listener l : mListeners) {
-                            l.onCallerDisplayNameChanged(Connection.this, callerDisplayName,
-                                    presentation);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                }
-                break;
-                case MSG_SET_CANCELED: {
-                    setState(State.CANCELED);
-                }
-                break;
-                case MSG_SET_FAILED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        int code = args.argi1;
-                        String message = (String) args.arg1;
-                        mFailureCode = code;
-                        mFailureMessage = message;
-                        setState(State.FAILED);
-                    } finally {
-                        args.recycle();
-                    }
-                }
-                break;
-                case MSG_SET_VIDEO_STATE: {
-                    int videoState = ((Integer) msg.obj).intValue();
-                    mVideoState = videoState;
-                    for (Listener l : mListeners) {
-                        l.onVideoStateChanged(Connection.this, mVideoState);
-                    }
-                }
-                break;
-                case MSG_SET_ACTIVE: {
-                    setRequestingRingback(false);
-                    setState(State.ACTIVE);
-                }
-                break;
-                case MSG_SET_RINGING: {
-                    setState(State.RINGING);
-                }
-                break;
-                case MSG_SET_INITIALIZING: {
-                    setState(State.INITIALIZING);
-                }
-                break;
-                case MSG_SET_INITIALIZED: {
-                    setState(State.NEW);
-                }
-                break;
-                case MSG_SET_DIALING: {
-                    setState(State.DIALING);
-                }
-                break;
-                case MSG_SET_ON_HOLD: {
-                    setState(State.HOLDING);
-                }
-                break;
-                case MSG_SET_VIDEO_CALL_PROVIDER: {
-                    ConnectionService.VideoCallProvider videoCallProvider =
-                            (ConnectionService.VideoCallProvider) msg.obj;
-                    mVideoCallProvider = videoCallProvider;
-                    for (Listener l : mListeners) {
-                        l.onVideoCallProviderChanged(Connection.this, videoCallProvider);
-                    }
-                }
-                break;
-                case MSG_SET_DISCONNECTED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        int cause = args.argi1;
-                        String message = (String) args.arg1;
-                        setState(State.DISCONNECTED);
-                        Log.d(this, "Disconnected with cause %d message %s", cause, message);
-                        for (Listener l : mListeners) {
-                            l.onDisconnected(Connection.this, cause, message);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                }
-                break;
-                case MSG_SET_POST_DIAL_WAIT: {
-                    String remaining = (String) msg.obj;
-                    for (Listener l : mListeners) {
-                        l.onPostDialWait(Connection.this, remaining);
-                    }
-                }
-                break;
-                case MSG_SET_REQUESTING_RINGBACK: {
-                    boolean ringback = ((Boolean) msg.obj).booleanValue();
-                    if (mRequestingRingback != ringback) {
-                        mRequestingRingback = ringback;
-                        for (Listener l : mListeners) {
-                            l.onRequestingRingback(Connection.this, ringback);
-                        }
-                    }
-                } break;
-                case MSG_SET_CALL_CAPABILITIES: {
-                    int callCapabilities = ((Integer) msg.obj).intValue();
-                    if (mCallCapabilities != callCapabilities) {
-                        mCallCapabilities = callCapabilities;
-                        for (Listener l : mListeners) {
-                            l.onCallCapabilitiesChanged(Connection.this, mCallCapabilities);
-                        }
-                    }
-                }
-                break;
-                case MSG_DESTROY: {
-                    // TODO: Is this still relevant because everything is on the main thread now.
-                    // It is possible that onDestroy() will trigger the listener to remove itself
-                    // which will result in a concurrent modification exception. To counteract
-                    // this we make a copy of the listeners and iterate on that.
-                    for (Listener l : new ArrayList<>(mListeners)) {
-                        if (mListeners.contains(l)) {
-                            l.onDestroyed(Connection.this);
-                        }
-                    }
-                }
-                break;
-                case MSG_SET_SIGNAL: {
-                    Bundle details = (Bundle) msg.obj;
-                    for (Listener l : mListeners) {
-                        l.onSignalChanged(Connection.this, details);
-                    }
-                }
-                break;
-                case MSG_SET_AUDIO_MODE_IS_VOIP: {
-                    boolean isVoip = ((Boolean) msg.obj).booleanValue();
-                    mAudioModeIsVoip = isVoip;
-                    for (Listener l : mListeners) {
-                        l.onAudioModeIsVoipChanged(Connection.this, isVoip);
-                    }
-                }
-                break;
-                case MSG_SET_STATUS_HINTS: {
-                    StatusHints statusHints = (StatusHints) msg.obj;
-                    mStatusHints = statusHints;
-                    for (Listener l : mListeners) {
-                        l.onStatusHintsChanged(Connection.this, statusHints);
-                    }
-                }
-                break;
-                case MSG_START_ACTIVITY_FROM_IN_CALL: {
-                    PendingIntent intent = (PendingIntent) msg.obj;
-                    for (Listener l : mListeners) {
-                        l.onStartActivityFromInCall(Connection.this, intent);
-                    }
-                }
-                break;
-            }
-        }
-    };
-
     /**
      * Create a new Connection.
      */
@@ -430,7 +188,7 @@
      * @hide
      */
     public final Connection addConnectionListener(Listener l) {
-        mHandler.obtainMessage(MSG_ADD_CONNECTION_LISTENER, l).sendToTarget();
+        mListeners.add(l);
         return this;
     }
 
@@ -443,7 +201,7 @@
      * @hide
      */
     public final Connection removeConnectionListener(Listener l) {
-        mHandler.obtainMessage(MSG_REMOVE_CONNECTION_LISTENER, l).sendToTarget();
+        mListeners.remove(l);
         return this;
     }
 
@@ -469,7 +227,8 @@
      */
     final void setAudioState(CallAudioState state) {
         Log.d(this, "setAudioState %s", state);
-        mHandler.obtainMessage(MSG_SET_AUDIO_STATE, state).sendToTarget();
+        mCallAudioState = state;
+        onSetAudioState(state);
     }
 
     /**
@@ -507,7 +266,19 @@
      */
     public final void setParentConnection(Connection parentConnection) {
         Log.d(this, "parenting %s to %s", this, parentConnection);
-        mHandler.obtainMessage(MSG_SET_PARENT_CONNECTION, parentConnection).sendToTarget();
+        if (mParentConnection != parentConnection) {
+            if (mParentConnection != null) {
+                mParentConnection.removeChild(this);
+            }
+            mParentConnection = parentConnection;
+            if (mParentConnection != null) {
+                mParentConnection.addChild(this);
+                // do something if the child connections goes down to ZERO.
+            }
+            for (Listener l : mListeners) {
+                l.onParentConnectionChanged(this, mParentConnection);
+            }
+        }
     }
 
     public final Connection getParentConnection() {
@@ -534,10 +305,11 @@
      */
     public final void setHandle(Uri handle, int presentation) {
         Log.d(this, "setHandle %s", handle);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = handle;
-        args.argi1 = presentation;
-        mHandler.obtainMessage(MSG_SET_HANDLE, args).sendToTarget();
+        mHandle = handle;
+        mHandlePresentation = presentation;
+        for (Listener l : mListeners) {
+            l.onHandleChanged(this, handle, presentation);
+        }
     }
 
     /**
@@ -549,10 +321,11 @@
      */
     public final void setCallerDisplayName(String callerDisplayName, int presentation) {
         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = callerDisplayName;
-        args.argi1 = presentation;
-        mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget();
+        mCallerDisplayName = callerDisplayName;
+        mCallerDisplayNamePresentation = presentation;
+        for (Listener l : mListeners) {
+            l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
+        }
     }
 
     /**
@@ -561,7 +334,7 @@
      */
     public final void setCanceled() {
         Log.d(this, "setCanceled");
-        mHandler.obtainMessage(MSG_SET_CANCELED).sendToTarget();
+        setState(State.CANCELED);
     }
 
     /**
@@ -577,10 +350,9 @@
      */
     public final void setFailed(int code, String message) {
         Log.d(this, "setFailed (%d: %s)", code, message);
-        SomeArgs args = SomeArgs.obtain();
-        args.argi1 = code;
-        args.arg1 = message;
-        mHandler.obtainMessage(MSG_SET_FAILED, args).sendToTarget();
+        mFailureCode = code;
+        mFailureMessage = message;
+        setState(State.FAILED);
     }
 
     /**
@@ -594,7 +366,10 @@
      */
     public final void setVideoState(int videoState) {
         Log.d(this, "setVideoState %d", videoState);
-        mHandler.obtainMessage(MSG_SET_VIDEO_STATE, Integer.valueOf(videoState)).sendToTarget();
+        mVideoState = videoState;
+        for (Listener l : mListeners) {
+            l.onVideoStateChanged(this, mVideoState);
+        }
     }
 
     /**
@@ -602,28 +377,28 @@
      * communicate).
      */
     public final void setActive() {
-        mHandler.obtainMessage(MSG_SET_ACTIVE).sendToTarget();
+        setRequestingRingback(false);
+        setState(State.ACTIVE);
     }
 
     /**
      * Sets state to ringing (e.g., an inbound ringing call).
      */
     public final void setRinging() {
-        mHandler.obtainMessage(MSG_SET_RINGING).sendToTarget();
+        setState(State.RINGING);
     }
 
     /**
      * Sets state to initializing (this Connection is not yet ready to be used).
      */
     public final void setInitializing() {
-        mHandler.obtainMessage(MSG_SET_INITIALIZING).sendToTarget();
+        setState(State.INITIALIZING);
     }
 
     /**
      * Sets state to initialized (the Connection has been set up and is now ready to be used).
      */
     public final void setInitialized() {
-        mHandler.obtainMessage(MSG_SET_INITIALIZED).sendToTarget();
         setState(State.NEW);
     }
 
@@ -631,14 +406,14 @@
      * Sets state to dialing (e.g., dialing an outbound call).
      */
     public final void setDialing() {
-        mHandler.obtainMessage(MSG_SET_DIALING).sendToTarget();
+        setState(State.DIALING);
     }
 
     /**
      * Sets state to be on hold.
      */
     public final void setOnHold() {
-        mHandler.obtainMessage(MSG_SET_ON_HOLD).sendToTarget();
+        setState(State.HOLDING);
     }
 
     /**
@@ -646,7 +421,10 @@
      * @param videoCallProvider The video call provider.
      */
     public final void setVideoCallProvider(ConnectionService.VideoCallProvider videoCallProvider) {
-        mHandler.obtainMessage(MSG_SET_VIDEO_CALL_PROVIDER, videoCallProvider).sendToTarget();
+        mVideoCallProvider = videoCallProvider;
+        for (Listener l : mListeners) {
+            l.onVideoCallProviderChanged(this, videoCallProvider);
+        }
     }
 
     public final ConnectionService.VideoCallProvider getVideoCallProvider() {
@@ -661,17 +439,20 @@
      * @param message Optional call-service-provided message about the disconnect.
      */
     public final void setDisconnected(int cause, String message) {
-        SomeArgs args = SomeArgs.obtain();
-        args.argi1 = cause;
-        args.arg1 = message;
-        mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
+        setState(State.DISCONNECTED);
+        Log.d(this, "Disconnected with cause %d message %s", cause, message);
+        for (Listener l : mListeners) {
+            l.onDisconnected(this, cause, message);
+        }
     }
 
     /**
      * TODO(santoscordon): Needs documentation.
      */
     public final void setPostDialWait(String remaining) {
-        mHandler.obtainMessage(MSG_SET_POST_DIAL_WAIT, remaining).sendToTarget();
+        for (Listener l : mListeners) {
+            l.onPostDialWait(this, remaining);
+        }
     }
 
     /**
@@ -681,8 +462,12 @@
      * @param ringback Whether the ringback tone is to be played.
      */
     public final void setRequestingRingback(boolean ringback) {
-        mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, Boolean.valueOf(ringback))
-                .sendToTarget();
+        if (mRequestingRingback != ringback) {
+            mRequestingRingback = ringback;
+            for (Listener l : mListeners) {
+                l.onRequestingRingback(this, ringback);
+            }
+        }
     }
 
     /**
@@ -691,15 +476,26 @@
      * @param callCapabilities The new call capabilities.
      */
     public final void setCallCapabilities(int callCapabilities) {
-        mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, Integer.valueOf(callCapabilities))
-                .sendToTarget();
+        if (mCallCapabilities != callCapabilities) {
+            mCallCapabilities = callCapabilities;
+            for (Listener l : mListeners) {
+                l.onCallCapabilitiesChanged(this, mCallCapabilities);
+            }
+        }
     }
 
     /**
      * TODO(santoscordon): Needs documentation.
      */
     public final void destroy() {
-        mHandler.obtainMessage(MSG_DESTROY).sendToTarget();
+        // It is possible that onDestroy() will trigger the listener to remove itself which will
+        // result in a concurrent modification exception. To counteract this we make a copy of the
+        // listeners and iterate on that.
+        for (Listener l : new ArrayList<>(mListeners)) {
+            if (mListeners.contains(l)) {
+                l.onDestroyed(this);
+            }
+        }
     }
 
     /**
@@ -708,7 +504,9 @@
      * @param details A {@link android.os.Bundle} containing details of the current level.
      */
     public final void setSignal(Bundle details) {
-        mHandler.obtainMessage(MSG_SET_SIGNAL, details).sendToTarget();
+        for (Listener l : mListeners) {
+            l.onSignalChanged(this, details);
+        }
     }
 
     /**
@@ -717,7 +515,10 @@
      * @param isVoip True if the audio mode is VOIP.
      */
     public final void setAudioModeIsVoip(boolean isVoip) {
-        mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, Boolean.valueOf(isVoip)).sendToTarget();
+        mAudioModeIsVoip = isVoip;
+        for (Listener l : mListeners) {
+            l.onAudioModeIsVoipChanged(this, isVoip);
+        }
     }
 
     /**
@@ -726,7 +527,10 @@
      * @param statusHints The status label and icon to set.
      */
     public final void setStatusHints(StatusHints statusHints) {
-        mHandler.obtainMessage(MSG_SET_STATUS_HINTS, statusHints).sendToTarget();
+        mStatusHints = statusHints;
+        for (Listener l : mListeners) {
+            l.onStatusHintsChanged(this, statusHints);
+        }
     }
 
     /**
@@ -738,13 +542,13 @@
         if (!intent.isActivity()) {
             throw new IllegalArgumentException("Activity intent required.");
         }
-        mHandler.obtainMessage(MSG_START_ACTIVITY_FROM_IN_CALL, intent).sendToTarget();
+        for (Listener l : mListeners) {
+            l.onStartActivityFromInCall(this, intent);
+        }
     }
 
     /**
      * Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
-     * <p>
-     * This callback will happen on the main thread.
      *
      * @param state The new call audio state.
      */
@@ -753,8 +557,6 @@
     /**
      * Notifies this Connection of an internal state change. This method is called after the
      * state is changed.
-     * <p>
-     * This callback will happen on the main thread.
      *
      * @param state The new state, a {@link Connection.State} member.
      */
@@ -762,8 +564,6 @@
 
     /**
      * Notifies this Connection of a request to play a DTMF tone.
-     * <p>
-     * This callback will happen on the main thread.
      *
      * @param c A DTMF character.
      */
@@ -771,81 +571,61 @@
 
     /**
      * Notifies this Connection of a request to stop any currently playing DTMF tones.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onStopDtmfTone() {}
 
     /**
      * Notifies this Connection of a request to disconnect.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onDisconnect() {}
 
     /**
      * Notifies this Connection of a request to disconnect.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onSeparate() {}
 
     /**
      * Notifies this Connection of a request to abort.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onAbort() {}
 
     /**
      * Notifies this Connection of a request to hold.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onHold() {}
 
     /**
      * Notifies this Connection of a request to exit a hold state.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onUnhold() {}
 
     /**
-     * Notifies this Connection, which is in {@link State#RINGING}, of a request to accept.
-     * <p>
-     * This callback will happen on the main thread.
+     * Notifies this Connection, which is in {@link State#RINGING}, of
+     * a request to accept.
      *
      * @param videoState The video state in which to answer the call.
      */
     public void onAnswer(int videoState) {}
 
     /**
-     * Notifies this Connection, which is in {@link State#RINGING}, of a request to reject.
-     * <p>
-     * This callback will happen on the main thread.
+     * Notifies this Connection, which is in {@link State#RINGING}, of
+     * a request to reject.
      */
     public void onReject() {}
 
     /**
      * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onPostDialContinue(boolean proceed) {}
 
     /**
      * Swap this call with a background call. This is used for calls that don't support hold,
      * e.g. CDMA.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onSwapWithBackgroundCall() {}
 
     /**
      * TODO(santoscordon): Needs documentation.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onChildrenChanged(List<Connection> children) {}
 
@@ -854,14 +634,12 @@
      */
     public void onPhoneAccountClicked() {}
 
-    /** This must be called from the main thread. */
     private void addChild(Connection connection) {
         Log.d(this, "adding child %s", connection);
         mChildConnections.add(connection);
         onChildrenChanged(mChildConnections);
     }
 
-    /** This must be called from the main thread. */
     private void removeChild(Connection connection) {
         Log.d(this, "removing child %s", connection);
         mChildConnections.remove(connection);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0772687..c50110a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2401,7 +2401,7 @@
      *            is sent to the SIM.
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
-     *            the end. If an error occurs, an empty string is returned.
+     *            the end.
      */
     public String iccTransmitApduLogicalChannel(int channel, int cla,
             int instruction, int p1, int p2, int p3, String data) {
@@ -2431,7 +2431,7 @@
      *            is sent to the SIM.
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
-     *            the end. If an error occurs, an empty string is returned.
+     *            the end.
      */
     public String iccTransmitApduBasicChannel(int cla,
             int instruction, int p1, int p2, int p3, String data) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 72b04cf..d256f9d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -530,7 +530,7 @@
      *            is sent to the SIM.
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
-     *            the end. If an error occurs, an empty string is returned.
+     *            the end.
      */
     String iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
             int p1, int p2, int p3, String data);
@@ -548,7 +548,7 @@
      *            is sent to the SIM.
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
-     *            the end. If an error occurs, an empty string is returned.
+     *            the end.
      */
     String iccTransmitApduBasicChannel(int cla, int instruction,
             int p1, int p2, int p3, String data);
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 8a2732d..46c81b6 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -572,6 +572,13 @@
 
     /** {@hide} */
     @Override
+    public Context createApplicationContext(ApplicationInfo application, int flags)
+            throws PackageManager.NameNotFoundException {
+        return null;
+    }
+
+    /** {@hide} */
+    @Override
     public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
             throws PackageManager.NameNotFoundException {
         throw new UnsupportedOperationException();
@@ -595,7 +602,7 @@
 
     @Override
     public boolean isRestricted() {
-        throw new UnsupportedOperationException();        
+        throw new UnsupportedOperationException();
     }
 
     /** @hide */
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index 3686899..c0799fc 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -193,8 +193,7 @@
             if (metadata == null) {
                 return;
             }
-            Log.d(TAG, "Received metadata change, title is "
-                    + metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
+            Log.d(TAG, "Received metadata change, " + metadata.getDescription());
         }
     }
 
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
index 4372ff9..65a3d8a 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
@@ -23,6 +23,7 @@
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.media.AudioFormat;
 import android.os.Parcel;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -97,7 +98,8 @@
         Keyphrase[] keyphrases = new Keyphrase[2];
         keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0});
         keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
-        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), null, keyphrases);
+        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
+                null, keyphrases);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -119,8 +121,8 @@
         Keyphrase[] keyphrases = new Keyphrase[2];
         keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0});
         keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
-        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), new byte[0],
-                keyphrases);
+        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
+                new byte[0], keyphrases);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -141,7 +143,8 @@
     public void testKeyphraseSoundModelParcelUnparcel_noKeyphrases() throws Exception {
         byte[] data = new byte[10];
         mRandom.nextBytes(data);
-        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data, null);
+        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
+                data, null);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -162,8 +165,8 @@
     public void testKeyphraseSoundModelParcelUnparcel_zeroKeyphrases() throws Exception {
         byte[] data = new byte[10];
         mRandom.nextBytes(data);
-        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data,
-                new Keyphrase[0]);
+        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
+                data, new Keyphrase[0]);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -187,7 +190,8 @@
         keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
         byte[] data = new byte[200 * 1024];
         mRandom.nextBytes(data);
-        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data, keyphrases);
+        KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
+                data, keyphrases);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -207,7 +211,7 @@
     @SmallTest
     public void testRecognitionEventParcelUnparcel_noData() throws Exception {
         RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1,
-                true, 2, 3, 4, null);
+                true, 2, 3, 4, false, null, null);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -224,7 +228,7 @@
     @SmallTest
     public void testRecognitionEventParcelUnparcel_zeroData() throws Exception {
         RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_FAILURE, 1,
-                true, 2, 3, 4, new byte[1]);
+                true, 2, 3, 4, false, null, new byte[1]);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -243,7 +247,32 @@
         byte[] data = new byte[200 * 1024];
         mRandom.nextBytes(data);
         RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1,
-                false, 2, 3, 4, data);
+                false, 2, 3, 4, false, null, data);
+
+        // Write to a parcel
+        Parcel parcel = Parcel.obtain();
+        re.writeToParcel(parcel, 0);
+
+        // Read from it
+        parcel.setDataPosition(0);
+        RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel);
+
+        // Verify that they are the same
+        assertEquals(re, unparceled);
+    }
+
+    @SmallTest
+    public void testRecognitionEventParcelUnparcel_largeAudioData() throws Exception {
+        byte[] data = new byte[200 * 1024];
+        mRandom.nextBytes(data);
+        RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1,
+                false, 2, 3, 4, true,
+                (new AudioFormat.Builder())
+                .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                .setSampleRate(16000)
+                .build(),
+                data);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -260,7 +289,7 @@
     @SmallTest
     public void testKeyphraseRecognitionEventParcelUnparcel_noKeyphrases() throws Exception {
         KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
-                SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1, true, 2, 3, 4, null, false, null);
+                SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1, true, 2, 3, 4, false, null, null, null);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -279,8 +308,8 @@
     public void testKeyphraseRecognitionEventParcelUnparcel_zeroData() throws Exception {
         KeyphraseRecognitionExtra[] kpExtra = new KeyphraseRecognitionExtra[0];
         KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
-                SoundTrigger.RECOGNITION_STATUS_FAILURE, 2, true, 2, 3, 4, new byte[1],
-                true, kpExtra);
+                SoundTrigger.RECOGNITION_STATUS_FAILURE, 2, true, 2, 3, 4, false, null, new byte[1],
+                kpExtra);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
@@ -303,20 +332,20 @@
         ConfidenceLevel cl1 = new ConfidenceLevel(1, 90);
         ConfidenceLevel cl2 = new ConfidenceLevel(2, 30);
         kpExtra[0] = new KeyphraseRecognitionExtra(1,
-                SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION,
+                SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION, 0,
                 new ConfidenceLevel[] {cl1, cl2});
         kpExtra[1] = new KeyphraseRecognitionExtra(1,
-                SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER,
+                SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0,
                 new ConfidenceLevel[] {cl2});
         kpExtra[2] = new KeyphraseRecognitionExtra(1,
-                SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, null);
+                SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0, null);
         kpExtra[3] = new KeyphraseRecognitionExtra(1,
-                SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER,
+                SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0,
                 new ConfidenceLevel[0]);
 
         KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
-                SoundTrigger.RECOGNITION_STATUS_FAILURE, 1, true, 2, 3, 4, data,
-                false, kpExtra);
+                SoundTrigger.RECOGNITION_STATUS_FAILURE, 1, true, 2, 3, 4, false, null, data,
+                kpExtra);
 
         // Write to a parcel
         Parcel parcel = Parcel.obtain();
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 88ebd1f..154851b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -16,6 +16,9 @@
 
 package android.graphics;
 
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+
 import java.awt.Font;
 import java.awt.Graphics2D;
 import java.awt.Toolkit;
@@ -58,6 +61,8 @@
     private final Graphics2D mGraphics;
     private final Paint_Delegate mPaint;
     private char[] mText;
+    // This List can contain nulls. A null font implies that the we weren't able to load the font
+    // properly. So, if we encounter a situation where we try to use that font, log a warning.
     private List<Font> mFonts;
     // Bounds of the text drawn so far.
     private RectF mBounds;
@@ -169,6 +174,10 @@
             // fonts to check which one can draw it.
             int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
             for (Font font : mFonts) {
+                if (font == null) {
+                    logFontWarning();
+                    continue;
+                }
                 canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
                 if (canDisplayUpTo == -1) {
                     render(start, start+charCount, font, flag, advances, advancesIndex, draw);
@@ -191,6 +200,12 @@
         }
     }
 
+    private static void logFontWarning() {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+                "Some fonts could not be loaded. The rendering may not be perfect. " +
+                        "Try running the IDE with JRE 7.", null, null);
+    }
+
     /**
      * Renders the text to the right of the bounds with the given font.
      * @param font The font to render the text with.
@@ -266,6 +281,10 @@
     private static void setScriptFont(char[] text, ScriptRun run,
             List<Font> fonts) {
         for (Font font : fonts) {
+            if (font == null) {
+                logFontWarning();
+                continue;
+            }
             if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
                 run.font = font;
                 return;
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 30b0ce5..de3307f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -24,6 +24,7 @@
 import android.content.res.AssetManager;
 
 import java.awt.Font;
+import java.awt.FontFormatException;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
@@ -152,14 +153,20 @@
             try {
                 return Font.createFont(Font.TRUETYPE_FONT, f);
             } catch (Exception e) {
+                if (path.endsWith(".otf") && e instanceof FontFormatException) {
+                    // If we aren't able to load an Open Type font, don't log a warning just yet.
+                    // We wait for a case where font is being used. Only then we try to log the
+                    // warning.
+                    return null;
+                }
                 Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
                         String.format("Unable to load font %1$s", relativePath),
-                        e /*throwable*/, null /*data*/);
+                        e, null);
             }
         } else {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                     "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
-                    null /*throwable*/, null /*data*/);
+                    null, null);
         }
 
         return null;
@@ -206,7 +213,7 @@
     @LayoutlibDelegate
     /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                "FontFamily.addFontFromAsset is not supported.", null /*throwable*/, null /*data*/);
+                "FontFamily.addFontFromAsset is not supported.", null, null);
         return false;
     }
 
diff --git a/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java
index 1e7564e..af0c456 100644
--- a/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java
@@ -48,4 +48,58 @@
 
         return def;
     }
+    @LayoutlibDelegate
+    /*package*/ static int native_get_int(String key, int def) {
+        Map<String, String> properties = Bridge.getPlatformProperties();
+        String value = properties.get(key);
+        if (value != null) {
+            return Integer.decode(value);
+        }
+
+        return def;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long native_get_long(String key, long def) {
+        Map<String, String> properties = Bridge.getPlatformProperties();
+        String value = properties.get(key);
+        if (value != null) {
+            return Long.decode(value);
+        }
+
+        return def;
+    }
+
+    /**
+     * Values 'n', 'no', '0', 'false' or 'off' are considered false.
+     * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
+     */
+    @LayoutlibDelegate
+    /*package*/ static boolean native_get_boolean(String key, boolean def) {
+        Map<String, String> properties = Bridge.getPlatformProperties();
+        String value = properties.get(key);
+
+        if ("n".equals(value) || "no".equals(value) || "0".equals(value) || "false".equals(value)
+                || "off".equals(value)) {
+            return false;
+        }
+        //noinspection SimplifiableIfStatement
+        if ("y".equals(value) || "yes".equals(value) || "1".equals(value) || "true".equals(value)
+                || "on".equals(value)) {
+            return true;
+        }
+
+        return def;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_set(String key, String def) {
+        Map<String, String> properties = Bridge.getPlatformProperties();
+        properties.put(key, def);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void native_add_change_callback() {
+        // pass.
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
new file mode 100644
index 0000000..5a467b2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
@@ -0,0 +1,55 @@
+package android.text;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.text.CharacterIterator;
+import java.util.Arrays;
+import java.util.Locale;
+
+import com.ibm.icu.lang.UCharacter;
+import com.ibm.icu.text.BreakIterator;
+import com.ibm.icu.util.ULocale;
+import javax.swing.text.Segment;
+
+/**
+ * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
+ *
+ * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class StaticLayout_Delegate {
+
+    /**
+     * Fills the recycle array with positions that are suitable to break the text at. The array
+     * must be terminated by '-1'.
+     */
+    @LayoutlibDelegate
+    /*package*/ static int[] nLineBreakOpportunities(String locale, char[] text, int length,
+            int[] recycle) {
+        BreakIterator iterator = BreakIterator.getLineInstance(new ULocale(locale));
+        Segment segment = new Segment(text, 0, length);
+        iterator.setText(segment);
+        if (recycle == null) {
+            // Because 42 is the answer to everything.
+            recycle = new int[42];
+        }
+        int breakOpp = iterator.first();
+        recycle[0] = breakOpp;
+        //noinspection ConstantConditions
+        assert BreakIterator.DONE == -1;
+        for (int i = 1; breakOpp != BreakIterator.DONE; ++i) {
+            if (i >= recycle.length) {
+                recycle = doubleSize(recycle);
+            }
+            assert (i < recycle.length);
+            breakOpp = iterator.next();
+            recycle[i] = breakOpp;
+        }
+        return recycle;
+    }
+
+    private static int[] doubleSize(int[] array) {
+        return Arrays.copyOf(array, array.length * 2);
+    }
+}
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 03b5211..04a52ea 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
@@ -967,6 +967,12 @@
     }
 
     @Override
+    public Context createApplicationContext(ApplicationInfo application, int flags)
+            throws PackageManager.NameNotFoundException {
+        return null;
+    }
+
+    @Override
     public boolean deleteDatabase(String arg0) {
         // pass
         return false;
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 4af07dd..31b3e25 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
@@ -116,17 +116,16 @@
         }
         File[] possibleSdks = sdkDir.listFiles(new FileFilter() {
             @Override
-            public boolean accept(File pathname) {
-                return pathname.isDirectory() && pathname.getAbsolutePath().contains("android-sdk");
+            public boolean accept(File path) {
+                return path.isDirectory() && path.getAbsolutePath().contains("android-sdk");
             }
         });
         for (File possibleSdk : possibleSdks) {
             File platformsDir = new File(possibleSdk, "platforms");
             File[] platforms = platformsDir.listFiles(new FileFilter() {
                 @Override
-                public boolean accept(File pathname) {
-                    return pathname.isDirectory()
-                            && pathname.toPath().getFileName().toString().startsWith("android-");
+                public boolean accept(File path) {
+                    return path.isDirectory() && path.getName().startsWith("android-");
                 }
             });
             if (platforms == null || platforms.length == 0) {
@@ -137,10 +136,8 @@
                 @Override
                 public int compare(File o1, File o2) {
                     final int MAX_VALUE = 1000;
-                    String suffix1 = o1.toPath().getFileName().toString()
-                            .substring("android-".length());
-                    String suffix2 = o2.toPath().getFileName().toString()
-                            .substring("android-".length());
+                    String suffix1 = o1.getName().substring("android-".length());
+                    String suffix2 = o2.getName().substring("android-".length());
                     int suff1, suff2;
                     try {
                         suff1 = Integer.parseInt(suffix1);
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 8fb8928..89cbaeb 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
@@ -150,7 +150,6 @@
         "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
         "com.android.internal.util.XmlUtils#convertValueToInt",
         "com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
-        "android.os.SystemProperties#native_get",
         "dalvik.system.VMRuntime#newUnpaddedArray"
     };
 
@@ -198,7 +197,9 @@
         "android.graphics.Typeface",
         "android.graphics.Xfermode",
         "android.os.SystemClock",
+        "android.os.SystemProperties",
         "android.text.AndroidBidi",
+        "android.text.StaticLayout",
         "android.text.format.Time",
         "android.util.FloatMath",
         "android.view.Display",
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
index 99a5671..9ff56d6 100644
--- a/tools/obbtool/Android.mk
+++ b/tools/obbtool/Android.mk
@@ -13,7 +13,7 @@
 LOCAL_SRC_FILES := \
 	Main.cpp
 
-LOCAL_CFLAGS := -Wall -Werror -Wno-mismatched-tags
+LOCAL_CFLAGS := -Wall -Werror
 
 #LOCAL_C_INCLUDES +=
 
@@ -36,7 +36,7 @@
 
 LOCAL_MODULE := pbkdf2gen
 LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wno-mismatched-tags
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_SRC_FILES := pbkdf2gen.cpp
 LOCAL_LDLIBS += -ldl
 LOCAL_C_INCLUDES := external/openssl/include $(LOCAL_C_INCLUDES)