Merge "Fix 7.1 audio playback from AudioTrack" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 2a3c086..5778c7e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8526,6 +8526,15 @@
     field public int reqTouchScreen;
   }
 
+  public final class FeatureGroupInfo implements android.os.Parcelable {
+    ctor public FeatureGroupInfo();
+    ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public android.content.pm.FeatureInfo[] features;
+  }
+
   public class FeatureInfo implements android.os.Parcelable {
     ctor public FeatureInfo();
     ctor public FeatureInfo(android.content.pm.FeatureInfo);
@@ -8540,36 +8549,6 @@
     field public int reqGlEsVersion;
   }
 
-  public class InstallSessionInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.graphics.Bitmap getAppIcon();
-    method public java.lang.CharSequence getAppLabel();
-    method public java.lang.String getAppPackageName();
-    method public android.content.Intent getDetailsIntent();
-    method public java.lang.String getInstallerPackageName();
-    method public float getProgress();
-    method public int getSessionId();
-    method public boolean isOpen();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public class InstallSessionParams implements android.os.Parcelable {
-    ctor public InstallSessionParams(int);
-    method public int describeContents();
-    method public void setAppIcon(android.graphics.Bitmap);
-    method public void setAppLabel(java.lang.CharSequence);
-    method public void setAppPackageName(java.lang.String);
-    method public void setInstallLocation(int);
-    method public void setOriginatingUri(android.net.Uri);
-    method public void setReferrerUri(android.net.Uri);
-    method public void setSize(long);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final int MODE_FULL_INSTALL = 1; // 0x1
-    field public static final int MODE_INHERIT_EXISTING = 2; // 0x2
-  }
-
   public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     ctor public InstrumentationInfo();
     ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo);
@@ -8647,6 +8626,7 @@
     field public android.content.pm.ActivityInfo[] activities;
     field public android.content.pm.ApplicationInfo applicationInfo;
     field public android.content.pm.ConfigurationInfo[] configPreferences;
+    field public android.content.pm.FeatureGroupInfo[] featureGroups;
     field public long firstInstallTime;
     field public int[] gids;
     field public int installLocation;
@@ -8671,37 +8651,35 @@
   public class PackageInstaller {
     method public void addSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
     method public void addSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
-    method public int createSession(android.content.pm.InstallSessionParams) throws java.io.IOException;
-    method public java.util.List<android.content.pm.InstallSessionInfo> getAllSessions();
-    method public java.util.List<android.content.pm.InstallSessionInfo> getMySessions();
-    method public android.content.pm.InstallSessionInfo getSessionInfo(int);
+    method public int createSession(android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
+    method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
+    method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
+    method public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
     method public android.content.pm.PackageInstaller.Session openSession(int);
     method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
-    method public void uninstall(java.lang.String, android.content.pm.PackageInstaller.UninstallCallback);
+    method public void uninstall(java.lang.String, android.content.IntentSender);
     field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+    field public static final java.lang.String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
     field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
-  }
-
-  public static abstract class PackageInstaller.CommitCallback {
-    ctor public PackageInstaller.CommitCallback();
-    method public abstract void onFailure(int, java.lang.String, android.os.Bundle);
-    method public abstract void onSuccess();
-    method public abstract void onUserActionRequired(android.content.Intent);
-    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
-    field public static final int FAILURE_ABORTED = 5; // 0x5
-    field public static final int FAILURE_CONFLICT = 2; // 0x2
-    field public static final int FAILURE_INCOMPATIBLE = 4; // 0x4
-    field public static final int FAILURE_INVALID = 1; // 0x1
-    field public static final int FAILURE_STORAGE = 3; // 0x3
-    field public static final int FAILURE_UNKNOWN = 0; // 0x0
+    field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
+    field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+    field public static final int STATUS_FAILURE = 1; // 0x1
+    field public static final int STATUS_FAILURE_ABORTED = 2; // 0x2
+    field public static final int STATUS_FAILURE_BLOCKED = 1; // 0x1
+    field public static final int STATUS_FAILURE_CONFLICT = 4; // 0x4
+    field public static final int STATUS_FAILURE_INCOMPATIBLE = 6; // 0x6
+    field public static final int STATUS_FAILURE_INVALID = 3; // 0x3
+    field public static final int STATUS_FAILURE_STORAGE = 5; // 0x5
+    field public static final int STATUS_SUCCESS = 0; // 0x0
+    field public static final int STATUS_USER_ACTION_REQUIRED = -1; // 0xffffffff
   }
 
   public static class PackageInstaller.Session implements java.io.Closeable {
     method public void abandon();
     method public void close();
-    method public void commit(android.content.pm.PackageInstaller.CommitCallback);
+    method public void commit(android.content.IntentSender);
     method public void fsync(java.io.OutputStream) throws java.io.IOException;
-    method public java.lang.String[] list();
+    method public java.lang.String[] getNames();
     method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
     method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
     method public void setProgress(float);
@@ -8716,14 +8694,34 @@
     method public abstract void onProgressChanged(int, float);
   }
 
-  public static abstract class PackageInstaller.UninstallCallback {
-    ctor public PackageInstaller.UninstallCallback();
-    method public abstract void onFailure(int, java.lang.String, android.os.Bundle);
-    method public abstract void onSuccess();
-    method public abstract void onUserActionRequired(android.content.Intent);
-    field public static final int FAILURE_ABORTED = 2; // 0x2
-    field public static final int FAILURE_BLOCKED = 1; // 0x1
-    field public static final int FAILURE_UNKNOWN = 0; // 0x0
+  public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.graphics.Bitmap getAppIcon();
+    method public java.lang.CharSequence getAppLabel();
+    method public java.lang.String getAppPackageName();
+    method public android.content.Intent getDetailsIntent();
+    method public java.lang.String getInstallerPackageName();
+    method public float getProgress();
+    method public int getSessionId();
+    method public boolean isOpen();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static class PackageInstaller.SessionParams implements android.os.Parcelable {
+    ctor public PackageInstaller.SessionParams(int);
+    method public int describeContents();
+    method public void setAppIcon(android.graphics.Bitmap);
+    method public void setAppLabel(java.lang.CharSequence);
+    method public void setAppPackageName(java.lang.String);
+    method public void setInstallLocation(int);
+    method public void setOriginatingUri(android.net.Uri);
+    method public void setReferrerUri(android.net.Uri);
+    method public void setSize(long);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int MODE_FULL_INSTALL = 1; // 0x1
+    field public static final int MODE_INHERIT_EXISTING = 2; // 0x2
   }
 
   public class PackageItemInfo {
@@ -11654,7 +11652,7 @@
   public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable {
     ctor public AnimatedStateListDrawable();
     method public void addState(int[], android.graphics.drawable.Drawable, int);
-    method public void addTransition(int, int, android.graphics.drawable.Drawable, boolean);
+    method public void addTransition(int, int, T, boolean);
   }
 
   public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
@@ -23026,9 +23024,9 @@
     method public boolean isUserRunning(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
     method public boolean setRestrictionsChallenge(java.lang.String);
-    method public void setUserRestriction(java.lang.String, boolean);
-    method public void setUserRestrictions(android.os.Bundle);
-    method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
+    method public deprecated void setUserRestriction(java.lang.String, boolean);
+    method public deprecated void setUserRestrictions(android.os.Bundle);
+    method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
     field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index b13b009..b37f896 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -225,7 +225,7 @@
         void printUsageMessage() {
             try {
                 System.out.println("V2Monitoring session " + mController.getTag()
-                        + "...  available commands:");
+                        + "...  available commands: play, pause, next, previous");
             } catch (RemoteException e) {
                 System.out.println("Error trying to monitor session!");
             }
@@ -257,6 +257,14 @@
                         addNewline = false;
                     } else if ("q".equals(line) || "quit".equals(line)) {
                         break;
+                    } else if ("play".equals(line)) {
+                        mController.play();
+                    } else if ("pause".equals(line)) {
+                        mController.pause();
+                    } else if ("next".equals(line)) {
+                        mController.next();
+                    } else if ("previous".equals(line)) {
+                        mController.previous();
                     } else {
                         System.out.println("Invalid command: " + line);
                     }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 15152e5..46d8ade 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -19,21 +19,22 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
-import android.app.PackageDeleteObserver;
 import android.app.PackageInstallObserver;
 import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageManager;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.CommitCallback;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
@@ -74,6 +75,8 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.WeakHashMap;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
 
 public final class Pm {
     private static final String TAG = "Pm";
@@ -776,36 +779,6 @@
         }
     }
 
-    class LocalCommitCallback extends CommitCallback {
-        boolean finished;
-        boolean success;
-        String msg;
-
-        private void setResult(boolean success, String msg) {
-            synchronized (this) {
-                this.finished = true;
-                this.success = success;
-                this.msg = msg;
-                notifyAll();
-            }
-        }
-
-        @Override
-        public void onUserActionRequired(Intent intent) {
-            setResult(false, "Unexepected user action required!");
-        }
-
-        @Override
-        public void onSuccess() {
-            setResult(true, null);
-        }
-
-        @Override
-        public void onFailure(int failureReason, String msg, Bundle extras) {
-            setResult(false, msg);
-        }
-    }
-
     /**
      * Converts a failure code into a string by using reflection to find a matching constant
      * in PackageManager.
@@ -1007,8 +980,7 @@
     private void runInstallCreate() throws RemoteException {
         String installerPackageName = null;
 
-        final InstallSessionParams params = new InstallSessionParams(
-                InstallSessionParams.MODE_FULL_INSTALL);
+        final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
         params.installFlags = PackageManager.INSTALL_ALL_USERS;
 
         String opt;
@@ -1031,7 +1003,7 @@
             } else if (opt.equals("-d")) {
                 params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
             } else if (opt.equals("-p")) {
-                params.mode = InstallSessionParams.MODE_INHERIT_EXISTING;
+                params.mode = SessionParams.MODE_INHERIT_EXISTING;
             } else if (opt.equals("-S")) {
                 params.setSize(Long.parseLong(nextOptionData()));
             } else if (opt.equals("--abi")) {
@@ -1073,7 +1045,7 @@
             }
         }
 
-        final InstallSessionInfo info = mInstaller.getSessionInfo(sessionId);
+        final SessionInfo info = mInstaller.getSessionInfo(sessionId);
 
         PackageInstaller.Session session = null;
         InputStream in = null;
@@ -1117,22 +1089,20 @@
         try {
             session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
 
-            final LocalCommitCallback callback = new LocalCommitCallback();
-            session.commit(callback);
+            final LocalIntentReceiver receiver = new LocalIntentReceiver();
+            session.commit(receiver.getIntentSender());
 
-            synchronized (callback) {
-                while (!callback.finished) {
-                    try {
-                        callback.wait();
-                    } catch (InterruptedException e) {
-                    }
-                }
-                if (!callback.success) {
-                    throw new IllegalStateException("Failure [" + callback.msg + "]");
-                }
+            final Intent result = receiver.getResult();
+            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageInstaller.STATUS_FAILURE);
+            if (status == PackageInstaller.STATUS_SUCCESS) {
+                System.out.println("Success");
+            } else {
+                Log.e(TAG, "Failure details: " + result.getExtras());
+                System.out.println("Failure ["
+                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+                return;
             }
-
-            System.out.println("Success");
         } finally {
             IoUtils.closeQuietly(session);
         }
@@ -1274,28 +1244,14 @@
         }
     }
 
-    class LocalPackageDeleteObserver extends PackageDeleteObserver {
-        boolean finished;
-        boolean result;
-
-        @Override
-        public void onPackageDeleted(String name, int returnCode, String msg) {
-            synchronized (this) {
-                finished = true;
-                result = returnCode == PackageManager.DELETE_SUCCEEDED;
-                notifyAll();
-            }
-        }
-    }
-
-    private void runUninstall() {
-        int unInstallFlags = 0;
+    private void runUninstall() throws RemoteException {
+        int flags = 0;
         int userId = UserHandle.USER_ALL;
 
         String opt;
         while ((opt=nextOption()) != null) {
             if (opt.equals("-k")) {
-                unInstallFlags |= PackageManager.DELETE_KEEP_DATA;
+                flags |= PackageManager.DELETE_KEEP_DATA;
             } else if (opt.equals("--user")) {
                 String param = nextArg();
                 if (isNumber(param)) {
@@ -1320,7 +1276,7 @@
 
         if (userId == UserHandle.USER_ALL) {
             userId = UserHandle.USER_OWNER;
-            unInstallFlags |= PackageManager.DELETE_ALL_USERS;
+            flags |= PackageManager.DELETE_ALL_USERS;
         } else {
             PackageInfo info;
             try {
@@ -1340,38 +1296,25 @@
             // user set flag so it disables rather than reverting to system
             // version of the app.
             if (isSystem) {
-                unInstallFlags |= PackageManager.DELETE_SYSTEM_APP;
+                flags |= PackageManager.DELETE_SYSTEM_APP;
             }
         }
 
-        boolean result = deletePackage(pkg, unInstallFlags, userId);
-        if (result) {
+        final LocalIntentReceiver receiver = new LocalIntentReceiver();
+        mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);
+
+        final Intent result = receiver.getResult();
+        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                PackageInstaller.STATUS_FAILURE);
+        if (status == PackageInstaller.STATUS_SUCCESS) {
             System.out.println("Success");
         } else {
-            System.out.println("Failure");
+            Log.e(TAG, "Failure details: " + result.getExtras());
+            System.out.println("Failure ["
+                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
         }
     }
 
-    private boolean deletePackage(String packageName, int flags, int userId) {
-        LocalPackageDeleteObserver obs = new LocalPackageDeleteObserver();
-        try {
-            mInstaller.uninstall(packageName, flags, obs.getBinder(), userId);
-
-            synchronized (obs) {
-                while (!obs.finished) {
-                    try {
-                        obs.wait();
-                    } catch (InterruptedException e) {
-                    }
-                }
-            }
-        } catch (RemoteException e) {
-            System.err.println(e.toString());
-            System.err.println(PM_NOT_RUNNING_ERR);
-        }
-        return obs.result;
-    }
-
     static class ClearDataObserver extends IPackageDataObserver.Stub {
         boolean finished;
         boolean result;
@@ -1384,7 +1327,6 @@
                 notifyAll();
             }
         }
-
     }
 
     private void runClear() {
@@ -1717,6 +1659,35 @@
         throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
     }
 
+    private static class LocalIntentReceiver {
+        private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public int send(int code, Intent intent, String resolvedType,
+                    IIntentReceiver finishedReceiver, String requiredPermission) {
+                try {
+                    mResult.offer(intent, 5, TimeUnit.SECONDS);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+                return 0;
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+
+        public Intent getResult() {
+            try {
+                return mResult.poll(30, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
     private String nextOption() {
         if (mNextArg >= mArgs.length) {
             return null;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index fed7ae3..d9ea671 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1811,6 +1811,10 @@
         synchronized (this) {
             getSystemContext().installSystemApplicationInfo(info, classLoader);
 
+            // The code package for "android" in the system server needs
+            // to be the system context's package.
+            mPackages.put("android", new WeakReference<LoadedApk>(getSystemContext().mPackageInfo));
+
             // give ourselves a default profiler
             mProfiler = new Profiler();
         }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a935dc0..b2812e3 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1568,7 +1568,7 @@
         synchronized (mLock) {
             if (mInstaller == null) {
                 try {
-                    mInstaller = new PackageInstaller(this, mPM.getPackageInstaller(),
+                    mInstaller = new PackageInstaller(mContext, this, mPM.getPackageInstaller(),
                             mContext.getPackageName(), mContext.getUserId());
                 } catch (RemoteException e) {
                     throw e.rethrowAsRuntimeException();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index fcfc1c49..e0c7816 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -40,6 +40,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.AndroidRuntimeException;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.DisplayAdjustments;
@@ -643,8 +644,17 @@
     }
 
     private void rewriteRValues(ClassLoader cl, String packageName, int id) {
+        final Class<?> rClazz;
         try {
-            final Class<?> rClazz = cl.loadClass(packageName + ".R");
+            rClazz = cl.loadClass(packageName + ".R");
+        } catch (ClassNotFoundException e) {
+            // This is not necessarily an error, as some packages do not ship with resources
+            // (or they do not need rewriting).
+            Log.i(TAG, "Could not find R class for package '" + packageName + "'");
+            return;
+        }
+
+        try {
             Class<?>[] declaredClasses = rClazz.getDeclaredClasses();
             for (Class<?> clazz : declaredClasses) {
                 try {
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index e9297b9..1bb4eba 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -229,6 +229,28 @@
     }
 
     /**
+     * Enable/disable data restore at application install time.  When enabled, app
+     * installation will include an attempt to fetch the app's historical data from
+     * the archival restore dataset (if any).  When disabled, no such attempt will
+     * be made.
+     *
+     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void setAutoRestore(boolean isEnabled) {
+        checkServiceBinder();
+        if (sService != null) {
+            try {
+                sService.setAutoRestore(isEnabled);
+            } catch (RemoteException e) {
+                Log.e(TAG, "setAutoRestore() couldn't connect");
+            }
+        }
+    }
+
+    /**
      * Identify the currently selected transport.  Callers must hold the
      * android.permission.BACKUP permission to use this method.
      * @return The name of the currently active backup transport.  In case of
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index a767246..0b40e35 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -271,13 +271,13 @@
      * @return The provider icon.
      */
     public final Drawable loadIcon(@NonNull Context context, int density) {
-        return loadDrawable(context, density, providerInfo.getIconResource());
+        return loadDrawable(context, density, providerInfo.getIconResource(), true);
     }
 
     /**
      * 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}
+     * 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>
@@ -288,10 +288,10 @@
      * @param context Context for accessing resources.
      * @param density The optional desired density as per
      *         {@link android.util.DisplayMetrics#densityDpi}.
-     * @return The widget preview image.
+     * @return The widget preview image or {@null} if preview image is not available.
      */
     public final Drawable loadPreviewImage(@NonNull Context context, int density) {
-        return loadDrawable(context, density, previewImage);
+        return loadDrawable(context, density, previewImage, false);
     }
 
     /**
@@ -361,7 +361,8 @@
         return 0;
     }
 
-    private Drawable loadDrawable(Context context, int density, int resourceId) {
+    private Drawable loadDrawable(Context context, int density, int resourceId,
+            boolean loadDefaultIcon) {
         try {
             Resources resources = context.getPackageManager().getResourcesForApplication(
                     providerInfo.applicationInfo);
@@ -374,7 +375,7 @@
         } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
             /* ignore */
         }
-        return providerInfo.loadIcon(context.getPackageManager());
+        return loadDefaultIcon ? providerInfo.loadIcon(context.getPackageManager()) : null;
     }
 
     /**
diff --git a/core/java/android/content/pm/FeatureGroupInfo.java b/core/java/android/content/pm/FeatureGroupInfo.java
new file mode 100644
index 0000000..79a6eea
--- /dev/null
+++ b/core/java/android/content/pm/FeatureGroupInfo.java
@@ -0,0 +1,65 @@
+/**
+ * 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A set of features that can be requested by an application. This corresponds
+ * to information collected from the
+ * AndroidManifest.xml's {@code <feature-group>} tag.
+ */
+public final class FeatureGroupInfo implements Parcelable {
+
+    /**
+     * The list of features that are required by this group.
+     *
+     * @see FeatureInfo#FLAG_REQUIRED
+     */
+    public FeatureInfo[] features;
+
+    public FeatureGroupInfo() {
+    }
+
+    public FeatureGroupInfo(FeatureGroupInfo other) {
+        features = other.features;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedArray(features, flags);
+    }
+
+    public static final Creator<FeatureGroupInfo> CREATOR = new Creator<FeatureGroupInfo>() {
+        @Override
+        public FeatureGroupInfo createFromParcel(Parcel source) {
+            FeatureGroupInfo group = new FeatureGroupInfo();
+            group.features = source.createTypedArray(FeatureInfo.CREATOR);
+            return group;
+        }
+
+        @Override
+        public FeatureGroupInfo[] newArray(int size) {
+            return new FeatureGroupInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
index d919fc3..79fa327 100644
--- a/core/java/android/content/pm/FeatureInfo.java
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -22,7 +22,7 @@
 /**
  * A single feature that can be requested by an application. This corresponds
  * to information collected from the
- * AndroidManifest.xml's &lt;uses-feature&gt; tag.
+ * AndroidManifest.xml's {@code <uses-feature>} tag.
  */
 public class FeatureInfo implements Parcelable {
     /**
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 5223476..97be8f0 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -19,23 +19,22 @@
 import android.content.pm.IPackageDeleteObserver2;
 import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.IPackageInstallerSession;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
+import android.content.pm.PackageInstaller;
+import android.content.IntentSender;
 
 /** {@hide} */
 interface IPackageInstaller {
-    int createSession(in InstallSessionParams params, String installerPackageName, int userId);
+    int createSession(in PackageInstaller.SessionParams params, String installerPackageName, int userId);
     IPackageInstallerSession openSession(int sessionId);
 
-    InstallSessionInfo getSessionInfo(int sessionId);
-    List<InstallSessionInfo> getAllSessions(int userId);
-    List<InstallSessionInfo> getMySessions(String installerPackageName, int userId);
+    PackageInstaller.SessionInfo getSessionInfo(int sessionId);
+    List<PackageInstaller.SessionInfo> getAllSessions(int userId);
+    List<PackageInstaller.SessionInfo> getMySessions(String installerPackageName, int userId);
 
     void registerCallback(IPackageInstallerCallback callback, int userId);
     void unregisterCallback(IPackageInstallerCallback callback);
 
-    void uninstall(String packageName, int flags, in IPackageDeleteObserver2 observer, int userId);
-    void uninstallSplit(String packageName, String splitName, int flags, in IPackageDeleteObserver2 observer, int userId);
+    void uninstall(String packageName, int flags, in IntentSender statusReceiver, int userId);
 
     void setPermissionsResult(int sessionId, boolean accepted);
 }
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index af0323f..aee3ba7 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.content.pm.IPackageInstallObserver2;
+import android.content.IntentSender;
 import android.os.ParcelFileDescriptor;
 
 /** {@hide} */
@@ -24,11 +25,11 @@
     void setClientProgress(float progress);
     void addClientProgress(float progress);
 
-    String[] list();
+    String[] getNames();
     ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
     ParcelFileDescriptor openRead(String name);
 
     void close();
-    void commit(in IPackageInstallObserver2 observer);
+    void commit(in IntentSender statusReceiver);
     void abandon();
 }
diff --git a/core/java/android/content/pm/InstallSessionInfo.aidl b/core/java/android/content/pm/InstallSessionInfo.aidl
deleted file mode 100644
index 3d21bbd..0000000
--- a/core/java/android/content/pm/InstallSessionInfo.aidl
+++ /dev/null
@@ -1,19 +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.content.pm;
-
-parcelable InstallSessionInfo;
diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java
deleted file mode 100644
index 161bcde..0000000
--- a/core/java/android/content/pm/InstallSessionInfo.java
+++ /dev/null
@@ -1,181 +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.content.pm;
-
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Details for an active install session.
- */
-public class InstallSessionInfo implements Parcelable {
-
-    /** {@hide} */
-    public int sessionId;
-    /** {@hide} */
-    public String installerPackageName;
-    /** {@hide} */
-    public String resolvedBaseCodePath;
-    /** {@hide} */
-    public float progress;
-    /** {@hide} */
-    public boolean sealed;
-    /** {@hide} */
-    public boolean open;
-
-    /** {@hide} */
-    public int mode;
-    /** {@hide} */
-    public long sizeBytes;
-    /** {@hide} */
-    public String appPackageName;
-    /** {@hide} */
-    public Bitmap appIcon;
-    /** {@hide} */
-    public CharSequence appLabel;
-
-    /** {@hide} */
-    public InstallSessionInfo() {
-    }
-
-    /** {@hide} */
-    public InstallSessionInfo(Parcel source) {
-        sessionId = source.readInt();
-        installerPackageName = source.readString();
-        resolvedBaseCodePath = source.readString();
-        progress = source.readFloat();
-        sealed = source.readInt() != 0;
-        open = source.readInt() != 0;
-
-        mode = source.readInt();
-        sizeBytes = source.readLong();
-        appPackageName = source.readString();
-        appIcon = source.readParcelable(null);
-        appLabel = source.readString();
-    }
-
-    /**
-     * Return the ID for this session.
-     */
-    public int getSessionId() {
-        return sessionId;
-    }
-
-    /**
-     * Return the package name of the app that owns this session.
-     */
-    public @Nullable String getInstallerPackageName() {
-        return installerPackageName;
-    }
-
-    /**
-     * Return current overall progress of this session, between 0 and 1.
-     * <p>
-     * Note that this progress may not directly correspond to the value reported
-     * by {@link PackageInstaller.Session#setProgress(float)}, as the system may
-     * carve out a portion of the overall progress to represent its own internal
-     * installation work.
-     */
-    public float getProgress() {
-        return progress;
-    }
-
-    /**
-     * Return if this session is currently open.
-     */
-    public boolean isOpen() {
-        return open;
-    }
-
-    /**
-     * Return the package name this session is working with. May be {@code null}
-     * if unknown.
-     */
-    public @Nullable String getAppPackageName() {
-        return appPackageName;
-    }
-
-    /**
-     * Return an icon representing the app being installed. May be {@code null}
-     * if unavailable.
-     */
-    public @Nullable Bitmap getAppIcon() {
-        return appIcon;
-    }
-
-    /**
-     * Return a label representing the app being installed. May be {@code null}
-     * if unavailable.
-     */
-    public @Nullable CharSequence getAppLabel() {
-        return appLabel;
-    }
-
-    /**
-     * Return an Intent that can be started to view details about this install
-     * session. This may surface actions such as pause, resume, or cancel.
-     * <p>
-     * In some cases, a matching Activity may not exist, so ensure you safeguard
-     * against this.
-     *
-     * @see PackageInstaller#ACTION_SESSION_DETAILS
-     */
-    public @Nullable Intent getDetailsIntent() {
-        final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS);
-        intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
-        intent.setPackage(installerPackageName);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return intent;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(sessionId);
-        dest.writeString(installerPackageName);
-        dest.writeString(resolvedBaseCodePath);
-        dest.writeFloat(progress);
-        dest.writeInt(sealed ? 1 : 0);
-        dest.writeInt(open ? 1 : 0);
-
-        dest.writeInt(mode);
-        dest.writeLong(sizeBytes);
-        dest.writeString(appPackageName);
-        dest.writeParcelable(appIcon, flags);
-        dest.writeString(appLabel != null ? appLabel.toString() : null);
-    }
-
-    public static final Parcelable.Creator<InstallSessionInfo>
-            CREATOR = new Parcelable.Creator<InstallSessionInfo>() {
-                @Override
-                public InstallSessionInfo createFromParcel(Parcel p) {
-                    return new InstallSessionInfo(p);
-                }
-
-                @Override
-                public InstallSessionInfo[] newArray(int size) {
-                    return new InstallSessionInfo[size];
-                }
-            };
-}
diff --git a/core/java/android/content/pm/InstallSessionParams.aidl b/core/java/android/content/pm/InstallSessionParams.aidl
deleted file mode 100644
index 81b7574..0000000
--- a/core/java/android/content/pm/InstallSessionParams.aidl
+++ /dev/null
@@ -1,19 +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.content.pm;
-
-parcelable InstallSessionParams;
diff --git a/core/java/android/content/pm/InstallSessionParams.java b/core/java/android/content/pm/InstallSessionParams.java
deleted file mode 100644
index 1716e39..0000000
--- a/core/java/android/content/pm/InstallSessionParams.java
+++ /dev/null
@@ -1,227 +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.content.pm;
-
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.IndentingPrintWriter;
-
-/**
- * Parameters for creating a new {@link PackageInstaller.Session}.
- */
-public class InstallSessionParams implements Parcelable {
-
-    /** {@hide} */
-    public static final int MODE_INVALID = -1;
-
-    /**
-     * Mode for an install session whose staged APKs should fully replace any
-     * existing APKs for the target app.
-     */
-    public static final int MODE_FULL_INSTALL = 1;
-
-    /**
-     * Mode for an install session that should inherit any existing APKs for the
-     * target app, unless they have been explicitly overridden (based on split
-     * name) by the session. For example, this can be used to add one or more
-     * split APKs to an existing installation.
-     * <p>
-     * If there are no existing APKs for the target app, this behaves like
-     * {@link #MODE_FULL_INSTALL}.
-     */
-    public static final int MODE_INHERIT_EXISTING = 2;
-
-    /** {@hide} */
-    public int mode = MODE_INVALID;
-    /** {@hide} */
-    public int installFlags;
-    /** {@hide} */
-    public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
-    /** {@hide} */
-    public long sizeBytes = -1;
-    /** {@hide} */
-    public String appPackageName;
-    /** {@hide} */
-    public Bitmap appIcon;
-    /** {@hide} */
-    public String appLabel;
-    /** {@hide} */
-    public Uri originatingUri;
-    /** {@hide} */
-    public Uri referrerUri;
-    /** {@hide} */
-    public String abiOverride;
-
-    /**
-     * Construct parameters for a new package install session.
-     *
-     * @param mode one of {@link #MODE_FULL_INSTALL} or
-     *            {@link #MODE_INHERIT_EXISTING} describing how the session
-     *            should interact with an existing app.
-     */
-    public InstallSessionParams(int mode) {
-        this.mode = mode;
-    }
-
-    /** {@hide} */
-    public InstallSessionParams(Parcel source) {
-        mode = source.readInt();
-        installFlags = source.readInt();
-        installLocation = source.readInt();
-        sizeBytes = source.readLong();
-        appPackageName = source.readString();
-        appIcon = source.readParcelable(null);
-        appLabel = source.readString();
-        originatingUri = source.readParcelable(null);
-        referrerUri = source.readParcelable(null);
-        abiOverride = source.readString();
-    }
-
-    /**
-     * Provide value of {@link PackageInfo#installLocation}, which may be used
-     * to determine where the app will be staged. Defaults to
-     * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
-     */
-    public void setInstallLocation(int installLocation) {
-        this.installLocation = installLocation;
-    }
-
-    /**
-     * @deprecated use {@link PackageInstaller.Session#openRead(String)} to
-     *             calculate message digest instead.
-     * @hide
-     */
-    @Deprecated
-    public void setSignatures(@Nullable Signature[] signatures) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Optionally indicate the total size (in bytes) of all APKs that will be
-     * delivered in this session. The system may use this to ensure enough disk
-     * space exists before proceeding, or to estimate container size for
-     * installations living on external storage.
-     *
-     * @see PackageInfo#INSTALL_LOCATION_AUTO
-     * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
-     */
-    public void setSize(long sizeBytes) {
-        this.sizeBytes = sizeBytes;
-    }
-
-    /**
-     * Optionally set the package name of the app being installed. It's strongly
-     * recommended that you provide this value when known, so that observers can
-     * communicate installing apps to users.
-     * <p>
-     * If the APKs staged in the session aren't consistent with this package
-     * name, the install will fail. Regardless of this value, all APKs in the
-     * app must have the same package name.
-     */
-    public void setAppPackageName(@Nullable String appPackageName) {
-        this.appPackageName = appPackageName;
-    }
-
-    /**
-     * Optionally set an icon representing the app being installed. This should
-     * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both
-     * dimensions.
-     */
-    public void setAppIcon(@Nullable Bitmap appIcon) {
-        this.appIcon = appIcon;
-    }
-
-    /**
-     * Optionally set a label representing the app being installed.
-     */
-    public void setAppLabel(@Nullable CharSequence appLabel) {
-        this.appLabel = (appLabel != null) ? appLabel.toString() : null;
-    }
-
-    /**
-     * Optionally set the URI where this package was downloaded from. Used for
-     * verification purposes.
-     *
-     * @see Intent#EXTRA_ORIGINATING_URI
-     */
-    public void setOriginatingUri(@Nullable Uri originatingUri) {
-        this.originatingUri = originatingUri;
-    }
-
-    /**
-     * Optionally set the URI that referred you to install this package. Used
-     * for verification purposes.
-     *
-     * @see Intent#EXTRA_REFERRER
-     */
-    public void setReferrerUri(@Nullable Uri referrerUri) {
-        this.referrerUri = referrerUri;
-    }
-
-    /** {@hide} */
-    public void dump(IndentingPrintWriter pw) {
-        pw.printPair("mode", mode);
-        pw.printHexPair("installFlags", installFlags);
-        pw.printPair("installLocation", installLocation);
-        pw.printPair("sizeBytes", sizeBytes);
-        pw.printPair("appPackageName", appPackageName);
-        pw.printPair("appIcon", (appIcon != null));
-        pw.printPair("appLabel", appLabel);
-        pw.printPair("originatingUri", originatingUri);
-        pw.printPair("referrerUri", referrerUri);
-        pw.printPair("abiOverride", abiOverride);
-        pw.println();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mode);
-        dest.writeInt(installFlags);
-        dest.writeInt(installLocation);
-        dest.writeLong(sizeBytes);
-        dest.writeString(appPackageName);
-        dest.writeParcelable(appIcon, flags);
-        dest.writeString(appLabel);
-        dest.writeParcelable(originatingUri, flags);
-        dest.writeParcelable(referrerUri, flags);
-        dest.writeString(abiOverride);
-    }
-
-    public static final Parcelable.Creator<InstallSessionParams>
-            CREATOR = new Parcelable.Creator<InstallSessionParams>() {
-                @Override
-                public InstallSessionParams createFromParcel(Parcel p) {
-                    return new InstallSessionParams(p);
-                }
-
-                @Override
-                public InstallSessionParams[] newArray(int size) {
-                    return new InstallSessionParams[size];
-                }
-            };
-}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 49ffef2..a0e3c4a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -180,7 +180,7 @@
      * {@link android.R.styleable#AndroidManifestUsesConfiguration
      * &lt;uses-configuration&gt;} tags included under &lt;manifest&gt;,
      * or null if there were none. This is only filled in if the flag
-     * {@link PackageManager#GET_CONFIGURATIONS} was set.  
+     * {@link PackageManager#GET_CONFIGURATIONS} was set.
      */
     public ConfigurationInfo[] configPreferences;
 
@@ -192,6 +192,16 @@
     public FeatureInfo[] reqFeatures;
 
     /**
+     * Groups of features that this application has requested.
+     * Each group contains a set of features that are required.
+     * A device must match the features listed in {@link #reqFeatures} and one
+     * or more FeatureGroups in order to have satisfied the feature requirement.
+     *
+     * @see FeatureInfo#FLAG_REQUIRED
+     */
+    public FeatureGroupInfo[] featureGroups;
+
+    /**
      * Constant corresponding to <code>auto</code> in
      * the {@link android.R.attr#installLocation} attribute.
      * @hide
@@ -300,6 +310,7 @@
         dest.writeTypedArray(signatures, parcelableFlags);
         dest.writeTypedArray(configPreferences, parcelableFlags);
         dest.writeTypedArray(reqFeatures, parcelableFlags);
+        dest.writeTypedArray(featureGroups, parcelableFlags);
         dest.writeInt(installLocation);
         dest.writeInt(requiredForAllUsers ? 1 : 0);
         dest.writeInt(requiredForProfile);
@@ -344,6 +355,7 @@
         signatures = source.createTypedArray(Signature.CREATOR);
         configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
         reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
+        featureGroups = source.createTypedArray(FeatureGroupInfo.CREATOR);
         installLocation = source.readInt();
         requiredForAllUsers = source.readInt() != 0;
         requiredForProfile = source.readInt();
diff --git a/core/java/android/content/pm/PackageInstallerParams.aidl b/core/java/android/content/pm/PackageInstaller.aidl
similarity index 88%
rename from core/java/android/content/pm/PackageInstallerParams.aidl
rename to core/java/android/content/pm/PackageInstaller.aidl
index b3dde21..270f870 100644
--- a/core/java/android/content/pm/PackageInstallerParams.aidl
+++ b/core/java/android/content/pm/PackageInstaller.aidl
@@ -16,4 +16,5 @@
 
 package android.content.pm;
 
-parcelable PackageInstallerParams;
+parcelable PackageInstaller.SessionParams;
+parcelable PackageInstaller.SessionInfo;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d70e22c..aa4ea45 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -20,17 +20,24 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.app.PackageDeleteObserver;
-import android.app.PackageInstallObserver;
+import android.app.ActivityManager;
+import android.content.Context;
 import android.content.Intent;
-import android.os.Bundle;
+import android.content.IntentSender;
+import android.graphics.Bitmap;
+import android.net.Uri;
 import android.os.FileBridge;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.util.ExceptionUtils;
+import android.util.Log;
+
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -67,13 +74,15 @@
  * </ul>
  */
 public class PackageInstaller {
+    private static final String TAG = "PackageInstaller";
+
     /**
      * Activity Action: Show details about a particular install session. This
      * may surface actions such as pause, resume, or cancel.
      * <p>
      * This should always be scoped to the installer package that owns the
-     * session. Clients should use {@link InstallSessionInfo#getDetailsIntent()}
-     * to build this intent correctly.
+     * session. Clients should use {@link SessionInfo#getDetailsIntent()} to
+     * build this intent correctly.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you safeguard
      * against this.
@@ -92,9 +101,110 @@
      */
     public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
 
+    public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
+    public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+
+    /**
+     * List of package names that are relevant to a status.
+     *
+     * @see Intent#getStringArrayExtra(String)
+     */
+    public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
+
+    /** {@hide} */
+    public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
+    /** {@hide} */
+    public static final String EXTRA_LEGACY_BUNDLE = "android.content.pm.extra.LEGACY_BUNDLE";
     /** {@hide} */
     public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
 
+    /**
+     * User action is currently required to proceed. You can launch the intent
+     * activity described by {@link Intent#EXTRA_INTENT} to involve the user and
+     * continue.
+     * <p>
+     * You may choose to immediately launch the intent if the user is actively
+     * using your app. Otherwise, you should use a notification to guide the
+     * user back into your app before launching.
+     *
+     * @see Intent#getParcelableExtra(String)
+     */
+    public static final int STATUS_USER_ACTION_REQUIRED = -1;
+
+    /**
+     * The operation succeeded.
+     */
+    public static final int STATUS_SUCCESS = 0;
+
+    /**
+     * The operation failed in a generic way. The system will always try to
+     * provide a more specific failure reason, but in some rare cases this may
+     * be delivered.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE = 1;
+
+    /**
+     * The operation failed because it was blocked. For example, a device policy
+     * may be blocking the operation, a package verifier may have blocked the
+     * operation, or the app may be required for core system operation.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_BLOCKED = 1;
+
+    /**
+     * The operation failed because it was actively aborted. For example, the
+     * user actively declined requested permissions, or the session was
+     * abandoned.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_ABORTED = 2;
+
+    /**
+     * The operation failed because one or more of the APKs was invalid. For
+     * example, they might be malformed, corrupt, incorrectly signed,
+     * mismatched, etc.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_INVALID = 3;
+
+    /**
+     * The operation failed because it conflicts (or is inconsistent with) with
+     * another package already installed on the device. For example, an existing
+     * permission, incompatible certificates, etc. The user may be able to
+     * uninstall another app to fix the issue.
+     * <p>
+     * The result may also contain {@link #EXTRA_PACKAGE_NAMES} with the
+     * specific packages identified as the cause of the conflict.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_CONFLICT = 4;
+
+    /**
+     * The operation failed because of storage issues. For example, the device
+     * may be running low on space, or external media may be unavailable. The
+     * user may be able to help free space or insert different external media.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_STORAGE = 5;
+
+    /**
+     * The operation failed because it is fundamentally incompatible with this
+     * device. For example, the app may require a hardware feature that doesn't
+     * exist, it may be missing native code for the ABIs supported by the
+     * device, or it requires a newer SDK version, etc.
+     *
+     * @see #EXTRA_STATUS_MESSAGE
+     */
+    public static final int STATUS_FAILURE_INCOMPATIBLE = 6;
+
+    private final Context mContext;
     private final PackageManager mPm;
     private final IPackageInstaller mInstaller;
     private final int mUserId;
@@ -103,8 +213,9 @@
     private final ArrayList<SessionCallbackDelegate> mDelegates = new ArrayList<>();
 
     /** {@hide} */
-    public PackageInstaller(PackageManager pm, IPackageInstaller installer,
+    public PackageInstaller(Context context, PackageManager pm, IPackageInstaller installer,
             String installerPackageName, int userId) {
+        mContext = context;
         mPm = pm;
         mInstaller = installer;
         mInstallerPackageName = installerPackageName;
@@ -126,7 +237,7 @@
      *         This ID remains consistent across device reboots until the
      *         session is finalized. IDs are not reused during a given boot.
      */
-    public int createSession(@NonNull InstallSessionParams params) throws IOException {
+    public int createSession(@NonNull SessionParams params) throws IOException {
         try {
             return mInstaller.createSession(params, mInstallerPackageName, mUserId);
         } catch (RuntimeException e) {
@@ -153,7 +264,7 @@
      * Return details for a specific session. To succeed, the caller must either
      * own this session, or be the current home app.
      */
-    public @Nullable InstallSessionInfo getSessionInfo(int sessionId) {
+    public @Nullable SessionInfo getSessionInfo(int sessionId) {
         try {
             return mInstaller.getSessionInfo(sessionId);
         } catch (RemoteException e) {
@@ -165,7 +276,7 @@
      * Return list of all active install sessions, regardless of the installer.
      * To succeed, the caller must be the current home app.
      */
-    public @NonNull List<InstallSessionInfo> getAllSessions() {
+    public @NonNull List<SessionInfo> getAllSessions() {
         try {
             return mInstaller.getAllSessions(mUserId);
         } catch (RemoteException e) {
@@ -176,7 +287,7 @@
     /**
      * Return list of all install sessions owned by the calling app.
      */
-    public @NonNull List<InstallSessionInfo> getMySessions() {
+    public @NonNull List<SessionInfo> getMySessions() {
         try {
             return mInstaller.getMySessions(mInstallerPackageName, mUserId);
         } catch (RemoteException e) {
@@ -189,25 +300,9 @@
      * method is only available to the current "installer of record" for the
      * package.
      */
-    public void uninstall(@NonNull String packageName, @NonNull UninstallCallback callback) {
+    public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
         try {
-            mInstaller.uninstall(packageName, 0,
-                    new UninstallCallbackDelegate(callback).getBinder(), mUserId);
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Uninstall only a specific split from the given package.
-     *
-     * @hide
-     */
-    public void uninstall(@NonNull String packageName, @NonNull String splitName,
-            @NonNull UninstallCallback callback) {
-        try {
-            mInstaller.uninstallSplit(packageName, splitName, 0,
-                    new UninstallCallbackDelegate(callback).getBinder(), mUserId);
+            mInstaller.uninstall(packageName, 0, statusReceiver, mUserId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
@@ -353,6 +448,14 @@
      *            calling thread.
      */
     public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
+        // TODO: remove this temporary guard once we have new prebuilts
+        final ApplicationInfo info = mContext.getApplicationInfo();
+        if ("com.google.android.googlequicksearchbox".equals(info.packageName)
+                && info.versionCode <= 300400070) {
+            Log.d(TAG, "Ignoring callback request from old prebuilt");
+            return;
+        }
+
         synchronized (mDelegates) {
             final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
                     handler.getLooper());
@@ -436,7 +539,7 @@
          * You can write data into the returned stream, optionally call
          * {@link #fsync(OutputStream)} as needed to ensure bytes have been
          * persisted to disk, and then close when finished. All streams must be
-         * closed before calling {@link #commit(CommitCallback)}.
+         * closed before calling {@link #commit(IntentSender)}.
          *
          * @param name arbitrary, unique name of your choosing to identify the
          *            APK being written. You can open a file again for
@@ -476,14 +579,14 @@
         }
 
         /**
-         * List all APK names contained in this session.
+         * Return all APK names contained in this session.
          * <p>
          * This returns all names which have been previously written through
          * {@link #openWrite(String, long, long)} as part of this session.
          */
-        public @NonNull String[] list() {
+        public @NonNull String[] getNames() {
             try {
-                return mSession.list();
+                return mSession.getNames();
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             }
@@ -518,9 +621,9 @@
          * on the session. If the device reboots before the session has been
          * finalized, you may commit the session again.
          */
-        public void commit(@NonNull CommitCallback callback) {
+        public void commit(@NonNull IntentSender statusReceiver) {
             try {
-                mSession.commit(new CommitCallbackDelegate(callback).getBinder());
+                mSession.commit(statusReceiver);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             }
@@ -553,169 +656,350 @@
     }
 
     /**
-     * Events for a specific uninstall request.
+     * Parameters for creating a new {@link PackageInstaller.Session}.
      */
-    public static abstract class UninstallCallback {
-        /**
-         * Generic unknown failure. The system will always try to provide a more
-         * specific failure reason, but in some rare cases this may be
-         * delivered.
-         */
-        public static final int FAILURE_UNKNOWN = 0;
+    public static class SessionParams implements Parcelable {
+
+        /** {@hide} */
+        public static final int MODE_INVALID = -1;
 
         /**
-         * This uninstall was blocked. The package may be required for core
-         * system operation, or the user may be restricted. Attempting to
-         * uninstall again will have the same result.
+         * Mode for an install session whose staged APKs should fully replace any
+         * existing APKs for the target app.
          */
-        public static final int FAILURE_BLOCKED = 1;
+        public static final int MODE_FULL_INSTALL = 1;
 
         /**
-         * This uninstall was actively aborted. For example, the user declined
-         * to uninstall. You may try to uninstall again.
-         */
-        public static final int FAILURE_ABORTED = 2;
-
-        /**
-         * User action is required to proceed. You can start the given intent
-         * activity to involve the user and continue.
+         * Mode for an install session that should inherit any existing APKs for the
+         * target app, unless they have been explicitly overridden (based on split
+         * name) by the session. For example, this can be used to add one or more
+         * split APKs to an existing installation.
          * <p>
-         * You may choose to immediately launch the intent if the user is
-         * actively using your app. However, you should use a notification to
-         * guide the user back into your app if not currently active.
+         * If there are no existing APKs for the target app, this behaves like
+         * {@link #MODE_FULL_INSTALL}.
          */
-        public abstract void onUserActionRequired(Intent intent);
+        public static final int MODE_INHERIT_EXISTING = 2;
 
-        public abstract void onSuccess();
-        public abstract void onFailure(int failureReason, String msg, Bundle extras);
-    }
+        /** {@hide} */
+        public int mode = MODE_INVALID;
+        /** {@hide} */
+        public int installFlags;
+        /** {@hide} */
+        public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+        /** {@hide} */
+        public long sizeBytes = -1;
+        /** {@hide} */
+        public String appPackageName;
+        /** {@hide} */
+        public Bitmap appIcon;
+        /** {@hide} */
+        public String appLabel;
+        /** {@hide} */
+        public Uri originatingUri;
+        /** {@hide} */
+        public Uri referrerUri;
+        /** {@hide} */
+        public String abiOverride;
 
-    /** {@hide} */
-    private static class UninstallCallbackDelegate extends PackageDeleteObserver {
-        private final UninstallCallback target;
+        /**
+         * Construct parameters for a new package install session.
+         *
+         * @param mode one of {@link #MODE_FULL_INSTALL} or
+         *            {@link #MODE_INHERIT_EXISTING} describing how the session
+         *            should interact with an existing app.
+         */
+        public SessionParams(int mode) {
+            this.mode = mode;
+        }
 
-        public UninstallCallbackDelegate(UninstallCallback target) {
-            this.target = target;
+        /** {@hide} */
+        public SessionParams(Parcel source) {
+            mode = source.readInt();
+            installFlags = source.readInt();
+            installLocation = source.readInt();
+            sizeBytes = source.readLong();
+            appPackageName = source.readString();
+            appIcon = source.readParcelable(null);
+            appLabel = source.readString();
+            originatingUri = source.readParcelable(null);
+            referrerUri = source.readParcelable(null);
+            abiOverride = source.readString();
+        }
+
+        /**
+         * Provide value of {@link PackageInfo#installLocation}, which may be used
+         * to determine where the app will be staged. Defaults to
+         * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
+         */
+        public void setInstallLocation(int installLocation) {
+            this.installLocation = installLocation;
+        }
+
+        /**
+         * Optionally indicate the total size (in bytes) of all APKs that will be
+         * delivered in this session. The system may use this to ensure enough disk
+         * space exists before proceeding, or to estimate container size for
+         * installations living on external storage.
+         *
+         * @see PackageInfo#INSTALL_LOCATION_AUTO
+         * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
+         */
+        public void setSize(long sizeBytes) {
+            this.sizeBytes = sizeBytes;
+        }
+
+        /**
+         * Optionally set the package name of the app being installed. It's strongly
+         * recommended that you provide this value when known, so that observers can
+         * communicate installing apps to users.
+         * <p>
+         * If the APKs staged in the session aren't consistent with this package
+         * name, the install will fail. Regardless of this value, all APKs in the
+         * app must have the same package name.
+         */
+        public void setAppPackageName(@Nullable String appPackageName) {
+            this.appPackageName = appPackageName;
+        }
+
+        /**
+         * Optionally set an icon representing the app being installed. This should
+         * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both
+         * dimensions.
+         */
+        public void setAppIcon(@Nullable Bitmap appIcon) {
+            this.appIcon = appIcon;
+        }
+
+        /**
+         * Optionally set a label representing the app being installed.
+         */
+        public void setAppLabel(@Nullable CharSequence appLabel) {
+            this.appLabel = (appLabel != null) ? appLabel.toString() : null;
+        }
+
+        /**
+         * Optionally set the URI where this package was downloaded from. Used for
+         * verification purposes.
+         *
+         * @see Intent#EXTRA_ORIGINATING_URI
+         */
+        public void setOriginatingUri(@Nullable Uri originatingUri) {
+            this.originatingUri = originatingUri;
+        }
+
+        /**
+         * Optionally set the URI that referred you to install this package. Used
+         * for verification purposes.
+         *
+         * @see Intent#EXTRA_REFERRER
+         */
+        public void setReferrerUri(@Nullable Uri referrerUri) {
+            this.referrerUri = referrerUri;
+        }
+
+        /** {@hide} */
+        public void dump(IndentingPrintWriter pw) {
+            pw.printPair("mode", mode);
+            pw.printHexPair("installFlags", installFlags);
+            pw.printPair("installLocation", installLocation);
+            pw.printPair("sizeBytes", sizeBytes);
+            pw.printPair("appPackageName", appPackageName);
+            pw.printPair("appIcon", (appIcon != null));
+            pw.printPair("appLabel", appLabel);
+            pw.printPair("originatingUri", originatingUri);
+            pw.printPair("referrerUri", referrerUri);
+            pw.printPair("abiOverride", abiOverride);
+            pw.println();
         }
 
         @Override
-        public void onUserActionRequired(Intent intent) {
-            target.onUserActionRequired(intent);
+        public int describeContents() {
+            return 0;
         }
 
         @Override
-        public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
-            if (returnCode == PackageManager.DELETE_SUCCEEDED) {
-                target.onSuccess();
-            } else {
-                final int failureReason = PackageManager.deleteStatusToFailureReason(returnCode);
-                msg = PackageManager.deleteStatusToString(returnCode) + ": " + msg;
-                target.onFailure(failureReason, msg, null);
-            }
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mode);
+            dest.writeInt(installFlags);
+            dest.writeInt(installLocation);
+            dest.writeLong(sizeBytes);
+            dest.writeString(appPackageName);
+            dest.writeParcelable(appIcon, flags);
+            dest.writeString(appLabel);
+            dest.writeParcelable(originatingUri, flags);
+            dest.writeParcelable(referrerUri, flags);
+            dest.writeString(abiOverride);
         }
+
+        public static final Parcelable.Creator<SessionParams>
+                CREATOR = new Parcelable.Creator<SessionParams>() {
+                    @Override
+                    public SessionParams createFromParcel(Parcel p) {
+                        return new SessionParams(p);
+                    }
+
+                    @Override
+                    public SessionParams[] newArray(int size) {
+                        return new SessionParams[size];
+                    }
+                };
     }
 
     /**
-     * Final result of a session commit request.
+     * Details for an active install session.
      */
-    public static abstract class CommitCallback {
-        /**
-         * Generic unknown failure. The system will always try to provide a more
-         * specific failure reason, but in some rare cases this may be
-         * delivered.
-         */
-        public static final int FAILURE_UNKNOWN = 0;
+    public static class SessionInfo implements Parcelable {
+
+        /** {@hide} */
+        public int sessionId;
+        /** {@hide} */
+        public String installerPackageName;
+        /** {@hide} */
+        public String resolvedBaseCodePath;
+        /** {@hide} */
+        public float progress;
+        /** {@hide} */
+        public boolean sealed;
+        /** {@hide} */
+        public boolean open;
+
+        /** {@hide} */
+        public int mode;
+        /** {@hide} */
+        public long sizeBytes;
+        /** {@hide} */
+        public String appPackageName;
+        /** {@hide} */
+        public Bitmap appIcon;
+        /** {@hide} */
+        public CharSequence appLabel;
+
+        /** {@hide} */
+        public SessionInfo() {
+        }
+
+        /** {@hide} */
+        public SessionInfo(Parcel source) {
+            sessionId = source.readInt();
+            installerPackageName = source.readString();
+            resolvedBaseCodePath = source.readString();
+            progress = source.readFloat();
+            sealed = source.readInt() != 0;
+            open = source.readInt() != 0;
+
+            mode = source.readInt();
+            sizeBytes = source.readLong();
+            appPackageName = source.readString();
+            appIcon = source.readParcelable(null);
+            appLabel = source.readString();
+        }
 
         /**
-         * One or more of the APKs included in the session was invalid. For
-         * example, they might be malformed, corrupt, incorrectly signed,
-         * mismatched, etc. The installer may want to try downloading and
-         * installing again.
+         * Return the ID for this session.
          */
-        public static final int FAILURE_INVALID = 1;
+        public int getSessionId() {
+            return sessionId;
+        }
 
         /**
-         * This install session conflicts (or is inconsistent with) with another
-         * package already installed on the device. For example, an existing
-         * permission, incompatible certificates, etc. The user may be able to
-         * uninstall another app to fix the issue.
+         * Return the package name of the app that owns this session.
+         */
+        public @Nullable String getInstallerPackageName() {
+            return installerPackageName;
+        }
+
+        /**
+         * Return current overall progress of this session, between 0 and 1.
          * <p>
-         * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} with the
-         * specific packages identified as the cause of the conflict.
+         * Note that this progress may not directly correspond to the value reported
+         * by {@link PackageInstaller.Session#setProgress(float)}, as the system may
+         * carve out a portion of the overall progress to represent its own internal
+         * installation work.
          */
-        public static final int FAILURE_CONFLICT = 2;
+        public float getProgress() {
+            return progress;
+        }
 
         /**
-         * This install session failed due to storage issues. For example,
-         * the device may be running low on space, or the required external
-         * media may be unavailable. The user may be able to help free space
-         * or insert the correct media.
+         * Return if this session is currently open.
          */
-        public static final int FAILURE_STORAGE = 3;
+        public boolean isOpen() {
+            return open;
+        }
 
         /**
-         * This install session is fundamentally incompatible with this
-         * device. For example, the package may require a hardware feature
-         * that doesn't exist, it may be missing native code for the device
-         * ABI, or it requires a newer SDK version, etc. This install would
-         * never succeed.
+         * Return the package name this session is working with. May be {@code null}
+         * if unknown.
          */
-        public static final int FAILURE_INCOMPATIBLE = 4;
+        public @Nullable String getAppPackageName() {
+            return appPackageName;
+        }
 
         /**
-         * This install session failed because it was actively aborted. For
-         * example, the user declined requested permissions, or a verifier
-         * rejected the session.
+         * Return an icon representing the app being installed. May be {@code null}
+         * if unavailable.
+         */
+        public @Nullable Bitmap getAppIcon() {
+            return appIcon;
+        }
+
+        /**
+         * Return a label representing the app being installed. May be {@code null}
+         * if unavailable.
+         */
+        public @Nullable CharSequence getAppLabel() {
+            return appLabel;
+        }
+
+        /**
+         * Return an Intent that can be started to view details about this install
+         * session. This may surface actions such as pause, resume, or cancel.
+         * <p>
+         * In some cases, a matching Activity may not exist, so ensure you safeguard
+         * against this.
          *
-         * @see PackageManager#VERIFICATION_REJECT
+         * @see PackageInstaller#ACTION_SESSION_DETAILS
          */
-        public static final int FAILURE_ABORTED = 5;
-
-        public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
-
-        /**
-         * User action is required to proceed. You can start the given intent
-         * activity to involve the user and continue.
-         * <p>
-         * You may choose to immediately launch the intent if the user is
-         * actively using your app. However, you should use a notification to
-         * guide the user back into your app if not currently active.
-         */
-        public abstract void onUserActionRequired(Intent intent);
-
-        public abstract void onSuccess();
-        public abstract void onFailure(int failureReason, String msg, Bundle extras);
-    }
-
-    /** {@hide} */
-    private static class CommitCallbackDelegate extends PackageInstallObserver {
-        private final CommitCallback target;
-
-        public CommitCallbackDelegate(CommitCallback target) {
-            this.target = target;
+        public @Nullable Intent getDetailsIntent() {
+            final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS);
+            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+            intent.setPackage(installerPackageName);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return intent;
         }
 
         @Override
-        public void onUserActionRequired(Intent intent) {
-            target.onUserActionRequired(intent);
+        public int describeContents() {
+            return 0;
         }
 
         @Override
-        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
-                Bundle extras) {
-            if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
-                target.onSuccess();
-            } else {
-                final int failureReason = PackageManager.installStatusToFailureReason(returnCode);
-                msg = PackageManager.installStatusToString(returnCode) + ": " + msg;
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(sessionId);
+            dest.writeString(installerPackageName);
+            dest.writeString(resolvedBaseCodePath);
+            dest.writeFloat(progress);
+            dest.writeInt(sealed ? 1 : 0);
+            dest.writeInt(open ? 1 : 0);
 
-                if (extras != null) {
-                    extras.putString(CommitCallback.EXTRA_PACKAGE_NAME,
-                            extras.getString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE));
-                }
-
-                target.onFailure(failureReason, msg, extras);
-            }
+            dest.writeInt(mode);
+            dest.writeLong(sizeBytes);
+            dest.writeString(appPackageName);
+            dest.writeParcelable(appIcon, flags);
+            dest.writeString(appLabel != null ? appLabel.toString() : null);
         }
+
+        public static final Parcelable.Creator<SessionInfo>
+                CREATOR = new Parcelable.Creator<SessionInfo>() {
+                    @Override
+                    public SessionInfo createFromParcel(Parcel p) {
+                        return new SessionInfo(p);
+                    }
+
+                    @Override
+                    public SessionInfo[] newArray(int size) {
+                        return new SessionInfo[size];
+                    }
+                };
     }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b957a15..56b7164 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -28,8 +28,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
-import android.content.pm.PackageInstaller.CommitCallback;
-import android.content.pm.PackageInstaller.UninstallCallback;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
@@ -179,9 +177,9 @@
     /**
      * {@link PackageInfo} flag: return information about
      * hardware preferences in
-     * {@link PackageInfo#configPreferences PackageInfo.configPreferences} and
-     * requested features in {@link PackageInfo#reqFeatures
-     * PackageInfo.reqFeatures}.
+     * {@link PackageInfo#configPreferences PackageInfo.configPreferences},
+     * and requested features in {@link PackageInfo#reqFeatures} and
+     * {@link PackageInfo#featureGroups}.
      */
     public static final int GET_CONFIGURATIONS = 0x00004000;
 
@@ -3797,6 +3795,16 @@
     public abstract boolean isPackageAvailable(String packageName);
 
     /** {@hide} */
+    public static String installStatusToString(int status, String msg) {
+        final String str = installStatusToString(status);
+        if (msg != null) {
+            return str + ": " + msg;
+        } else {
+            return str;
+        }
+    }
+
+    /** {@hide} */
     public static String installStatusToString(int status) {
         switch (status) {
             case INSTALL_SUCCEEDED: return "INSTALL_SUCCEEDED";
@@ -3845,49 +3853,60 @@
     }
 
     /** {@hide} */
-    public static int installStatusToFailureReason(int status) {
+    public static int installStatusToPublicStatus(int status) {
         switch (status) {
-            case INSTALL_FAILED_ALREADY_EXISTS: return CommitCallback.FAILURE_CONFLICT;
-            case INSTALL_FAILED_INVALID_APK: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_FAILED_INVALID_URI: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_FAILED_INSUFFICIENT_STORAGE: return CommitCallback.FAILURE_STORAGE;
-            case INSTALL_FAILED_DUPLICATE_PACKAGE: return CommitCallback.FAILURE_CONFLICT;
-            case INSTALL_FAILED_NO_SHARED_USER: return CommitCallback.FAILURE_CONFLICT;
-            case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return CommitCallback.FAILURE_CONFLICT;
-            case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return CommitCallback.FAILURE_CONFLICT;
-            case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return CommitCallback.FAILURE_INCOMPATIBLE;
-            case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return CommitCallback.FAILURE_CONFLICT;
-            case INSTALL_FAILED_DEXOPT: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_FAILED_OLDER_SDK: return CommitCallback.FAILURE_INCOMPATIBLE;
-            case INSTALL_FAILED_CONFLICTING_PROVIDER: return CommitCallback.FAILURE_CONFLICT;
-            case INSTALL_FAILED_NEWER_SDK: return CommitCallback.FAILURE_INCOMPATIBLE;
-            case INSTALL_FAILED_TEST_ONLY: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return CommitCallback.FAILURE_INCOMPATIBLE;
-            case INSTALL_FAILED_MISSING_FEATURE: return CommitCallback.FAILURE_INCOMPATIBLE;
-            case INSTALL_FAILED_CONTAINER_ERROR: return CommitCallback.FAILURE_STORAGE;
-            case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return CommitCallback.FAILURE_STORAGE;
-            case INSTALL_FAILED_MEDIA_UNAVAILABLE: return CommitCallback.FAILURE_STORAGE;
-            case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_ABORTED;
-            case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_ABORTED;
-            case INSTALL_FAILED_PACKAGE_CHANGED: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_FAILED_UID_CHANGED: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_FAILED_VERSION_DOWNGRADE: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_NOT_APK: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_BAD_MANIFEST: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return CommitCallback.FAILURE_INVALID;
-            case INSTALL_FAILED_INTERNAL_ERROR: return CommitCallback.FAILURE_UNKNOWN;
-            case INSTALL_FAILED_USER_RESTRICTED: return CommitCallback.FAILURE_INCOMPATIBLE;
-            case INSTALL_FAILED_DUPLICATE_PERMISSION: return CommitCallback.FAILURE_CONFLICT;
-            case INSTALL_FAILED_NO_MATCHING_ABIS: return CommitCallback.FAILURE_INCOMPATIBLE;
-            case INSTALL_FAILED_ABORTED: return CommitCallback.FAILURE_ABORTED;
-            default: return CommitCallback.FAILURE_UNKNOWN;
+            case INSTALL_SUCCEEDED: return PackageInstaller.STATUS_SUCCESS;
+            case INSTALL_FAILED_ALREADY_EXISTS: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_INVALID_APK: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_INVALID_URI: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_INSUFFICIENT_STORAGE: return PackageInstaller.STATUS_FAILURE_STORAGE;
+            case INSTALL_FAILED_DUPLICATE_PACKAGE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_NO_SHARED_USER: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_DEXOPT: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_OLDER_SDK: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_CONFLICTING_PROVIDER: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_NEWER_SDK: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_TEST_ONLY: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_MISSING_FEATURE: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_CONTAINER_ERROR: return PackageInstaller.STATUS_FAILURE_STORAGE;
+            case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return PackageInstaller.STATUS_FAILURE_STORAGE;
+            case INSTALL_FAILED_MEDIA_UNAVAILABLE: return PackageInstaller.STATUS_FAILURE_STORAGE;
+            case INSTALL_FAILED_VERIFICATION_TIMEOUT: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            case INSTALL_FAILED_VERIFICATION_FAILURE: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            case INSTALL_FAILED_PACKAGE_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_UID_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_VERSION_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_NOT_APK: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_BAD_MANIFEST: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID;
+            case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
+            case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case INSTALL_FAILED_NO_MATCHING_ABIS: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            default: return PackageInstaller.STATUS_FAILURE;
+        }
+    }
+
+    /** {@hide} */
+    public static String deleteStatusToString(int status, String msg) {
+        final String str = deleteStatusToString(status);
+        if (msg != null) {
+            return str + ": " + msg;
+        } else {
+            return str;
         }
     }
 
@@ -3905,14 +3924,15 @@
     }
 
     /** {@hide} */
-    public static int deleteStatusToFailureReason(int status) {
+    public static int deleteStatusToPublicStatus(int status) {
         switch (status) {
-            case DELETE_FAILED_INTERNAL_ERROR: return UninstallCallback.FAILURE_UNKNOWN;
-            case DELETE_FAILED_DEVICE_POLICY_MANAGER: return UninstallCallback.FAILURE_BLOCKED;
-            case DELETE_FAILED_USER_RESTRICTED: return UninstallCallback.FAILURE_BLOCKED;
-            case DELETE_FAILED_OWNER_BLOCKED: return UninstallCallback.FAILURE_BLOCKED;
-            case DELETE_FAILED_ABORTED: return UninstallCallback.FAILURE_ABORTED;
-            default: return UninstallCallback.FAILURE_UNKNOWN;
+            case DELETE_SUCCEEDED: return PackageInstaller.STATUS_SUCCESS;
+            case DELETE_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
+            case DELETE_FAILED_DEVICE_POLICY_MANAGER: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+            case DELETE_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+            case DELETE_FAILED_OWNER_BLOCKED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+            case DELETE_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
+            default: return PackageInstaller.STATUS_FAILURE;
         }
     }
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 44bf35d..cddefb5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -434,6 +434,11 @@
                 pi.reqFeatures = new FeatureInfo[N];
                 p.reqFeatures.toArray(pi.reqFeatures);
             }
+            N = p.featureGroups != null ? p.featureGroups.size() : 0;
+            if (N > 0) {
+                pi.featureGroups = new FeatureGroupInfo[N];
+                p.featureGroups.toArray(pi.featureGroups);
+            }
         }
         if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
             int N = p.activities.size();
@@ -1502,24 +1507,7 @@
                 XmlUtils.skipCurrentTag(parser);
 
             } else if (tagName.equals("uses-feature")) {
-                FeatureInfo fi = new FeatureInfo();
-                sa = res.obtainAttributes(attrs,
-                        com.android.internal.R.styleable.AndroidManifestUsesFeature);
-                // Note: don't allow this value to be a reference to a resource
-                // that may change.
-                fi.name = sa.getNonResourceString(
-                        com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
-                if (fi.name == null) {
-                    fi.reqGlEsVersion = sa.getInt(
-                            com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
-                            FeatureInfo.GL_ES_VERSION_UNDEFINED);
-                }
-                if (sa.getBoolean(
-                        com.android.internal.R.styleable.AndroidManifestUsesFeature_required,
-                        true)) {
-                    fi.flags |= FeatureInfo.FLAG_REQUIRED;
-                }
-                sa.recycle();
+                FeatureInfo fi = parseUsesFeature(res, attrs);
                 pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
 
                 if (fi.name == null) {
@@ -1531,9 +1519,35 @@
                 XmlUtils.skipCurrentTag(parser);
 
             } else if (tagName.equals("feature-group")) {
-                // Skip this for now until we know what to do with it.
+                FeatureGroupInfo group = new FeatureGroupInfo();
+                ArrayList<FeatureInfo> features = null;
+                final int innerDepth = parser.getDepth();
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                        continue;
+                    }
 
-                XmlUtils.skipCurrentTag(parser);
+                    final String innerTagName = parser.getName();
+                    if (innerTagName.equals("uses-feature")) {
+                        FeatureInfo featureInfo = parseUsesFeature(res, attrs);
+                        // FeatureGroups are stricter and mandate that
+                        // any <uses-feature> declared are mandatory.
+                        featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
+                        features = ArrayUtils.add(features, featureInfo);
+                    } else {
+                        Slog.w(TAG, "Unknown element under <feature-group>: " + innerTagName +
+                                " at " + mArchiveSourcePath + " " +
+                                parser.getPositionDescription());
+                    }
+                    XmlUtils.skipCurrentTag(parser);
+                }
+
+                if (features != null) {
+                    group.features = new FeatureInfo[features.size()];
+                    group.features = features.toArray(group.features);
+                }
+                pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);
 
             } else if (tagName.equals("uses-sdk")) {
                 if (SDK_VERSION > 0) {
@@ -1851,6 +1865,28 @@
         return pkg;
     }
 
+    private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+        FeatureInfo fi = new FeatureInfo();
+        TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestUsesFeature);
+        // Note: don't allow this value to be a reference to a resource
+        // that may change.
+        fi.name = sa.getNonResourceString(
+                com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
+        if (fi.name == null) {
+            fi.reqGlEsVersion = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
+                        FeatureInfo.GL_ES_VERSION_UNDEFINED);
+        }
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestUsesFeature_required, true)) {
+            fi.flags |= FeatureInfo.FLAG_REQUIRED;
+        }
+        sa.recycle();
+        return fi;
+    }
+
     private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
                                         AttributeSet attrs, String[] outError)
             throws XmlPullParserException, IOException {
@@ -4225,6 +4261,9 @@
         // Applications requested features
         public ArrayList<FeatureInfo> reqFeatures = null;
 
+        // Applications requested feature groups
+        public ArrayList<FeatureGroupInfo> featureGroups = null;
+
         public int installLocation;
 
         /* An app that's required for all users and cannot be uninstalled for a user */
diff --git a/core/java/android/content/pm/VerificationParams.java b/core/java/android/content/pm/VerificationParams.java
index bf1f77f..e5119b6 100644
--- a/core/java/android/content/pm/VerificationParams.java
+++ b/core/java/android/content/pm/VerificationParams.java
@@ -24,7 +24,7 @@
 /**
  * Represents verification parameters used to verify packages to be installed.
  *
- * @deprecated callers should migrate to {@link PackageInstallerParams}.
+ * @deprecated callers should migrate to {@link PackageInstaller}.
  * @hide
  */
 @Deprecated
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index cbf4a3d..1cf7797 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -510,4 +510,6 @@
             /*out*/int[/*2*/] dimens);
 
     private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
+
+    static native int nativeGetJpegFooterSize();
 }
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 0337c96..fbe26e5 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -33,6 +33,7 @@
 import android.hardware.camera2.utils.ArrayUtils;
 import android.hardware.camera2.utils.ListUtils;
 import android.hardware.camera2.utils.ParamsUtils;
+import android.hardware.camera2.utils.SizeAreaComparator;
 import android.util.Log;
 import android.util.Range;
 import android.util.Size;
@@ -40,6 +41,8 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 import static com.android.internal.util.Preconditions.*;
@@ -187,8 +190,10 @@
          * flash.*
          */
         mapFlash(m, p);
-
-        // TODO: map other fields
+        /*
+         * jpeg.*
+         */
+        mapJpeg(m, p);
 
         /*
          * scaler.*
@@ -595,6 +600,16 @@
         m.set(FLASH_INFO_AVAILABLE, flashAvailable);
     }
 
+    private static void mapJpeg(CameraMetadataNative m, Camera.Parameters p) {
+        List<Camera.Size> thumbnailSizes = p.getSupportedJpegThumbnailSizes();
+
+        if (thumbnailSizes != null) {
+            Size[] sizes = convertSizeListToArray(thumbnailSizes);
+            Arrays.sort(sizes, new SizeAreaComparator());
+            m.set(JPEG_AVAILABLE_THUMBNAIL_SIZES, sizes);
+        }
+    }
+
     private static void mapRequest(CameraMetadataNative m, Parameters p) {
         /*
          * request.availableCapabilities
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 35646fe..4c4ad0d2 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -24,6 +24,7 @@
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.utils.ListUtils;
 import android.hardware.camera2.utils.ParamsUtils;
+import android.location.Location;
 import android.util.Log;
 import android.util.Range;
 import android.util.Size;
@@ -44,6 +45,8 @@
     private static final String TAG = "LegacyRequestMapper";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
+    private static final byte DEFAULT_JPEG_QUALITY = 85;
+
     /**
      * Set the legacy parameters using the {@link LegacyRequest legacy request}.
      *
@@ -350,6 +353,70 @@
                         + testPatternMode + "; only OFF is supported");
             }
         }
+
+        /*
+         * jpeg.*
+         */
+
+        // jpeg.gpsLocation
+        {
+            Location location = request.get(JPEG_GPS_LOCATION);
+            if (location != null) {
+                if (checkForCompleteGpsData(location)) {
+                    params.setGpsAltitude(location.getAltitude());
+                    params.setGpsLatitude(location.getLatitude());
+                    params.setGpsLongitude(location.getLongitude());
+                    params.setGpsProcessingMethod(location.getProvider().toUpperCase());
+                    params.setGpsTimestamp(location.getTime());
+                } else {
+                    Log.w(TAG, "Incomplete GPS parameters provided in location " + location);
+                }
+            } else {
+                params.removeGpsData();
+            }
+        }
+
+        // jpeg.orientation
+        {
+            int orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+            params.setRotation(ParamsUtils.getOrDefault(request, JPEG_ORIENTATION, orientation));
+        }
+
+        // jpeg.quality
+        {
+            params.setJpegQuality(0xFF & ParamsUtils.getOrDefault(request, JPEG_QUALITY,
+                    DEFAULT_JPEG_QUALITY));
+        }
+
+        // jpeg.thumbnailQuality
+        {
+            params.setJpegQuality(0xFF & ParamsUtils.getOrDefault(request, JPEG_THUMBNAIL_QUALITY,
+                    DEFAULT_JPEG_QUALITY));
+        }
+
+        // jpeg.thumbnailSize
+        {
+            List<Camera.Size> sizes = params.getSupportedJpegThumbnailSizes();
+
+            if (sizes != null && sizes.size() > 0) {
+                Size s = request.get(JPEG_THUMBNAIL_SIZE);
+                boolean invalidSize = (s == null) ? false : !ParameterUtils.containsSize(sizes,
+                        s.getWidth(), s.getHeight());
+                if (invalidSize) {
+                    Log.w(TAG, "Invalid JPEG thumbnail size set " + s + ", skipping thumbnail...");
+                }
+                if (s == null || invalidSize) {
+                    // (0,0) = "no thumbnail" in Camera API 1
+                    params.setJpegThumbnailSize(/*width*/0, /*height*/0);
+                } else {
+                    params.setJpegThumbnailSize(s.getWidth(), s.getHeight());
+                }
+            }
+        }
+    }
+
+    private static boolean checkForCompleteGpsData(Location location) {
+        return location != null && location.getProvider() != null && location.getTime() != 0;
     }
 
     static int filterSupportedCaptureIntent(int captureIntent) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index a2f9b4c..090a822 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -28,6 +28,7 @@
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.utils.ListUtils;
 import android.hardware.camera2.utils.ParamsUtils;
+import android.location.Location;
 import android.util.Log;
 import android.util.Size;
 
@@ -250,6 +251,29 @@
             result.set(SENSOR_TEST_PATTERN_MODE, SENSOR_TEST_PATTERN_MODE_OFF);
         }
 
+        /*
+         * jpeg
+         */
+        // jpeg.gpsLocation
+        result.set(JPEG_GPS_LOCATION, request.get(CaptureRequest.JPEG_GPS_LOCATION));
+
+        // jpeg.orientation
+        result.set(JPEG_ORIENTATION, request.get(CaptureRequest.JPEG_ORIENTATION));
+
+        // jpeg.quality
+        result.set(JPEG_QUALITY, (byte) params.getJpegQuality());
+
+        // jpeg.thumbnailQuality
+        result.set(JPEG_THUMBNAIL_QUALITY, (byte) params.getJpegThumbnailQuality());
+
+        // jpeg.thumbnailSize
+        Camera.Size s = params.getJpegThumbnailSize();
+        if (s != null) {
+            result.set(JPEG_THUMBNAIL_SIZE, ParameterUtils.convertSize(s));
+        } else {
+            Log.w(TAG, "Null thumbnail size received from parameters.");
+        }
+
         // TODO: Remaining result metadata tags conversions.
         return result;
     }
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index 385f844..98adcea 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -253,6 +253,33 @@
     }
 
     /**
+     * Convert a camera API1 list of sizes into an array of sizes
+     */
+    public static Size[] convertSizeListToArray(List<Camera.Size> sizeList) {
+        checkNotNull(sizeList, "sizeList must not be null");
+
+        Size[] array = new Size[sizeList.size()];
+        int ctr = 0;
+        for (Camera.Size s : sizeList) {
+            array[ctr++] = new Size(s.width, s.height);
+        }
+        return array;
+    }
+
+    /**
+     * Check if the camera API1 list of sizes contains a size with the given dimens.
+     */
+    public static boolean containsSize(List<Camera.Size> sizeList, int width, int height) {
+        checkNotNull(sizeList, "sizeList must not be null");
+        for (Camera.Size s : sizeList) {
+            if (s.height == height && s.width == width) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns the largest supported picture size, as compared by its area.
      */
     public static Size getLargestSupportedJpegSizeByArea(Camera.Parameters params) {
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 5c66753..eb8debb 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -190,7 +190,8 @@
                 try {
                     if (RequestHolder.jpegType(s)) {
                         Log.i(TAG, "Producing jpeg buffer...");
-                        LegacyCameraDevice.setSurfaceDimens(s, data.length, /*height*/1);
+                        LegacyCameraDevice.setSurfaceDimens(s, data.length +
+                                LegacyCameraDevice.nativeGetJpegFooterSize(), /*height*/1);
                         LegacyCameraDevice.setNextTimestamp(s, timestamp);
                         LegacyCameraDevice.produceFrame(s, data, data.length, /*height*/1,
                                 CameraMetadataNative.NATIVE_JPEG_FORMAT);
@@ -665,10 +666,6 @@
                                 }
                                 mReceivedJpeg.close();
                                 doJpegCapturePrepare(holder);
-                                if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
-                                    // TODO: report error to CameraDevice
-                                    Log.e(TAG, "Hit timeout for jpeg callback!");
-                                }
                             }
 
                             /*
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 3f24b2c..b1b0f9b 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -16,6 +16,7 @@
 package android.hardware.camera2.legacy;
 
 import android.graphics.ImageFormat;
+import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
 import android.os.Environment;
 import android.opengl.EGL14;
@@ -194,6 +195,9 @@
         checkGlError("onDrawFrame start");
         st.getTransformMatrix(mSTMatrix);
 
+        Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
+
+        // Find intermediate buffer dimensions
         Size dimens;
         try {
             dimens = LegacyCameraDevice.getTextureSize(st);
@@ -201,9 +205,6 @@
             // Should never hit this.
             throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
         }
-
-        Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
-
         float texWidth = dimens.getWidth();
         float texHeight = dimens.getHeight();
 
@@ -211,32 +212,30 @@
             throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
         }
 
-        // Find largest scaling factor from the intermediate texture dimension to the
-        // output surface dimension.  Scaling the intermediate texture by this allows
-        // us to letterbox/pillerbox the output surface into the intermediate texture.
-        float widthRatio = width / texWidth;
-        float heightRatio = height / texHeight;
-        float actual = (widthRatio < heightRatio) ? heightRatio : widthRatio;
+        // Letterbox or pillerbox output dimensions into intermediate dimensions.
+        RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight);
+        RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
+        android.graphics.Matrix boxingXform = new android.graphics.Matrix();
+        boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER);
+        boxingXform.mapRect(output);
+
+        // Find scaling factor from pillerboxed/letterboxed output dimensions to intermediate
+        // buffer dimensions.
+        float scaleX = intermediate.width() / output.width();
+        float scaleY = intermediate.height() / output.height();
+
+        // Scale opposite dimension in clip coordinates so output is letterboxed/pillerboxed into
+        // the intermediate dimensions (rather than vice-versa).
+        Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleY, /*y*/scaleX, /*z*/1);
 
         if (DEBUG) {
-            Log.d(TAG, "Scaling factor " + actual + " used for " + width + "x" + height +
-                    " surface, intermediate buffer size is " + texWidth + "x" + texHeight);
+            Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " +
+                    width + "x" + height + " surface, intermediate buffer size is " + texWidth +
+                    "x" + texHeight);
         }
 
-        // Set the viewport height and width to be the scaled intermediate texture dimensions.
-        int viewportW = (int) (actual * texWidth);
-        int viewportH = (int) (actual * texHeight);
-
-        // Set the offset of the viewport so that the output surface is centered in the viewport.
-        float dx = (width - viewportW) / 2f;
-        float dy = (height - viewportH) / 2f;
-
-        if (DEBUG) {
-            Log.d(TAG, "Translation " + dx + "," + dy + " used for " + width + "x" + height +
-                    " surface");
-        }
-
-        GLES20.glViewport((int) dx, (int) dy, viewportW, viewportH);
+        // Set viewport to be output buffer dimensions
+        GLES20.glViewport(0, 0, width, height);
 
         if (DEBUG) {
             GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
@@ -251,7 +250,7 @@
 
         mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
         GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT,
-                /*normalized*/ false,TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+                /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
         checkGlError("glVertexAttribPointer maPosition");
         GLES20.glEnableVertexAttribArray(maPositionHandle);
         checkGlError("glEnableVertexAttribArray maPositionHandle");
@@ -654,6 +653,8 @@
             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
                 makeCurrent(holder.eglSurface);
                 try {
+                    LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
+                            holder.height);
                     LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
                     drawFrame(mSurfaceTexture, holder.width, holder.height);
                     swapBuffers(holder.eglSurface);
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 6160bc2..dcb2892 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -56,6 +56,10 @@
     private ProxyInfo mHttpProxy;
     private int mMtu;
 
+    private static final int MIN_MTU    = 68;
+    private static final int MIN_MTU_V6 = 1280;
+    private static final int MAX_MTU    = 10000;
+
     // Stores the properties of links that are "stacked" above this link.
     // Indexed by interface name to allow modification and to prevent duplicates being added.
     private Hashtable<String, LinkProperties> mStackedLinks =
@@ -996,4 +1000,17 @@
                 return new LinkProperties[size];
             }
         };
+
+        /**
+         * Check the valid MTU range based on IPv4 or IPv6.
+         * @hide
+         */
+        public static boolean isValidMtu(int mtu, boolean ipv6) {
+            if (ipv6) {
+                if ((mtu >= MIN_MTU_V6 && mtu <= MAX_MTU)) return true;
+            } else {
+                if ((mtu >= MIN_MTU && mtu <= MAX_MTU)) return true;
+            }
+            return false;
+        }
 }
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index ee4d45e..5815fa6 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -59,5 +59,5 @@
 
     void registerLockscreenDispatch(INfcLockscreenDispatch lockscreenDispatch, in int[] techList);
     void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
-    void removeNfcUnlockHandler(IBinder b);
+    void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index dde2cf1..6bd5a32 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -311,7 +311,7 @@
 
     final NfcActivityManager mNfcActivityManager;
     final Context mContext;
-    final HashMap<NfcUnlockHandler, IBinder> mNfcUnlockHandlers;
+    final HashMap<NfcUnlockHandler, INfcUnlockHandler> mNfcUnlockHandlers;
     final Object mLock;
 
     /**
@@ -542,7 +542,7 @@
     NfcAdapter(Context context) {
         mContext = context;
         mNfcActivityManager = new NfcActivityManager(this);
-        mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, IBinder>();
+        mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>();
         mLock = new Object();
     }
 
@@ -1498,27 +1498,37 @@
      * <p /> The parameter {@code tagTechnologies} determines which Tag technologies will be polled for
      * at the lockscreen. Polling for less tag technologies reduces latency, and so it is
      * strongly recommended to only provide the Tag technologies that the handler is expected to
-     * receive.
+     * receive. There must be at least one tag technology provided, otherwise the unlock handler
+     * is ignored.
      *
      * @hide
      */
     @SystemApi
     public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler,
                                        String[] tagTechnologies) {
-        try {
-            INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() {
-                @Override
-                public boolean onUnlockAttempted(Tag tag) throws RemoteException {
-                    return unlockHandler.onUnlockAttempted(tag);
-                }
-            };
+        // If there are no tag technologies, don't bother adding unlock handler
+        if (tagTechnologies.length == 0) {
+            return false;
+        }
 
+        try {
             synchronized (mLock) {
                 if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
-                    return true;
+                    // update the tag technologies
+                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
+                    mNfcUnlockHandlers.remove(unlockHandler);
                 }
-                sService.addNfcUnlockHandler(iHandler, Tag.getTechCodesFromStrings(tagTechnologies));
-                mNfcUnlockHandlers.put(unlockHandler, iHandler.asBinder());
+
+                INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() {
+                    @Override
+                    public boolean onUnlockAttempted(Tag tag) throws RemoteException {
+                        return unlockHandler.onUnlockAttempted(tag);
+                    }
+                };
+
+                sService.addNfcUnlockHandler(iHandler,
+                        Tag.getTechCodesFromStrings(tagTechnologies));
+                mNfcUnlockHandlers.put(unlockHandler, iHandler);
             }
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
@@ -1542,8 +1552,7 @@
         try {
             synchronized (mLock) {
                 if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
-                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
-                    mNfcUnlockHandlers.remove(unlockHandler);
+                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.remove(unlockHandler));
                 }
 
                 return true;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index f9e7b78..9d1a7bc 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -499,7 +499,12 @@
      * Sets all the user-wide restrictions for this user.
      * Requires the MANAGE_USERS permission.
      * @param restrictions the Bundle containing all the restrictions.
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
      */
+    @Deprecated
     public void setUserRestrictions(Bundle restrictions) {
         setUserRestrictions(restrictions, Process.myUserHandle());
     }
@@ -509,7 +514,12 @@
      * Requires the MANAGE_USERS permission.
      * @param restrictions the Bundle containing all the restrictions.
      * @param userHandle the UserHandle of the user for whom to set the restrictions.
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
      */
+    @Deprecated
     public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
         try {
             mService.setUserRestrictions(restrictions, userHandle.getIdentifier());
@@ -523,7 +533,12 @@
      * Requires the MANAGE_USERS permission.
      * @param key the key of the restriction
      * @param value the value for the restriction
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
      */
+    @Deprecated
     public void setUserRestriction(String key, boolean value) {
         Bundle bundle = getUserRestrictions();
         bundle.putBoolean(key, value);
@@ -537,7 +552,12 @@
      * @param key the key of the restriction
      * @param value the value for the restriction
      * @param userHandle the user whose restriction is to be changed.
+     * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+     * android.content.ComponentName, String)} or
+     * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+     * android.content.ComponentName, String)} instead.
      */
+    @Deprecated
     public void setUserRestriction(String key, boolean value, UserHandle userHandle) {
         Bundle bundle = getUserRestrictions(userHandle);
         bundle.putBoolean(key, value);
@@ -718,7 +738,7 @@
     /**
      * Returns list of the profiles of userHandle including
      * userHandle itself.
-     * Note that it this returns both enabled and not enabled profiles. See
+     * Note that this returns both enabled and not enabled profiles. See
      * {@link #getUserProfiles()} if you need only the enabled ones.
      *
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8886559..e075d8b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2667,6 +2667,16 @@
         };
 
         /**
+         * These entries are considered common between the personal and the managed profile,
+         * since the managed profile doesn't get to change them.
+         * @hide
+         */
+        public static final String[] CLONE_TO_MANAGED_PROFILE = {
+            DATE_FORMAT,
+            TIME_12_24
+        };
+
+        /**
          * When to use Wi-Fi calling
          *
          * @see android.telephony.TelephonyManager.WifiCallingChoices
@@ -4797,6 +4807,26 @@
         };
 
         /**
+         * These entries are considered common between the personal and the managed profile,
+         * since the managed profile doesn't get to change them.
+         * @hide
+         */
+        public static final String[] CLONE_TO_MANAGED_PROFILE = {
+            ACCESSIBILITY_ENABLED,
+            ALLOW_MOCK_LOCATION,
+            ALLOWED_GEOLOCATION_ORIGINS,
+            DEFAULT_INPUT_METHOD,
+            ENABLED_ACCESSIBILITY_SERVICES,
+            ENABLED_INPUT_METHODS,
+            LOCATION_MODE,
+            LOCATION_PROVIDERS_ALLOWED,
+            LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+            SELECTED_INPUT_METHOD_SUBTYPE,
+            SELECTED_SPELL_CHECKER,
+            SELECTED_SPELL_CHECKER_SUBTYPE
+        };
+
+        /**
          * Helper method for determining if a location provider is enabled.
          *
          * @param cr the content resolver to use
@@ -6440,6 +6470,14 @@
         public static final String GUEST_USER_ENABLED = "guest_user_enabled";
 
         /**
+         * Whether the NetworkScoringService has been first initialized.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         * @hide
+         */
+        public static final String NETWORK_SCORING_PROVISIONED = "network_scoring_provisioned";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 697cdc6..980ead0 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -541,6 +541,15 @@
         ALOGE("%s: Error while setting surface dimens %s (%d).", __FUNCTION__, strerror(-err), err);
         return err;
     }
+
+    // WAR - Set user dimensions also to avoid incorrect scaling after TextureView orientation
+    // change.
+    err = native_window_set_buffers_user_dimensions(anw.get(), width, height);
+    if (err != NO_ERROR) {
+        ALOGE("%s: Error while setting surface user dimens %s (%d).", __FUNCTION__, strerror(-err),
+                err);
+        return err;
+    }
     return NO_ERROR;
 }
 
@@ -624,6 +633,11 @@
     return NO_ERROR;
 }
 
+static jint LegacyCameraDevice_nativeGetJpegFooterSize(JNIEnv* env, jobject thiz) {
+    ALOGV("nativeGetJpegFooterSize");
+    return static_cast<jint>(sizeof(struct camera3_jpeg_blob));
+}
+
 } // extern "C"
 
 static JNINativeMethod gCameraDeviceMethods[] = {
@@ -657,6 +671,9 @@
     { "nativeSetNextTimestamp",
     "(Landroid/view/Surface;J)I",
     (void *)LegacyCameraDevice_nativeSetNextTimestamp },
+    { "nativeGetJpegFooterSize",
+    "()I",
+    (void *)LegacyCameraDevice_nativeGetJpegFooterSize },
 };
 
 // Get all the required offsets in java class and register native functions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 931d1c6..aee3090 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1257,6 +1257,14 @@
         android:description="@string/permdesc_bind_call_service"
         android:label="@string/permlab_bind_call_service" />
 
+    <!-- @SystemApi Allows an application to bind to ConnectionService implementations.
+         @hide -->
+    <permission android:name="android.permission.BIND_CONNECTION_SERVICE"
+                android:permissionGroup="android.permission-group.PHONE_CALLS"
+                android:protectionLevel="system|signature"
+                android:description="@string/permdesc_bind_connection_service"
+                android:label="@string/permlab_bind_connection_service" />
+
     <!-- ================================== -->
     <!-- Permissions for sdcard interaction -->
     <!-- ================================== -->
diff --git a/core/res/res/animator/leanback_setup_fragment_close_enter.xml b/core/res/res/animator/leanback_setup_fragment_close_enter.xml
new file mode 100644
index 0000000..1626dd3
--- /dev/null
+++ b/core/res/res/animator/leanback_setup_fragment_close_enter.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="@dimen/leanback_setup_alpha_backward_in_content_start"
+        android:valueTo="@dimen/leanback_setup_alpha_backward_in_content_end"
+        android:duration="@integer/leanback_setup_alpha_backward_in_content_duration"
+        android:startOffset="@integer/leanback_setup_alpha_backward_in_content_delay"/>
+</set>
diff --git a/core/res/res/animator/leanback_setup_fragment_close_exit.xml b/core/res/res/animator/leanback_setup_fragment_close_exit.xml
new file mode 100644
index 0000000..a827df4
--- /dev/null
+++ b/core/res/res/animator/leanback_setup_fragment_close_exit.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="@dimen/leanback_setup_alpha_backward_out_content_start"
+        android:valueTo="@dimen/leanback_setup_alpha_backward_out_content_end"
+        android:duration="@integer/leanback_setup_alpha_backward_out_content_duration"
+        android:startOffset="@integer/leanback_setup_alpha_backward_out_content_delay"/>
+    <objectAnimator
+        android:propertyName="x"
+        android:valueType="floatType"
+        android:valueFrom="@dimen/leanback_setup_translation_backward_out_content_start"
+        android:valueTo="@dimen/leanback_setup_translation_backward_out_content_end"
+        android:duration="@integer/leanback_setup_translation_backward_out_content_duration"
+        android:startOffset="@integer/leanback_setup_translation_backward_out_content_delay"/>
+</set>
diff --git a/core/res/res/animator/leanback_setup_fragment_open_enter.xml b/core/res/res/animator/leanback_setup_fragment_open_enter.xml
new file mode 100644
index 0000000..34b9a57
--- /dev/null
+++ b/core/res/res/animator/leanback_setup_fragment_open_enter.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="@dimen/leanback_setup_alpha_forward_in_content_start"
+        android:valueTo="@dimen/leanback_setup_alpha_forward_in_content_end"
+        android:duration="@integer/leanback_setup_alpha_forward_in_content_duration"
+        android:startOffset="@integer/leanback_setup_alpha_forward_in_content_delay"/>
+    <objectAnimator
+        android:propertyName="x"
+        android:valueType="floatType"
+        android:valueFrom="@dimen/leanback_setup_translation_forward_in_content_start"
+        android:valueTo="@dimen/leanback_setup_translation_forward_in_content_end"
+        android:duration="@integer/leanback_setup_translation_forward_in_content_duration"
+        android:startOffset="@integer/leanback_setup_translation_forward_in_content_delay" />
+</set>
diff --git a/core/res/res/animator/leanback_setup_fragment_open_exit.xml b/core/res/res/animator/leanback_setup_fragment_open_exit.xml
new file mode 100644
index 0000000..5622db4
--- /dev/null
+++ b/core/res/res/animator/leanback_setup_fragment_open_exit.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="@dimen/leanback_setup_alpha_forward_out_content_start"
+        android:valueTo="@dimen/leanback_setup_alpha_forward_out_content_end"
+        android:duration="@integer/leanback_setup_alpha_forward_out_content_duration"
+        android:startOffset="@integer/leanback_setup_alpha_forward_out_content_delay"/>
+</set>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c268d97..7d4c37e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1250,7 +1250,9 @@
          application.
 
          <p>This appears as a child tag of the root
-         {@link #AndroidManifest manifest} tag. -->
+         {@link #AndroidManifest manifest} tag.
+
+         @deprecated Use <code>feature-group</code> instead.-->
     <declare-styleable name="AndroidManifestUsesConfiguration" parent="AndroidManifest">
         <!-- The type of touch screen used by an application. -->
         <attr name="reqTouchScreen" />
diff --git a/core/res/res/values/dimens_leanback.xml b/core/res/res/values/dimens_leanback.xml
index fb5f8f0..c824a2a 100644
--- a/core/res/res/values/dimens_leanback.xml
+++ b/core/res/res/values/dimens_leanback.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?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");
@@ -13,7 +13,74 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources>
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
     <!-- Default alpha value for disabled elements. -->
     <item name="disabled_alpha_leanback_formwizard" format="float" type="dimen">0.2</item>
+    <!-- The duration of most animations related to screen content transitions -->
+    <integer name="leanback_setup_base_animation_duration">500</integer>
+    <item name="leanback_setup_alpha_animiation_max_opacity" format="float" type="dimen">1.0</item>
+    <item name="leanback_setup_alpha_animiation_min_opacity" format="float" type="dimen">0.0</item>
+    <!-- Where stable, on-screen content rests -->
+    <dimen name="leanback_setup_translation_content_resting_point">0dp</dimen>
+    <integer name="leanback_setup_translation_content_resting_point_v4">0</integer>
+    <!-- The screen position at which content enters/exits. If you're over the edge of the cliff, we can't see you. -->
+    <dimen name="leanback_setup_translation_content_cliff">100dp</dimen>
+    <integer name="leanback_setup_translation_content_cliff_v4">200</integer>
+
+    <!-- Opacity animation for activity background -->
+    <!-- The opacity of the background of the new activity background when the alpha animation starts-->
+    <item name="leanback_setup_alpha_activity_in_bkg_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+    <!-- The opacity of the background of the new activity background when the alpha animation ends-->
+    <item name="leanback_setup_alpha_activity_in_bkg_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+    <integer name="leanback_setup_alpha_activity_in_bkg_delay">0</integer>
+    <integer name="leanback_setup_alpha_activity_in_bkg_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+    <item name="leanback_setup_alpha_activity_out_bkg_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+    <!-- The opacity of the background of the new activity background when the alpha animation ends-->
+    <item name="leanback_setup_alpha_activity_out_bkg_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+    <integer name="leanback_setup_alpha_activity_out_bkg_delay">0</integer>
+    <integer name="leanback_setup_alpha_activity_out_bkg_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+    <!-- Content forward animation configuration values -->
+    <!-- Parameter for alpha animation of new content coming on to the screen when we're moving "forward" -->
+    <!--  Initial opacity of the new content that is coming on to the screen -->
+    <item name="leanback_setup_alpha_forward_in_content_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+    <item name="leanback_setup_alpha_forward_in_content_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+    <integer name="leanback_setup_alpha_forward_in_content_delay">0</integer>
+    <integer name="leanback_setup_alpha_forward_in_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+    <item name="leanback_setup_alpha_forward_out_content_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+    <item name="leanback_setup_alpha_forward_out_content_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+    <integer name="leanback_setup_alpha_forward_out_content_delay">0</integer>
+    <integer name="leanback_setup_alpha_forward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+    <!-- Position animation of incoming content during a "forward" transition -->
+    <dimen name="leanback_setup_translation_forward_in_content_start">@dimen/leanback_setup_translation_content_cliff</dimen>
+    <dimen name="leanback_setup_translation_forward_in_content_start_v4">@integer/leanback_setup_translation_content_cliff_v4</dimen>
+    <dimen name="leanback_setup_translation_forward_in_content_end">@dimen/leanback_setup_translation_content_resting_point</dimen>
+    <dimen name="leanback_setup_translation_forward_in_content_end_v4">@integer/leanback_setup_translation_content_resting_point_v4</dimen>
+    <integer name="leanback_setup_translation_forward_in_content_delay">0</integer>
+    <integer name="leanback_setup_translation_forward_in_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+    <!-- Content backward animation configuration values -->
+    <!-- Alpha animation values for the content that will be displayed after the transition is complete, this is the content coming in. -->
+    <item name="leanback_setup_alpha_backward_in_content_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+    <item name="leanback_setup_alpha_backward_in_content_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+    <integer name="leanback_setup_alpha_backward_in_content_delay">0</integer>
+    <integer name="leanback_setup_alpha_backward_in_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+    <!-- Alpha animiation values for the content that is displayed when the transition starts, this is the content going away. -->
+    <item name="leanback_setup_alpha_backward_out_content_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+    <item name="leanback_setup_alpha_backward_out_content_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+    <integer name="leanback_setup_alpha_backward_out_content_delay">0</integer>
+    <integer name="leanback_setup_alpha_backward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+    <!-- Position animation for content that is displayed when the transition starts, this is the content going away. -->
+    <dimen name="leanback_setup_translation_backward_out_content_start">@dimen/leanback_setup_translation_content_resting_point</dimen>
+    <dimen name="leanback_setup_translation_backward_out_content_start_v4">@integer/leanback_setup_translation_content_resting_point_v4</dimen>
+    <dimen name="leanback_setup_translation_backward_out_content_end">@dimen/leanback_setup_translation_content_cliff</dimen>
+    <dimen name="leanback_setup_translation_backward_out_content_end_v4">@integer/leanback_setup_translation_content_cliff_v4</dimen>
+    <integer name="leanback_setup_translation_backward_out_content_delay">0</integer>
+    <integer name="leanback_setup_translation_backward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8065a9c..6262f13 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2117,6 +2117,11 @@
     <string name="permdesc_bind_call_service">Allows the app to control when and how the user sees the in-call screen.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bind_connection_service">interact with telephony services</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bind_connection_service">Allows the app to interact with telephony services to make/receive calls.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readNetworkUsageHistory">read historical network usage</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readNetworkUsageHistory">Allows the app to read historical network usage for specific networks and apps.</string>
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index 72735f7..da83c36 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -65,4 +65,10 @@
         <item name="fontFamily">sans-serif-condensed</item>
     </style>
 
+    <style name="WindowAnimationStyle.Leanback.Setup" parent="@style/Animation.Material.Activity">
+        <item name="android:fragmentOpenEnterAnimation">@animator/leanback_setup_fragment_open_enter</item>
+        <item name="android:fragmentOpenExitAnimation">@animator/leanback_setup_fragment_open_exit</item>
+        <item name="android:fragmentCloseEnterAnimation">@animator/leanback_setup_fragment_close_enter</item>
+        <item name="android:fragmentCloseExitAnimation">@animator/leanback_setup_fragment_close_exit</item>
+    </style>
 </resources>
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index 1cda843..0a2c0a4 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -58,5 +58,6 @@
         <item name="textAppearanceListItem">@style/TextAppearance.Leanback.FormWizard.ListItem</item>
         <item name="textAppearance">@style/TextAppearance.Leanback.FormWizard</item>
         <item name="textColorPrimary">@color/primary_text_leanback_formwizard_dark</item>
+        <item name="windowAnimationStyle">@style/WindowAnimationStyle.Leanback.Setup</item>
     </style>
 </resources>
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index fc38e8a..14aa570 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -119,11 +119,11 @@
      *
      * @param fromId Unique identifier of the starting keyframe
      * @param toId Unique identifier of the ending keyframe
-     * @param transition An animatable drawable to use as a transition, may not be null
+     * @param transition An {@link Animatable} drawable to use as a transition, may not be null
      * @param reversible Whether the transition can be reversed
      */
-    public void addTransition(int fromId, int toId, @NonNull Drawable transition,
-            boolean reversible) {
+    public <T extends Drawable & Animatable> void addTransition(int fromId, int toId,
+            @NonNull T transition, boolean reversible) {
         if (transition == null) {
             throw new IllegalArgumentException("Transition drawable must not be null");
         }
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 766e681..9ac6927 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -49,7 +49,7 @@
 import java.util.Stack;
 
 /**
- * This lets you create a drawable based on an XML vector graphic It can be
+ * This lets you create a drawable based on an XML vector graphic. It can be
  * defined in an XML file with the <code>&lt;vector></code> element.
  * <p/>
  * The vector drawable has the following elements:
diff --git a/graphics/java/android/graphics/drawable/shapes/Shape.java b/graphics/java/android/graphics/drawable/shapes/Shape.java
index 589fbaa..eab8666 100644
--- a/graphics/java/android/graphics/drawable/shapes/Shape.java
+++ b/graphics/java/android/graphics/drawable/shapes/Shape.java
@@ -16,6 +16,7 @@
 
 package android.graphics.drawable.shapes;
 
+import android.annotation.NonNull;
 import android.graphics.Canvas;
 import android.graphics.Outline;
 import android.graphics.Paint;
@@ -93,11 +94,12 @@
     protected void onResize(float width, float height) {}
 
     /**
-     * Compute the Outline of the shape.
+     * Compute the Outline of the shape and return it in the supplied Outline
+     * parameter. The default implementation does nothing and {@code outline} is not changed.
      *
-     * The default implementation does not supply an outline.
+     * @param outline The Outline to be populated with the result. Should not be null.
      */
-    public void getOutline(Outline outline) {}
+    public void getOutline(@NonNull Outline outline) {}
 
     @Override
     public Shape clone() throws CloneNotSupportedException {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 1316cb8..2a4dec0 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2860,16 +2860,17 @@
 struct ResTable::Package
 {
     Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
-        : owner(_owner), header(_header), typeIdOffset(0) {
-        if (_package != NULL && dtohs(_package->header.headerSize) == sizeof(_package)) {
+        : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
+        if (dtohs(package->header.headerSize) == sizeof(package)) {
             // The package structure is the same size as the definition.
             // This means it contains the typeIdOffset field.
-            typeIdOffset = _package->typeIdOffset;
+            typeIdOffset = package->typeIdOffset;
         }
     }
 
     const ResTable* const           owner;
     const Header* const             header;
+    const ResTable_package* const   package;
 
     ResStringPool                   typeStrings;
     ResStringPool                   keyStrings;
@@ -3368,10 +3369,6 @@
 
     header->header = (const ResTable_header*) resHeader;
     mHeaders.add(header);
-
-    PackageGroup* pg = new PackageGroup(this, String16(), 0);
-    pg->packages.add(new Package(this, header, NULL));
-    mPackageGroups.add(pg);
     return (mError=NO_ERROR);
 }
 
@@ -5940,7 +5937,7 @@
     *outSize += 2 * sizeof(uint16_t);
 
     // overlay packages are assumed to contain only one package group
-    const String16 overlayPackage(overlay.mPackageGroups[0]->name);
+    const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
 
     for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
         const TypeList& typeList = pg->types[typeIndex];
@@ -6223,6 +6220,13 @@
                 (int)pgIndex, pg->id, (int)pg->packages.size(),
                 String8(pg->name).string());
 
+        size_t pkgCount = pg->packages.size();
+        for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
+            const Package* pkg = pg->packages[pkgIndex];
+            printf("  Package %d id=%d name=%s\n", (int)pkgIndex,
+                    pkg->package->id, String8(String16(pkg->package->name)).string());
+        }
+
         for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) {
             const TypeList& typeList = pg->types[typeIndex];
             if (typeList.isEmpty()) {
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 68c228e..89d271d0 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -196,17 +196,16 @@
 }
 
 TEST(ResTableTest, emptyTableHasSensibleDefaults) {
-    const int32_t expectedCookie = 1;
+    const int32_t assetCookie = 1;
 
     ResTable table;
-    ASSERT_EQ(NO_ERROR, table.addEmpty(expectedCookie));
+    ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie));
 
+    // Adding an empty table gives us one table!
     ASSERT_EQ(uint32_t(1), table.getTableCount());
-    ASSERT_EQ(uint32_t(1), table.getBasePackageCount());
-    ASSERT_EQ(expectedCookie, table.getTableCookie(0));
 
-    const DynamicRefTable* dynamicRefTable = table.getDynamicRefTableForCookie(expectedCookie);
-    ASSERT_TRUE(dynamicRefTable != NULL);
+    // Adding an empty table doesn't mean we get packages.
+    ASSERT_EQ(uint32_t(0), table.getBasePackageCount());
 
     Res_value val;
     ASSERT_LT(table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG), 0);
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 2ea6c8c..ba878bac 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -140,12 +140,12 @@
 
 void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
         uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
-    int nPenX = x + glyph->mBitmapLeft;
-    int nPenY = y + glyph->mBitmapTop;
-
     int width = (int) glyph->mBitmapWidth;
     int height = (int) glyph->mBitmapHeight;
 
+    int nPenX = x + glyph->mBitmapLeft;
+    int nPenY = y + glyph->mBitmapTop;
+
     if (bounds->bottom > nPenY) {
         bounds->bottom = nPenY;
     }
@@ -162,12 +162,12 @@
 
 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
         uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
-    float nPenX = x + glyph->mBitmapLeft;
-    float nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
-
     float width = (float) glyph->mBitmapWidth;
     float height = (float) glyph->mBitmapHeight;
 
+    float nPenX = x + glyph->mBitmapLeft;
+    float nPenY = y + glyph->mBitmapTop + height;
+
     float u1 = glyph->mBitmapMinU;
     float u2 = glyph->mBitmapMaxU;
     float v1 = glyph->mBitmapMinV;
@@ -181,10 +181,13 @@
 
 void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y,
         uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+    float width = (float) glyph->mBitmapWidth;
+    float height = (float) glyph->mBitmapHeight;
+
     SkPoint p[4];
-    p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + glyph->mBitmapHeight);
-    p[1].iset(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop + glyph->mBitmapHeight);
-    p[2].iset(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop);
+    p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + height);
+    p[1].iset(glyph->mBitmapLeft + width, glyph->mBitmapTop + height);
+    p[2].iset(glyph->mBitmapLeft + width, glyph->mBitmapTop);
     p[3].iset(glyph->mBitmapLeft, glyph->mBitmapTop);
 
     mDescription.mInverseLookupTransform.mapPoints(p, 4);
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index a182982..aa196a9 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -290,7 +290,6 @@
             if (DEBUG) {
                 Log.d(TAG, "addMediaButtonListener already added " + pi);
             }
-            return;
         }
         holder.mMediaButtonListener = new MediaButtonListener(pi, context);
         // TODO determine if handling transport performer commands should also
@@ -468,7 +467,11 @@
                 mSessions.remove(mPi);
             } else if (mCb == null) {
                 mCb = new SessionCallback();
-                mSession.setCallback(mCb);
+                Handler handler = null;
+                if (Looper.myLooper() == null) {
+                    handler = new Handler(Looper.getMainLooper());
+                }
+                mSession.setCallback(mCb, handler);
             }
         }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5dc7d26..87c015c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -20,6 +20,7 @@
 import java.security.SecureRandom;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -94,6 +95,9 @@
     // Each defined user has their own settings
     protected final SparseArray<DatabaseHelper> mOpenHelpers = new SparseArray<DatabaseHelper>();
 
+    // Keep the list of managed profiles synced here
+    private List<UserInfo> mManagedProfiles = null;
+
     // Over this size we don't reject loading or saving settings but
     // we do consider them broken/malicious and don't keep them in
     // memory at least:
@@ -119,6 +123,9 @@
 
     private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
 
+    static final HashSet<String> sSecureCloneToManagedKeys;
+    static final HashSet<String> sSystemCloneToManagedKeys;
+
     static {
         // Keys (name column) from the 'secure' table that are now in the owner user's 'global'
         // table, shared across all users
@@ -142,6 +149,15 @@
                 UserManager.ENSURE_VERIFY_APPS);
         sRestrictedKeys.put(Settings.Global.PREFERRED_NETWORK_MODE,
                 UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+
+        sSecureCloneToManagedKeys = new HashSet<String>();
+        for (int i = 0; i < Settings.Secure.CLONE_TO_MANAGED_PROFILE.length; i++) {
+            sSecureCloneToManagedKeys.add(Settings.Secure.CLONE_TO_MANAGED_PROFILE[i]);
+        }
+        sSystemCloneToManagedKeys = new HashSet<String>();
+        for (int i = 0; i < Settings.System.CLONE_TO_MANAGED_PROFILE.length; i++) {
+            sSystemCloneToManagedKeys.add(Settings.System.CLONE_TO_MANAGED_PROFILE[i]);
+        }
     }
 
     private boolean settingMovedToGlobal(final String name) {
@@ -362,18 +378,22 @@
 
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
+        userFilter.addAction(Intent.ACTION_USER_ADDED);
         getContext().registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
+                final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        UserHandle.USER_OWNER);
                 if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
-                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                            UserHandle.USER_OWNER);
-                    if (userHandle != UserHandle.USER_OWNER) {
-                        onUserRemoved(userHandle);
-                    }
+                    onUserRemoved(userHandle);
+                } else if (intent.getAction().equals(Intent.ACTION_USER_ADDED)) {
+                    onProfilesChanged();
                 }
             }
         }, userFilter);
+
+        onProfilesChanged();
+
         return true;
     }
 
@@ -391,6 +411,32 @@
             sSystemCaches.delete(userHandle);
             sSecureCaches.delete(userHandle);
             sKnownMutationsInFlight.delete(userHandle);
+            onProfilesChanged();
+        }
+    }
+
+    /**
+     * Updates the list of managed profiles. It assumes that only the primary user
+     * can have managed profiles. Modify this code if that changes in the future.
+     */
+    void onProfilesChanged() {
+        synchronized (this) {
+            mManagedProfiles = mUserManager.getProfiles(UserHandle.USER_OWNER);
+            if (mManagedProfiles != null) {
+                // Remove the primary user from the list
+                for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+                    if (mManagedProfiles.get(i).id == UserHandle.USER_OWNER) {
+                        mManagedProfiles.remove(i);
+                    }
+                }
+                // If there are no managed profiles, reset the variable
+                if (mManagedProfiles.size() == 0) {
+                    mManagedProfiles = null;
+                }
+            }
+            if (LOCAL_LOGV) {
+                Slog.d(TAG, "Managed Profiles = " + mManagedProfiles);
+            }
         }
     }
 
@@ -601,6 +647,24 @@
     }
 
     /**
+     * Checks if the calling user is a managed profile of the primary user.
+     * Currently only the primary user (USER_OWNER) can have managed profiles.
+     * @param callingUser the user trying to read/write settings
+     * @return true if it is a managed profile of the primary user
+     */
+    private boolean isManagedProfile(int callingUser) {
+        synchronized (this) {
+            if (mManagedProfiles == null) return false;
+            for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+                if (mManagedProfiles.get(i).id == callingUser) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
      * Fast path that avoids the use of chatty remoted Cursors.
      */
     @Override
@@ -625,12 +689,18 @@
         // Get methods
         if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
             if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
+            if (isManagedProfile(callingUser) && sSystemCloneToManagedKeys.contains(request)) {
+                callingUser = UserHandle.USER_OWNER;
+            }
             dbHelper = getOrEstablishDatabase(callingUser);
             cache = sSystemCaches.get(callingUser);
             return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
         }
         if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
             if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
+            if (isManagedProfile(callingUser) && sSecureCloneToManagedKeys.contains(request)) {
+                callingUser = UserHandle.USER_OWNER;
+            }
             dbHelper = getOrEstablishDatabase(callingUser);
             cache = sSecureCaches.get(callingUser);
             return lookupValue(dbHelper, TABLE_SECURE, cache, request);
@@ -667,13 +737,70 @@
         values.put(Settings.NameValueTable.NAME, request);
         values.put(Settings.NameValueTable.VALUE, newValue);
         if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
-            if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
+            if (LOCAL_LOGV) {
+                Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for "
+                        + callingUser);
+            }
+            // Extra check for USER_OWNER to optimize for the 99%
+            if (callingUser != UserHandle.USER_OWNER && isManagedProfile(callingUser)) {
+                if (sSystemCloneToManagedKeys.contains(request)) {
+                    // Don't write these settings
+                    return null;
+                }
+            }
             insertForUser(Settings.System.CONTENT_URI, values, callingUser);
+            // Clone the settings to the managed profiles so that notifications can be sent out
+            if (callingUser == UserHandle.USER_OWNER && mManagedProfiles != null
+                    && sSystemCloneToManagedKeys.contains(request)) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+                        if (LOCAL_LOGV) {
+                            Slog.v(TAG, "putting to additional user "
+                                    + mManagedProfiles.get(i).id);
+                        }
+                        insertForUser(Settings.System.CONTENT_URI, values,
+                                mManagedProfiles.get(i).id);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
         } else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
-            if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
+            if (LOCAL_LOGV) {
+                Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for "
+                        + callingUser);
+            }
+            // Extra check for USER_OWNER to optimize for the 99%
+            if (callingUser != UserHandle.USER_OWNER && isManagedProfile(callingUser)) {
+                if (sSecureCloneToManagedKeys.contains(request)) {
+                    // Don't write these settings
+                    return null;
+                }
+            }
             insertForUser(Settings.Secure.CONTENT_URI, values, callingUser);
+            // Clone the settings to the managed profiles so that notifications can be sent out
+            if (callingUser == UserHandle.USER_OWNER && mManagedProfiles != null
+                    && sSecureCloneToManagedKeys.contains(request)) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+                        if (LOCAL_LOGV) {
+                            Slog.v(TAG, "putting to additional user "
+                                    + mManagedProfiles.get(i).id);
+                        }
+                        insertForUser(Settings.Secure.CONTENT_URI, values,
+                                mManagedProfiles.get(i).id);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
         } else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
-            if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
+            if (LOCAL_LOGV) {
+                Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for "
+                        + callingUser);
+            }
             insertForUser(Settings.Global.CONTENT_URI, values, callingUser);
         } else {
             Slog.w(TAG, "call() with invalid method: " + method);
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c24fcae..d145172 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -709,13 +709,13 @@
     <string name="guest_wipe_session_title">Welcome back, guest!</string>
 
     <!-- Message of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
-    <string name="guest_wipe_session_message">Do you want to start a new session?</string>
+    <string name="guest_wipe_session_message">Do you want to continue your session?</string>
 
     <!-- Notification when resuming an existing guest session: Action that starts a new session [CHAR LIMIT=35] -->
-    <string name="guest_wipe_session_wipe">Yes</string>
+    <string name="guest_wipe_session_wipe">Start over</string>
 
     <!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] -->
-    <string name="guest_wipe_session_dontwipe">No, thanks</string>
+    <string name="guest_wipe_session_dontwipe">Yes, continue</string>
 
 
     <!-- Zen mode condition: time duration in minutes. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 2113c68..9fbcd7f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -59,6 +59,7 @@
 
     private boolean mAutomatic;
     private boolean mListening;
+    private boolean mExternalChange;
 
     public interface BrightnessStateChangeCallback {
         public void onBrightnessLevelChanged();
@@ -86,19 +87,24 @@
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (selfChange) return;
-            if (BRIGHTNESS_MODE_URI.equals(uri)) {
-                updateMode();
-                updateSlider();
-            } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
-                updateSlider();
-            } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
-                updateSlider();
-            } else {
-                updateMode();
-                updateSlider();
-            }
-            for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
-                cb.onBrightnessLevelChanged();
+            try {
+                mExternalChange = true;
+                if (BRIGHTNESS_MODE_URI.equals(uri)) {
+                    updateMode();
+                    updateSlider();
+                } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
+                    updateSlider();
+                } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
+                    updateSlider();
+                } else {
+                    updateMode();
+                    updateSlider();
+                }
+                for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
+                    cb.onBrightnessLevelChanged();
+                }
+            } finally {
+                mExternalChange = false;
             }
         }
 
@@ -191,6 +197,8 @@
     @Override
     public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
         updateIcon(mAutomatic);
+        if (mExternalChange) return;
+
         if (!mAutomatic) {
             final int val = value + mMinimumBacklight;
             setBrightness(val);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index d113139..a1704ff 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -40,35 +40,27 @@
         super.onCreate(savedInstanceState);
 
         final Window window = getWindow();
-        final WindowManager.LayoutParams lp = window.getAttributes();
 
-        // Offset from the top
-        lp.y = getResources().getDimensionPixelOffset(R.dimen.volume_panel_top);
-        lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-
-        window.setAttributes(lp);
         window.setGravity(Gravity.TOP);
         window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
         window.requestFeature(Window.FEATURE_NO_TITLE);
 
         setContentView(R.layout.quick_settings_brightness_dialog);
+
+        final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
+        final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
+        mBrightnessController = new BrightnessController(this, icon, slider);
     }
 
     @Override
     protected void onStart() {
         super.onStart();
-
-        final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
-        final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
-        mBrightnessController = new BrightnessController(this, icon, slider);
         mBrightnessController.registerCallbacks();
     }
 
     @Override
     protected void onStop() {
         super.onStop();
-
         mBrightnessController.unregisterCallbacks();
     }
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b2d1b71..c44474d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -8599,7 +8599,15 @@
             skip = true;
         }
 
-        if (!skip && mAutoRestore && mProvisioned) {
+        if (!mAutoRestore || !mProvisioned) {
+            if (DEBUG) {
+                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore
+                        + " prov=" + mProvisioned);
+            }
+            skip = true;
+        }
+
+        if (!skip) {
             try {
                 // okay, we're going to attempt a restore of this package from this restore set.
                 // The eventual message back into the Package Manager to run the post-install
@@ -8632,7 +8640,7 @@
         if (skip) {
             // Auto-restore disabled or no way to attempt a restore; just tell the Package
             // Manager to proceed with the post-install handling for this package.
-            if (DEBUG) Slog.v(TAG, "Skipping");
+            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
             try {
                 mPackageManagerBinder.finishPackageInstall(token);
             } catch (RemoteException e) { /* can't happen */ }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dd5a7ea..02695c5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1534,11 +1534,17 @@
             return;
         }
 
-        if (mtu < 68 || mtu > 10000) {
+        if (LinkProperties.isValidMtu(mtu, newLp.hasGlobalIPv6Address())) {
             loge("Unexpected mtu value: " + mtu + ", " + iface);
             return;
         }
 
+        // Cannot set MTU without interface name
+        if (TextUtils.isEmpty(iface)) {
+            loge("Setting MTU size with null iface.");
+            return;
+        }
+
         try {
             if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
             mNetd.setMtu(iface, mtu);
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index ab4d4dc..395e365 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -17,9 +17,9 @@
 package com.android.server;
 
 import android.Manifest.permission;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.net.INetworkScoreCache;
 import android.net.INetworkScoreService;
@@ -30,6 +30,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -51,9 +52,6 @@
 public class NetworkScoreService extends INetworkScoreService.Stub {
     private static final String TAG = "NetworkScoreService";
 
-    /** SharedPreference bit set to true after the service is first initialized. */
-    private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
-
     private final Context mContext;
 
     private final Map<Integer, INetworkScoreCache> mScoreCaches;
@@ -65,8 +63,8 @@
 
     /** Called when the system is ready to run third-party code but before it actually does so. */
     void systemReady() {
-        SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
-        if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
+        ContentResolver cr = mContext.getContentResolver();
+        if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
             // On first run, we try to initialize the scorer to the one configured at build time.
             // This will be a no-op if the scorer isn't actually valid.
             String defaultPackage = mContext.getResources().getString(
@@ -74,7 +72,7 @@
             if (!TextUtils.isEmpty(defaultPackage)) {
                 NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
             }
-            prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
+            Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index b7b5f98..758f334 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -169,24 +169,19 @@
                     return false;
                 }
             }
-            // audience has veto power over all following rules
-            if (!audienceMatches(record)) {
-                ZenLog.traceIntercepted(record, "!audienceMatches");
-                return true;
-            }
             if (isCall(record)) {
                 if (!mConfig.allowCalls) {
                     ZenLog.traceIntercepted(record, "!allowCalls");
                     return true;
                 }
-                return false;
+                return shouldInterceptAudience(record);
             }
             if (isMessage(record)) {
                 if (!mConfig.allowMessages) {
                     ZenLog.traceIntercepted(record, "!allowMessages");
                     return true;
                 }
-                return false;
+                return shouldInterceptAudience(record);
             }
             ZenLog.traceIntercepted(record, "!allowed");
             return true;
@@ -194,6 +189,14 @@
         return false;
     }
 
+    private boolean shouldInterceptAudience(NotificationRecord record) {
+        if (!audienceMatches(record)) {
+            ZenLog.traceIntercepted(record, "!audienceMatches");
+            return true;
+        }
+        return false;
+    }
+
     public int getZenMode() {
         return mZenMode;
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c7e3fb7..dca8ad4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -36,19 +36,23 @@
 
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
+import android.app.PackageDeleteObserver;
+import android.app.PackageInstallObserver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.IPackageDeleteObserver2;
+import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageInstallerCallback;
 import android.content.pm.IPackageInstallerSession;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -63,6 +67,7 @@
 import android.os.UserManager;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -279,8 +284,8 @@
         final File sessionStageDir = new File(readStringAttribute(in, ATTR_SESSION_STAGE_DIR));
         final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
 
-        final InstallSessionParams params = new InstallSessionParams(
-                InstallSessionParams.MODE_INVALID);
+        final SessionParams params = new SessionParams(
+                SessionParams.MODE_INVALID);
         params.mode = readIntAttribute(in, ATTR_MODE);
         params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
         params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
@@ -292,9 +297,9 @@
         params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
         params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
 
-        return new PackageInstallerSession(mInternalCallback, mPm, mInstallThread.getLooper(),
-                sessionId, userId, installerPackageName, params, createdMillis, sessionStageDir,
-                sealed);
+        return new PackageInstallerSession(mInternalCallback, mContext, mPm,
+                mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
+                createdMillis, sessionStageDir, sealed);
     }
 
     private void writeSessionsLocked() {
@@ -326,7 +331,7 @@
 
     private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
             throws IOException {
-        final InstallSessionParams params = session.params;
+        final SessionParams params = session.params;
         final Snapshot snapshot = session.snapshot();
 
         out.startTag(null, TAG_SESSION);
@@ -366,7 +371,7 @@
     }
 
     @Override
-    public int createSession(InstallSessionParams params, String installerPackageName, int userId) {
+    public int createSession(SessionParams params, String installerPackageName, int userId) {
         final int callingUid = Binder.getCallingUid();
         mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession");
 
@@ -389,8 +394,8 @@
         }
 
         switch (params.mode) {
-            case InstallSessionParams.MODE_FULL_INSTALL:
-            case InstallSessionParams.MODE_INHERIT_EXISTING:
+            case SessionParams.MODE_FULL_INSTALL:
+            case SessionParams.MODE_INHERIT_EXISTING:
                 break;
             default:
                 throw new IllegalArgumentException("Params must have valid mode set");
@@ -437,7 +442,7 @@
             final long createdMillis = System.currentTimeMillis();
             final File sessionStageDir = prepareSessionStageDir(sessionId);
 
-            session = new PackageInstallerSession(mInternalCallback, mPm,
+            session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
                     mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
                     createdMillis, sessionStageDir, false);
             mSessions.put(sessionId, session);
@@ -501,7 +506,7 @@
     }
 
     @Override
-    public InstallSessionInfo getSessionInfo(int sessionId) {
+    public SessionInfo getSessionInfo(int sessionId) {
         synchronized (mSessions) {
             final PackageInstallerSession session = mSessions.get(sessionId);
             if (!isCallingUidOwner(session)) {
@@ -512,11 +517,11 @@
     }
 
     @Override
-    public List<InstallSessionInfo> getAllSessions(int userId) {
+    public List<SessionInfo> getAllSessions(int userId) {
         mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions");
         enforceCallerCanReadSessions();
 
-        final List<InstallSessionInfo> result = new ArrayList<>();
+        final List<SessionInfo> result = new ArrayList<>();
         synchronized (mSessions) {
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
@@ -529,11 +534,11 @@
     }
 
     @Override
-    public List<InstallSessionInfo> getMySessions(String installerPackageName, int userId) {
+    public List<SessionInfo> getMySessions(String installerPackageName, int userId) {
         mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getMySessions");
         mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
 
-        final List<InstallSessionInfo> result = new ArrayList<>();
+        final List<SessionInfo> result = new ArrayList<>();
         synchronized (mSessions) {
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
@@ -547,37 +552,26 @@
     }
 
     @Override
-    public void uninstall(String packageName, int flags, IPackageDeleteObserver2 observer,
-            int userId) {
+    public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) {
         mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall");
 
+        final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
+                statusReceiver);
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
                 == PackageManager.PERMISSION_GRANTED) {
             // Sweet, call straight through!
-            mPm.deletePackage(packageName, observer, userId, flags);
+            mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
 
         } else {
             // Take a short detour to confirm with user
             final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
             intent.setData(Uri.fromParts("package", packageName, null));
-            intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder());
-            try {
-                observer.onUserActionRequired(intent);
-            } catch (RemoteException ignored) {
-            }
+            intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
+            adapter.onUserActionRequired(intent);
         }
     }
 
     @Override
-    public void uninstallSplit(String basePackageName, String overlayName, int flags,
-            IPackageDeleteObserver2 observer, int userId) {
-        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstallSplit");
-
-        // TODO: flesh out once PM has split support
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
     public void setPermissionsResult(int sessionId, boolean accepted) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
 
@@ -636,6 +630,87 @@
         }
     }
 
+    static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
+        private final Context mContext;
+        private final IntentSender mTarget;
+
+        public PackageDeleteObserverAdapter(Context context, IntentSender target) {
+            mContext = context;
+            mTarget = target;
+        }
+
+        @Override
+        public void onUserActionRequired(Intent intent) {
+            final Intent fillIn = new Intent();
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageInstaller.STATUS_USER_ACTION_REQUIRED);
+            fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+            try {
+                mTarget.sendIntent(mContext, 0, fillIn, null, null);
+            } catch (SendIntentException ignored) {
+            }
+        }
+
+        @Override
+        public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
+            final Intent fillIn = new Intent();
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageManager.deleteStatusToPublicStatus(returnCode));
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+                    PackageManager.deleteStatusToString(returnCode, msg));
+            fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+            try {
+                mTarget.sendIntent(mContext, 0, fillIn, null, null);
+            } catch (SendIntentException ignored) {
+            }
+        }
+    }
+
+    static class PackageInstallObserverAdapter extends PackageInstallObserver {
+        private final Context mContext;
+        private final IntentSender mTarget;
+
+        public PackageInstallObserverAdapter(Context context, IntentSender target) {
+            mContext = context;
+            mTarget = target;
+        }
+
+        @Override
+        public void onUserActionRequired(Intent intent) {
+            final Intent fillIn = new Intent();
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageInstaller.STATUS_USER_ACTION_REQUIRED);
+            fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+            try {
+                mTarget.sendIntent(mContext, 0, fillIn, null, null);
+            } catch (SendIntentException ignored) {
+            }
+        }
+
+        @Override
+        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+                Bundle extras) {
+            final Intent fillIn = new Intent();
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+                    PackageManager.installStatusToPublicStatus(returnCode));
+            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+                    PackageManager.installStatusToString(returnCode, msg));
+            fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+            if (extras != null) {
+                final String existing = extras.getString(
+                        PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
+                if (!TextUtils.isEmpty(existing)) {
+                    fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAMES, new String[] {
+                            existing });
+                }
+            }
+            try {
+                mTarget.sendIntent(mContext, 0, fillIn, null, null);
+            } catch (SendIntentException ignored) {
+            }
+        }
+    }
+
     private static class Callbacks extends Handler {
         private static final int MSG_SESSION_CREATED = 1;
         private static final int MSG_SESSION_OPENED = 2;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a3184f0..5ef24f2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -25,13 +25,15 @@
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_WRONLY;
 
+import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.IPackageInstallerSession;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ApkLite;
@@ -59,6 +61,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
+import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
 
 import libcore.io.Libcore;
 
@@ -81,13 +84,14 @@
     // TODO: treat INHERIT_EXISTING as installExistingPackage()
 
     private final PackageInstallerService.InternalCallback mCallback;
+    private final Context mContext;
     private final PackageManagerService mPm;
     private final Handler mHandler;
 
     final int sessionId;
     final int userId;
     final String installerPackageName;
-    final InstallSessionParams params;
+    final SessionParams params;
     final long createdMillis;
     final File sessionStageDir;
 
@@ -159,10 +163,11 @@
     };
 
     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
-            PackageManagerService pm, Looper looper, int sessionId, int userId,
-            String installerPackageName, InstallSessionParams params, long createdMillis,
+            Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
+            String installerPackageName, SessionParams params, long createdMillis,
             File sessionStageDir, boolean sealed) {
         mCallback = callback;
+        mContext = context;
         mPm = pm;
         mHandler = new Handler(looper, mHandlerCallback);
 
@@ -188,8 +193,8 @@
         computeProgressLocked();
     }
 
-    public InstallSessionInfo generateInfo() {
-        final InstallSessionInfo info = new InstallSessionInfo();
+    public SessionInfo generateInfo() {
+        final SessionInfo info = new SessionInfo();
 
         info.sessionId = sessionId;
         info.installerPackageName = installerPackageName;
@@ -246,8 +251,8 @@
     }
 
     @Override
-    public String[] list() {
-        assertNotSealed("list");
+    public String[] getNames() {
+        assertNotSealed("getNames");
         return sessionStageDir.list();
     }
 
@@ -337,9 +342,12 @@
     }
 
     @Override
-    public void commit(IPackageInstallObserver2 observer) {
-        Preconditions.checkNotNull(observer);
-        mHandler.obtainMessage(MSG_COMMIT, observer).sendToTarget();
+    public void commit(IntentSender statusReceiver) {
+        Preconditions.checkNotNull(statusReceiver);
+
+        final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
+                statusReceiver);
+        mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
     }
 
     private void commitLocked() throws PackageManagerException {
@@ -385,7 +393,7 @@
 
         // Inherit any packages and native libraries from existing install that
         // haven't been overridden.
-        if (params.mode == InstallSessionParams.MODE_INHERIT_EXISTING) {
+        if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
             spliceExistingFilesIntoStage();
         }
 
@@ -396,7 +404,6 @@
 
         // We've reached point of no return; call into PMS to install the stage.
         // Regardless of success or failure we always destroy session.
-        final IPackageInstallObserver2 remoteObserver = mRemoteObserver;
         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
             @Override
             public void onUserActionRequired(Intent intent) {
@@ -488,7 +495,7 @@
         // currently relying on PMS to do this.
         // TODO: teach about compatible upgrade keysets.
 
-        if (params.mode == InstallSessionParams.MODE_FULL_INSTALL) {
+        if (params.mode == SessionParams.MODE_FULL_INSTALL) {
             // Full installs must include a base package
             if (!seenSplits.contains(null)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 304441c..63f3c0f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -107,12 +107,12 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
-import android.content.pm.InstallSessionParams;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
 import android.content.pm.PackageParser.ActivityIntentInfo;
@@ -7846,7 +7846,7 @@
     }
 
     void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer,
-            InstallSessionParams params, String installerPackageName, int installerUid,
+            PackageInstaller.SessionParams params, String installerPackageName, int installerUid,
             UserHandle user) {
         final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
                 params.referrerUri, installerUid, null);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 39c6e0e..db19285 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -58,8 +58,6 @@
 import android.os.SystemService;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.os.Parcel;
-import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
 import android.util.EventLog;
@@ -709,7 +707,6 @@
         if (mLowPowerModeEnabled != lowPowerModeEnabled) {
             mLowPowerModeEnabled = lowPowerModeEnabled;
             powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
-            setSurfaceFlingerLowPowerMode(lowPowerModeEnabled ? 1 : 0);
             mLowPowerModeEnabled = lowPowerModeEnabled;
             BackgroundThread.getHandler().post(new Runnable() {
                 @Override
@@ -2198,21 +2195,6 @@
         nativeSendPowerHint(hintId, data);
     }
 
-    private static void setSurfaceFlingerLowPowerMode(int enabled) {
-        try {
-            final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
-            if (flinger != null) {
-                final Parcel data = Parcel.obtain();
-                data.writeInterfaceToken("android.ui.ISurfaceComposer");
-                data.writeInt(enabled);
-                flinger.transact(1016, data, null, 0);
-                data.recycle();
-            }
-        } catch (RemoteException ex) {
-            Slog.e(TAG, "Failed to reduce refresh rate", ex);
-        }
-    }
-
     /**
      * Low-level function turn the device off immediately, without trying
      * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fc96991..d46ae42 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 
 import android.app.admin.DevicePolicyManagerInternal;
+
 import com.android.internal.R;
 import com.android.internal.os.storage.ExternalStorageFormatter;
 import com.android.internal.util.FastXmlSerializer;
@@ -58,6 +59,7 @@
 import android.net.Uri;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.ContentObserver;
+import android.hardware.usb.UsbManager;
 import android.net.ProxyInfo;
 import android.os.Binder;
 import android.os.Bundle;
@@ -3417,8 +3419,7 @@
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mUserManager.setUserRestrictions(new Bundle(),
-                        new UserHandle(UserHandle.USER_OWNER));
+                clearUserRestrictions(new UserHandle(UserHandle.USER_OWNER));
                 if (mDeviceOwner != null) {
                     mDeviceOwner.clearDeviceOwner();
                     mDeviceOwner.writeOwnerFile();
@@ -3481,7 +3482,7 @@
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mUserManager.setUserRestrictions(new Bundle(), callingUser);
+                clearUserRestrictions(callingUser);
                 if (mDeviceOwner != null) {
                     mDeviceOwner.removeProfileOwner(callingUser.getIdentifier());
                     mDeviceOwner.writeOwnerFile();
@@ -3492,6 +3493,19 @@
         }
     }
 
+    private void clearUserRestrictions(UserHandle userHandle) {
+        AudioManager audioManager =
+                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        Bundle userRestrictions = mUserManager.getUserRestrictions();
+        mUserManager.setUserRestrictions(new Bundle(), userHandle);
+        if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
+            audioManager.setMasterMute(false);
+        }
+        if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
+            audioManager.setMicrophoneMute(false);
+        }
+    }
+
     @Override
     public boolean hasUserSetupCompleted() {
         if (!mHasFeature) {
@@ -4034,7 +4048,57 @@
 
             long id = Binder.clearCallingIdentity();
             try {
+                AudioManager audioManager =
+                        (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+                boolean alreadyRestricted = mUserManager.hasUserRestriction(key);
+
+                if (enabled && !alreadyRestricted) {
+                    if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
+                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
+                                userHandle.getIdentifier());
+                    } else if (UserManager.DISALLOW_USB_FILE_TRANSFER.equals(key)) {
+                        UsbManager manager =
+                                (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+                        manager.setCurrentFunction("none", false);
+                    } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
+                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF,
+                                userHandle.getIdentifier());
+                        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
+                                userHandle.getIdentifier());
+                    } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
+                        Settings.Global.putStringForUser(mContext.getContentResolver(),
+                                Settings.Global.ADB_ENABLED, "0", userHandle.getIdentifier());
+                    } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
+                        Settings.Global.putStringForUser(mContext.getContentResolver(),
+                                Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
+                                userHandle.getIdentifier());
+                        Settings.Global.putStringForUser(mContext.getContentResolver(),
+                                Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
+                                userHandle.getIdentifier());
+                    } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
+                        Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
+                                userHandle.getIdentifier());
+                    } else if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+                        audioManager.setMicrophoneMute(true);
+                    } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+                        audioManager.setMasterMute(true);
+                    }
+                }
+
                 mUserManager.setUserRestriction(key, enabled, userHandle);
+
+                if (!enabled && alreadyRestricted) {
+                    if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+                        audioManager.setMicrophoneMute(false);
+                    } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+                        audioManager.setMasterMute(false);
+                    }
+                }
+
             } finally {
                 restoreCallingIdentity(id);
             }
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index f988ac8..7223574 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -27,6 +27,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * Represents an ongoing phone call that the in-call app should present to the user.
@@ -364,7 +365,7 @@
     private final InCallAdapter mInCallAdapter;
     private final List<Call> mChildren = new ArrayList<>();
     private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
-    private final List<Listener> mListeners = new ArrayList<>();
+    private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
     private final List<Call> mConferenceableCalls = new ArrayList<>();
     private final List<Call> mUnmodifiableConferenceableCalls =
             Collections.unmodifiableList(mConferenceableCalls);
@@ -589,7 +590,9 @@
      * @param listener A {@code Listener}.
      */
     public void removeListener(Listener listener) {
-        mListeners.remove(listener);
+        if (listener != null) {
+            mListeners.remove(listener);
+        }
     }
 
     /** {@hide} */
@@ -709,72 +712,62 @@
     }
 
     private void fireStateChanged(int newState) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onStateChanged(this, newState);
+        for (Listener listener : mListeners) {
+            listener.onStateChanged(this, newState);
         }
     }
 
     private void fireParentChanged(Call newParent) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onParentChanged(this, newParent);
+        for (Listener listener : mListeners) {
+            listener.onParentChanged(this, newParent);
         }
     }
 
     private void fireChildrenChanged(List<Call> children) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onChildrenChanged(this, children);
+        for (Listener listener : mListeners) {
+            listener.onChildrenChanged(this, children);
         }
     }
 
     private void fireDetailsChanged(Details details) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onDetailsChanged(this, details);
+        for (Listener listener : mListeners) {
+            listener.onDetailsChanged(this, details);
         }
     }
 
     private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onCannedTextResponsesLoaded(this, cannedTextResponses);
+        for (Listener listener : mListeners) {
+            listener.onCannedTextResponsesLoaded(this, cannedTextResponses);
         }
     }
 
     private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onVideoCallChanged(this, videoCall);
+        for (Listener listener : mListeners) {
+            listener.onVideoCallChanged(this, videoCall);
         }
     }
 
     private void firePostDialWait(String remainingPostDialSequence) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onPostDialWait(this, remainingPostDialSequence);
+        for (Listener listener : mListeners) {
+            listener.onPostDialWait(this, remainingPostDialSequence);
         }
     }
 
     private void fireStartActivity(PendingIntent intent) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onStartActivity(this, intent);
+        for (Listener listener : mListeners) {
+            listener.onStartActivity(this, intent);
         }
     }
 
     private void fireCallDestroyed() {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onCallDestroyed(this);
+        for (Listener listener : mListeners) {
+            listener.onCallDestroyed(this);
         }
     }
 
     private void fireConferenceableCallsChanged() {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
+        for (Listener listener : mListeners) {
+            listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
         }
     }
 
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 3ecb4cb..78c34a1 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -32,7 +32,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Represents a connection to a remote endpoint that carries voice traffic.
@@ -448,7 +448,13 @@
         }
     };
 
-    private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
+    /**
+     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+     * load factor before resizing, 1 means we only expect a single thread to
+     * access the map so make only a single shard
+     */
+    private final Set<Listener> mListeners = Collections.newSetFromMap(
+            new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
     private final List<Connection> mChildConnections = new ArrayList<>();
     private final List<Connection> mUnmodifiableChildConnections =
             Collections.unmodifiableList(mChildConnections);
@@ -587,7 +593,9 @@
      * @hide
      */
     public final Connection removeConnectionListener(Listener l) {
-        mListeners.remove(l);
+        if (l != null) {
+            mListeners.remove(l);
+        }
         return this;
     }
 
@@ -874,13 +882,8 @@
      * Tears down the Connection object.
      */
     public final void destroy() {
-        // 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);
-            }
+        for (Listener l : mListeners) {
+            l.onDestroyed(this);
         }
     }
 
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 44aacfc..8ab5e13 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -16,6 +16,7 @@
 
 package android.telecomm;
 
+import android.Manifest;
 import android.annotation.SdkConstant;
 import android.app.PendingIntent;
 import android.app.Service;
@@ -26,6 +27,8 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.RemoteException;
 import android.telephony.DisconnectCause;
 
 import com.android.internal.os.SomeArgs;
@@ -45,7 +48,6 @@
  * Android device.
  */
 public abstract class ConnectionService extends Service {
-
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
@@ -81,6 +83,18 @@
     private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
 
     private final IBinder mBinder = new IConnectionService.Stub() {
+        /**
+         * Enforces the requirement that all calls into the ConnectionService require the
+         * {@code BIND_CONNECTION_SERVICE} permission.
+         */
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply,
+                int flags) throws RemoteException
+        {
+            enforceBindConnectionServicePermission();
+            return super.onTransact(code, data, reply, flags);
+        }
+
         @Override
         public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
             mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
@@ -481,15 +495,12 @@
                             case Connection.STATE_INITIALIZING:
                                 Log.d(this, "State changed to STATE_INITIALIZING; ignoring");
                                 return; // Don't want to stop listening on this state transition.
-                            default:
-                                Log.d(this, "Connection created in state %s",
-                                        Connection.stateToString(state));
-                                connectionCreated(callId, request, createdConnection);
-                                break;
                         }
                         c.removeConnectionListener(this);
                     }
                 });
+                Log.d(this, "Connection created in state INITIALIZING");
+                connectionCreated(callId, request, createdConnection);
             } else if (createdConnection.getState() == Connection.STATE_CANCELED) {
                 // Tell telecomm not to attempt any more services.
                 mAdapter.handleCreateConnectionCancelled(callId, request);
@@ -620,7 +631,8 @@
                     public void onError(String request, int code, String reason) {
                         // no-op
                     }
-                });
+                }
+        );
     }
 
     private void splitFromConference(String callId) {
@@ -833,4 +845,10 @@
         return Connection.getNullConnection();
     }
 
+    /**
+     * Enforces the {@code BIND_CONNECTION_SERVICE} permission for connection service calls.
+     */
+    private void enforceBindConnectionServicePermission() {
+        enforceCallingPermission(Manifest.permission.BIND_CONNECTION_SERVICE, null);
+    }
 }
diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index bfcb5c3..4144b81 100644
--- a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -36,8 +36,13 @@
  * @hide
  */
 final class ConnectionServiceAdapter implements DeathRecipient {
+    /**
+     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+     * load factor before resizing, 1 means we only expect a single thread to
+     * access the map so make only a single shard
+     */
     private final Set<IConnectionServiceAdapter> mAdapters = Collections.newSetFromMap(
-            new ConcurrentHashMap<IConnectionServiceAdapter, Boolean>(2));
+            new ConcurrentHashMap<IConnectionServiceAdapter, Boolean>(8, 0.9f, 1));
 
     ConnectionServiceAdapter() {
     }
@@ -53,7 +58,7 @@
     }
 
     void removeAdapter(IConnectionServiceAdapter adapter) {
-        if (mAdapters.remove(adapter)) {
+        if (adapter != null && mAdapters.remove(adapter)) {
             adapter.asBinder().unlinkToDeath(this, 0);
         }
     }
diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java
index 79e777a..03a8676 100644
--- a/telecomm/java/android/telecomm/Phone.java
+++ b/telecomm/java/android/telecomm/Phone.java
@@ -24,6 +24,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * A unified virtual device providing a means of voice (and other) communication on a device.
@@ -89,7 +90,7 @@
 
     private AudioState mAudioState;
 
-    private final List<Listener> mListeners = new ArrayList<>();
+    private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
 
     /** {@hide} */
     Phone(InCallAdapter adapter) {
@@ -171,7 +172,9 @@
      * @param listener A {@code Listener} object.
      */
     public final void removeListener(Listener listener) {
-        mListeners.remove(listener);
+        if (listener != null) {
+            mListeners.remove(listener);
+        }
     }
 
     /**
@@ -236,30 +239,26 @@
     }
 
     private void fireCallAdded(Call call) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onCallAdded(this, call);
+        for (Listener listener : mListeners) {
+            listener.onCallAdded(this, call);
         }
     }
 
     private void fireCallRemoved(Call call) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onCallRemoved(this, call);
+        for (Listener listener : mListeners) {
+            listener.onCallRemoved(this, call);
         }
     }
 
     private void fireAudioStateChanged(AudioState audioState) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onAudioStateChanged(this, audioState);
+        for (Listener listener : mListeners) {
+            listener.onAudioStateChanged(this, audioState);
         }
     }
 
     private void fireBringToForeground(boolean showDialpad) {
-        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
-        for (int i = 0; i < listeners.length; i++) {
-            listeners[i].onBringToForeground(this, showDialpad);
+        for (Listener listener : mListeners) {
+            listener.onBringToForeground(this, showDialpad);
         }
     }
 
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index d3972d31..f1cee10 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -184,8 +184,13 @@
 
     private IConnectionService mConnectionService;
     private final String mConnectionId;
+    /**
+     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+     * load factor before resizing, 1 means we only expect a single thread to
+     * access the map so make only a single shard
+     */
     private final Set<Listener> mListeners = Collections.newSetFromMap(
-            new ConcurrentHashMap<Listener, Boolean>(2));
+            new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
     private final Set<RemoteConnection> mConferenceableConnections = new HashSet<>();
 
     private int mState = Connection.STATE_NEW;
@@ -248,7 +253,9 @@
      * @param listener A {@code Listener}.
      */
     public void removeListener(Listener listener) {
-        mListeners.remove(listener);
+        if (listener != null) {
+            mListeners.remove(listener);
+        }
     }
 
     /**
@@ -588,11 +595,10 @@
                 setDisconnected(DisconnectCause.ERROR_UNSPECIFIED, "Connection destroyed.");
             }
 
-            Set<Listener> listeners = new HashSet<Listener>(mListeners);
-            mListeners.clear();
-            for (Listener l : listeners) {
+            for (Listener l : mListeners) {
                 l.onDestroyed(this);
             }
+            mListeners.clear();
 
             mConnected = false;
         }
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index cf87bec..b4b1ea5 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -187,4 +187,7 @@
         REASON_RADIO_UNAVAILABLE,
         REASON_SIM_REFRESH_RESET
     };
+
+    // Initial MTU value.
+    public static final int UNSET_MTU = 0;
 }
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index 9d78ca5..95072a4 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -23,7 +23,7 @@
         </activity>
         <service
             android:name="com.android.onemedia.OnePlayerService"
-            android:exported="false"
+            android:exported="true"
             android:process="com.android.onemedia.service" />
         <service
             android:name=".provider.OneMediaRouteProvider"
diff --git a/tests/OneMedia/res/drawable/ic_fast_forward.xml b/tests/OneMedia/res/drawable/ic_fast_forward.xml
new file mode 100644
index 0000000..8daf07d
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_fast_forward.xml
@@ -0,0 +1,25 @@
+<!--
+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:width="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M4.0,18.0l8.5,-6.0L4.0,6.0L4.0,18.0zM13.0,6.0l0.0,12.0l8.5,-6.0L13.0,6.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_fast_rewind.xml b/tests/OneMedia/res/drawable/ic_fast_rewind.xml
new file mode 100644
index 0000000..4ed1f54
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_fast_rewind.xml
@@ -0,0 +1,24 @@
+<!--
+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:width="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M11.0,18.0L11.0,6.0l-8.5,6.0L11.0,18.0zM11.5,12.0l8.5,6.0L20.0,6.0L11.5,12.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_pause.xml b/tests/OneMedia/res/drawable/ic_pause.xml
new file mode 100644
index 0000000..15d0756
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_pause.xml
@@ -0,0 +1,25 @@
+<!--
+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:width="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.0,19.0l4.0,0.0L10.0,5.0L6.0,5.0L6.0,19.0zM14.0,5.0l0.0,14.0l4.0,0.0L18.0,5.0L14.0,5.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_play_arrow.xml b/tests/OneMedia/res/drawable/ic_play_arrow.xml
new file mode 100644
index 0000000..49d766d
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_play_arrow.xml
@@ -0,0 +1,25 @@
+<!--
+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:width="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M8.0,5.0l0.0,14.0 11.0,-7.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_skip_next.xml b/tests/OneMedia/res/drawable/ic_skip_next.xml
new file mode 100644
index 0000000..8a6ceca
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_skip_next.xml
@@ -0,0 +1,25 @@
+<!--
+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:width="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.0,18.0l8.5,-6.0L6.0,6.0L6.0,18.0zM16.0,6.0l0.0,12.0l2.0,0.0L18.0,6.0L16.0,6.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_skip_previous.xml b/tests/OneMedia/res/drawable/ic_skip_previous.xml
new file mode 100644
index 0000000..c5d07db
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_skip_previous.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.0,6.0l2.0,0.0l0.0,12.0l-2.0,0.0z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M9.5,12.0l8.5,6.0 0.0,-12.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_stop.xml b/tests/OneMedia/res/drawable/ic_stop.xml
new file mode 100644
index 0000000..6043fdb6
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_stop.xml
@@ -0,0 +1,25 @@
+<!--
+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:width="36dp"
+    android:height="36dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M6.0,6.0l12.0,0.0l0.0,12.0l-12.0,0.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/layout/activity_one_player.xml b/tests/OneMedia/res/layout/activity_one_player.xml
index 516562f..ce2d641 100644
--- a/tests/OneMedia/res/layout/activity_one_player.xml
+++ b/tests/OneMedia/res/layout/activity_one_player.xml
@@ -33,6 +33,19 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/has_video" />
+    <ImageView
+            android:id="@+id/art"
+            android:layout_width="match_parent"
+            android:layout_height="96dp"
+            android:scaleType="centerCrop"
+            android:visibility="gone"
+            />
+    <Button
+            android:id="@+id/art_picker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/art_picker"
+            />
     <LinearLayout
             android:id="@+id/controls"
             android:layout_width="match_parent"
diff --git a/tests/OneMedia/res/values/strings.xml b/tests/OneMedia/res/values/strings.xml
index 3735c8d..86657fd 100644
--- a/tests/OneMedia/res/values/strings.xml
+++ b/tests/OneMedia/res/values/strings.xml
@@ -12,5 +12,5 @@
     <string name="media_next_hint">Next content</string>
     <string name="has_video">Is video</string>
     <string name="has_duration">Has duration</string>
-
+    <string name="art_picker">Choose artwork</string>
 </resources>
diff --git a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
index d4df4c5..f53eac0 100644
--- a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
+++ b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
@@ -15,6 +15,7 @@
 
 package com.android.onemedia;
 
+import android.graphics.Bitmap;
 import android.media.session.MediaSession;
 import android.os.Bundle;
 
@@ -26,4 +27,5 @@
     void registerCallback(in IPlayerCallback cb);
     void unregisterCallback(in IPlayerCallback cb);
     void sendRequest(String action, in Bundle params, in IRequestCallback cb);
+    void setIcon(in Bitmap icon);
 }
diff --git a/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
new file mode 100644
index 0000000..a5bcda5
--- /dev/null
+++ b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
@@ -0,0 +1,234 @@
+package com.android.onemedia;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.onemedia.playback.RequestUtils;
+
+/**
+ * Keeps track of a notification and updates it automatically for a given
+ * MediaSession.
+ */
+public class NotificationHelper extends BroadcastReceiver {
+    private static final String TAG = "NotificationHelper";
+
+    private static final int NOTIFICATION_ID = 433; // John Cage, 1952
+
+    private final Service mService;
+    private final MediaSession mSession;
+    private final MediaController mController;
+    private final MediaController.TransportControls mTransportControls;
+    private final SparseArray<PendingIntent> mIntents = new SparseArray<PendingIntent>();
+
+    private PlaybackState mPlaybackState;
+    private MediaMetadata mMetadata;
+
+    private boolean mStarted = false;
+
+    public NotificationHelper(Service service, MediaSession session) {
+        mService = service;
+        mSession = session;
+        mController = session.getController();
+        mTransportControls = mController.getTransportControls();
+        String pkg = mService.getPackageName();
+
+        mIntents.put(R.drawable.ic_pause, PendingIntent.getBroadcast(mService, 100, new Intent(
+                com.android.onemedia.playback.RequestUtils.ACTION_PAUSE).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_play_arrow, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PLAY).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_skip_previous, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PREV).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_skip_next, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_NEXT).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_fast_rewind, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_REW).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+        mIntents.put(R.drawable.ic_fast_forward, PendingIntent.getBroadcast(mService, 100,
+                new Intent(com.android.onemedia.playback.RequestUtils.ACTION_FFWD).setPackage(pkg),
+                PendingIntent.FLAG_CANCEL_CURRENT));
+    }
+
+    /**
+     * Posts the notification and starts tracking the session to keep it
+     * updated. The notification will automatically be removed if the session is
+     * destroyed before {@link #onStop} is called.
+     */
+    public void onStart() {
+        mController.addCallback(mCb);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(RequestUtils.ACTION_FFWD);
+        filter.addAction(RequestUtils.ACTION_NEXT);
+        filter.addAction(RequestUtils.ACTION_PAUSE);
+        filter.addAction(RequestUtils.ACTION_PLAY);
+        filter.addAction(RequestUtils.ACTION_PREV);
+        filter.addAction(RequestUtils.ACTION_REW);
+        mService.registerReceiver(this, filter);
+
+        mMetadata = mController.getMetadata();
+        mPlaybackState = mController.getPlaybackState();
+
+        mStarted = true;
+        // The notification must be updated after setting started to true
+        updateNotification();
+    }
+
+    /**
+     * Removes the notification and stops tracking the session. If the session
+     * was destroyed this has no effect.
+     */
+    public void onStop() {
+        mStarted = false;
+        mController.removeCallback(mCb);
+        mService.unregisterReceiver(this);
+        updateNotification();
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        Log.d(TAG, "Received intent with action " + action);
+        if (RequestUtils.ACTION_PAUSE.equals(action)) {
+            mTransportControls.pause();
+        } else if (RequestUtils.ACTION_PLAY.equals(action)) {
+            mTransportControls.play();
+        } else if (RequestUtils.ACTION_NEXT.equals(action)) {
+            mTransportControls.skipToNext();
+        } else if (RequestUtils.ACTION_PREV.equals(action)) {
+            mTransportControls.skipToPrevious();
+        } else if (RequestUtils.ACTION_REW.equals(action)) {
+            mTransportControls.rewind();
+        } else if (RequestUtils.ACTION_FFWD.equals(action)) {
+            mTransportControls.fastForward();
+        }
+
+    }
+
+    private final MediaController.Callback mCb = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            mPlaybackState = state;
+            Log.d(TAG, "Received new playback state" + state);
+            updateNotification();
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            mMetadata = metadata;
+            Log.d(TAG, "Received new metadata " + metadata);
+            updateNotification();
+        }
+    };
+
+    NotificationManager mNoMan = null;
+
+    private void updateNotification() {
+        if (mNoMan == null) {
+            mNoMan = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
+        }
+        if (mPlaybackState == null) {
+            mNoMan.cancel(NOTIFICATION_ID);
+            return;
+        }
+        if (!mStarted) {
+            mNoMan.cancel(NOTIFICATION_ID);
+            return;
+        }
+
+        String status;
+        final int state = mPlaybackState.getState();
+        switch (state) {
+            case PlaybackState.STATE_PLAYING:
+                status = "PLAYING: ";
+                break;
+            case PlaybackState.STATE_PAUSED:
+                status = "PAUSED: ";
+                break;
+            case PlaybackState.STATE_STOPPED:
+                status = "STOPPED: ";
+                break;
+            case PlaybackState.STATE_ERROR:
+                status = "ERROR: ";
+                break;
+            case PlaybackState.STATE_BUFFERING:
+                status = "BUFFERING: ";
+                break;
+            case PlaybackState.STATE_NONE:
+            default:
+                status = "";
+                break;
+        }
+        CharSequence title, text;
+        Bitmap art;
+        if (mMetadata == null) {
+            title = status;
+            text = "Empty metadata!";
+            art = null;
+        } else {
+            MediaMetadata.Description description = mMetadata.getDescription();
+            title = description.getTitle();
+            text = description.getSubtitle();
+            art = description.getIcon();
+        }
+
+        String playPauseLabel = "";
+        int playPauseIcon;
+        if (state == PlaybackState.STATE_PLAYING) {
+            playPauseLabel = "Pause";
+            playPauseIcon = R.drawable.ic_pause;
+        } else {
+            playPauseLabel = "Play";
+            playPauseIcon = R.drawable.ic_play_arrow;
+        }
+
+        final long pos = mPlaybackState.getPosition();
+        final long end = mMetadata == null ? 0 : mMetadata
+                .getLong(MediaMetadata.METADATA_KEY_DURATION);
+        Notification notification = new Notification.Builder(mService)
+                .setSmallIcon(android.R.drawable.stat_notify_chat)
+                .setContentTitle(title)
+                .setContentText(text)
+                .setShowWhen(false)
+                .setContentInfo(DateUtils.formatElapsedTime(pos))
+                .setProgress((int) end, (int) pos, false)
+                .setLargeIcon(art)
+                .addAction(R.drawable.ic_skip_previous, "Previous",
+                        mIntents.get(R.drawable.ic_skip_previous))
+                .addAction(R.drawable.ic_fast_rewind, "Rewind",
+                        mIntents.get(R.drawable.ic_fast_rewind))
+                .addAction(playPauseIcon, playPauseLabel,
+                        mIntents.get(playPauseIcon))
+                .addAction(R.drawable.ic_fast_forward, "Fast Forward",
+                        mIntents.get(R.drawable.ic_fast_forward))
+                .addAction(R.drawable.ic_skip_next, "Next",
+                        mIntents.get(R.drawable.ic_skip_next))
+                .setStyle(new Notification.MediaStyle()
+                        .setShowActionsInCompactView(2)
+                        .setMediaSession(mSession.getSessionToken()))
+                .setColor(0xFFDB4437)
+                .build();
+
+        mService.startForeground(NOTIFICATION_ID, notification);
+    }
+
+}
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index 894377b..2ff3e20 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -17,20 +17,35 @@
 
 
 import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.media.MediaMetadata;
 import android.media.session.PlaybackState;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.MediaStore;
+import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.Menu;
 import android.view.View;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.EditText;
+import android.widget.ImageView;
 import android.widget.TextView;
 
+import java.io.IOException;
+
 public class OnePlayerActivity extends Activity {
     private static final String TAG = "OnePlayerActivity";
 
+    private static final int READ_REQUEST_CODE = 42;
+
     protected PlayerController mPlayer;
 
     private Button mStartButton;
@@ -41,8 +56,10 @@
     private EditText mContentText;
     private EditText mNextContentText;
     private CheckBox mHasVideo;
+    private ImageView mArtView;
 
-    private int mPlaybackState;
+    private PlaybackState mPlaybackState;
+    private Bitmap mAlbumArtBitmap;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -58,6 +75,10 @@
         mContentText = (EditText) findViewById(R.id.content);
         mNextContentText = (EditText) findViewById(R.id.next_content);
         mHasVideo = (CheckBox) findViewById(R.id.has_video);
+        mArtView = (ImageView) findViewById(R.id.art);
+
+        final Button artPicker = (Button) findViewById(R.id.art_picker);
+        artPicker.setOnClickListener(mButtonListener);
 
         mStartButton.setOnClickListener(mButtonListener);
         mPlayButton.setOnClickListener(mButtonListener);
@@ -86,6 +107,31 @@
         super.onPause();
     }
 
+    @Override
+    public void onActivityResult(int requestCode, int resultCode,
+            Intent resultData) {
+        if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+            Uri uri = null;
+            if (resultData != null) {
+                uri = resultData.getData();
+                Log.i(TAG, "Uri: " + uri.toString());
+                mAlbumArtBitmap = null;
+                try {
+                    mAlbumArtBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
+                } catch (IOException e) {
+                    Log.v(TAG, "Couldn't load album art", e);
+                }
+                mArtView.setImageBitmap(mAlbumArtBitmap);
+                if (mAlbumArtBitmap != null) {
+                    mArtView.setVisibility(View.VISIBLE);
+                } else {
+                    mArtView.setVisibility(View.GONE);
+                }
+                mPlayer.setArt(mAlbumArtBitmap);
+            }
+        }
+    }
+
     private void setControlsEnabled(boolean enabled) {
         mStartButton.setEnabled(enabled);
         mPlayButton.setEnabled(enabled);
@@ -94,36 +140,46 @@
     private View.OnClickListener mButtonListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
+            final int state = mPlaybackState.getState();
             switch (v.getId()) {
                 case R.id.play_button:
-                    Log.d(TAG, "Play button pressed, in state " + mPlaybackState);
-                    if (mPlaybackState == PlaybackState.STATE_PAUSED
-                            || mPlaybackState == PlaybackState.STATE_STOPPED) {
+                    Log.d(TAG, "Play button pressed, in state " + state);
+                    if (state == PlaybackState.STATE_PAUSED
+                            || state == PlaybackState.STATE_STOPPED) {
                         mPlayer.play();
-                    } else if (mPlaybackState == PlaybackState.STATE_PLAYING) {
+                    } else if (state == PlaybackState.STATE_PLAYING) {
                         mPlayer.pause();
                     }
                     break;
                 case R.id.start_button:
-                    Log.d(TAG, "Start button pressed, in state " + mPlaybackState);
+                    Log.d(TAG, "Start button pressed, in state " + state);
                     mPlayer.setContent(mContentText.getText().toString());
                     break;
                 case R.id.route_button:
                     mPlayer.showRoutePicker();
                     break;
+                case R.id.art_picker:
+                    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+                    intent.addCategory(Intent.CATEGORY_OPENABLE);
+                    intent.setType("image/*");
+
+                    startActivityForResult(intent, READ_REQUEST_CODE);
+                    break;
             }
 
         }
     };
 
     private PlayerController.Listener mListener = new PlayerController.Listener() {
+        public MediaMetadata mMetadata;
+
         @Override
         public void onPlaybackStateChange(PlaybackState state) {
-            mPlaybackState = state.getState();
+            mPlaybackState = state;
             boolean enablePlay = false;
             boolean enableControls = true;
             StringBuilder statusBuilder = new StringBuilder();
-            switch (mPlaybackState) {
+            switch (mPlaybackState.getState()) {
                 case PlaybackState.STATE_PLAYING:
                     statusBuilder.append("playing");
                     mPlayButton.setText("Pause");
@@ -172,7 +228,7 @@
 
         @Override
         public void onMetadataChange(MediaMetadata metadata) {
-            Log.d(TAG, "Metadata update! Title: " + metadata);
+            mMetadata = metadata;
         }
     };
 }
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index c0799fc..c8d72ca 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.graphics.Bitmap;
 import android.util.Log;
 
 import com.android.onemedia.playback.RequestUtils;
@@ -52,6 +53,7 @@
     private Handler mHandler = new Handler();
 
     private boolean mResumed;
+    private Bitmap mArt;
 
     public PlayerController(Activity context, Intent serviceIntent) {
         mContext = context;
@@ -89,6 +91,16 @@
         unbindFromService();
     }
 
+    public void setArt(Bitmap art) {
+        mArt = art;
+        if (mBinder != null) {
+            try {
+                mBinder.setIcon(art);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     public void play() {
         if (mTransportControls != null) {
             mTransportControls.play();
@@ -125,6 +137,16 @@
         // TODO
     }
 
+    public MediaSession.Token getSessionToken() {
+        if (mBinder != null) {
+            try {
+                return mBinder.getSessionToken();
+            } catch (RemoteException e) {
+            }
+        }
+        return null;
+    }
+
     private void unbindFromService() {
         mContext.unbindService(mServiceConnection);
     }
@@ -165,6 +187,9 @@
             mContext.setMediaController(mController);
             mController.addCallback(mControllerCb, mHandler);
             mTransportControls = mController.getTransportControls();
+            if (mArt != null) {
+                setArt(mArt);
+            }
             Log.d(TAG, "Ready to use PlayerService");
 
             if (mListener != null) {
@@ -194,6 +219,9 @@
                 return;
             }
             Log.d(TAG, "Received metadata change, " + metadata.getDescription());
+            if (mListener != null) {
+                mListener.onMetadataChange(metadata);
+            }
         }
     }
 
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
index 58ee4a1..9967c99 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
@@ -17,6 +17,7 @@
 
 import android.app.Service;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
@@ -34,6 +35,7 @@
 
     private PlayerBinder mBinder;
     private PlayerSession mSession;
+    private NotificationHelper mNotifyHelper;
     private Intent mIntent;
     private boolean mStarted = false;
 
@@ -47,6 +49,7 @@
             mSession = onCreatePlayerController();
             mSession.createSession();
             mSession.setListener(mPlayerListener);
+            mNotifyHelper = new NotificationHelper(this, mSession.mSession);
         }
     }
 
@@ -75,6 +78,7 @@
         if (!mStarted) {
             Log.d(TAG, "Starting self");
             startService(onCreateServiceIntent());
+            mNotifyHelper.onStart();
             mStarted = true;
         }
     }
@@ -82,6 +86,7 @@
     public void onPlaybackEnded() {
         if (mStarted) {
             Log.d(TAG, "Stopping self");
+            mNotifyHelper.onStop();
             stopSelf();
             mStarted = false;
         }
@@ -150,8 +155,17 @@
 
         @Override
         public MediaSession.Token getSessionToken() throws RemoteException {
+            if (mSession == null) {
+                Log.e(TAG, "Error in PlayerService: mSession=null in getSessionToken()");
+                return null;
+            }
             return mSession.getSessionToken();
         }
+
+        @Override
+        public void setIcon(Bitmap icon) {
+            mSession.setIcon(icon);
+        }
     }
 
 }
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 890d68d..9afcf24 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata;
 import android.media.routing.MediaRouteSelector;
 import android.media.routing.MediaRouter;
 import android.media.routing.MediaRouter.ConnectionRequest;
@@ -50,6 +52,7 @@
     protected Renderer mRenderer;
     protected MediaSession.Callback mCallback;
     protected Renderer.Listener mRenderListener;
+    protected MediaMetadata.Builder mMetadataBuilder;
 
     protected PlaybackState mPlaybackState;
     protected Listener mListener;
@@ -66,6 +69,8 @@
         mPlaybackState = psBob.build();
 
         mRenderer.registerListener(mRenderListener);
+
+        initMetadata();
     }
 
     public void createSession() {
@@ -92,6 +97,7 @@
                 | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
         mSession.setMediaRouter(mRouter);
         mSession.setActive(true);
+        updateMetadata();
     }
 
     public void onDestroy() {
@@ -130,6 +136,19 @@
         mRenderer.setNextContent(request);
     }
 
+    public void setIcon(Bitmap icon) {
+        mMetadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, icon);
+        updateMetadata();
+    }
+
+    private void updateMetadata() {
+        // This is a mild abuse of metadata and shouldn't be duplicated in real
+        // code
+        if (mSession != null && mSession.isActive()) {
+            mSession.setMetadata(mMetadataBuilder.build());
+        }
+    }
+
     private void updateState(int newState) {
         float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
         long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
@@ -140,6 +159,14 @@
         mSession.setPlaybackState(mPlaybackState);
     }
 
+    private void initMetadata() {
+        mMetadataBuilder = new MediaMetadata.Builder();
+        mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
+                "OneMedia display title");
+        mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
+                "OneMedia display subtitle");
+    }
+
     public interface Listener {
         public void onPlayStateChanged(PlaybackState state);
     }
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
index 3778c5f..1688395 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
@@ -26,6 +26,12 @@
 public class RequestUtils {
     public static final String ACTION_SET_CONTENT = "set_content";
     public static final String ACTION_SET_NEXT_CONTENT = "set_next_content";
+    public static final String ACTION_PAUSE = "com.android.onemedia.pause";
+    public static final String ACTION_PLAY = "com.android.onemedia.play";
+    public static final String ACTION_REW = "com.android.onemedia.rew";
+    public static final String ACTION_FFWD = "com.android.onemedia.ffwd";
+    public static final String ACTION_PREV = "com.android.onemedia.prev";
+    public static final String ACTION_NEXT = "com.android.onemedia.next";
 
     public static final String EXTRA_KEY_SOURCE = "source";
     public static final String EXTRA_KEY_METADATA = "metadata";
diff --git a/tests/UsesFeature2Test/AndroidManifest.xml b/tests/UsesFeature2Test/AndroidManifest.xml
index 6b6c4da..8caf4a1 100644
--- a/tests/UsesFeature2Test/AndroidManifest.xml
+++ b/tests/UsesFeature2Test/AndroidManifest.xml
@@ -33,12 +33,5 @@
         <uses-feature android:name="android.hardware.opengles.aep" />
     </feature-group>
 
-    <application android:label="@string/app_title">
-        <activity android:name="ActivityMain">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
+    <application android:label="@string/app_title" android:hasCode="false" />
 </manifest>
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 41102fe..fe0a601 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -764,12 +764,9 @@
         return 1;
     }
 
+    // The dynamicRefTable can be null if there are no resources for this asset cookie.
+    // This fine.
     const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
-    if (dynamicRefTable == NULL) {
-        fprintf(stderr, "ERROR: failed to find dynamic reference table for asset cookie %d\n",
-                assetsCookie);
-        return 1;
-    }
 
     Asset* asset = NULL;
 
@@ -1676,12 +1673,8 @@
 
                         String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
-                            int required = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
-                            top.features.add(name, required);
-                            if (required) {
-                                addParentFeatures(&top, name);
-                            }
-
+                            top.features.add(name, true);
+                            addParentFeatures(&top, name);
                         } else {
                             int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
                             if (error == "") {
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 010d59b..0a80805 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1470,6 +1470,8 @@
     String16 action16("action");
     String16 category16("category");
     String16 data16("scheme");
+    String16 feature_group16("feature-group");
+    String16 uses_feature16("uses-feature");
     const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
     const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
@@ -1680,10 +1682,43 @@
                                  schemeIdentChars, true) != ATTR_OKAY) {
                     hasErrors = true;
                 }
+            } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) {
+                int depth = 1;
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+                       && code > ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::START_TAG) {
+                        depth++;
+                        if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) {
+                            ssize_t idx = block.indexOfAttribute(
+                                    RESOURCES_ANDROID_NAMESPACE, "required");
+                            if (idx < 0) {
+                                continue;
+                            }
+
+                            int32_t data = block.getAttributeData(idx);
+                            if (data == 0) {
+                                fprintf(stderr, "%s:%d: Tag <uses-feature> can not have "
+                                        "android:required=\"false\" when inside a "
+                                        "<feature-group> tag.\n",
+                                        manifestPath.string(), block.getLineNumber());
+                                hasErrors = true;
+                            }
+                        }
+                    } else if (code == ResXMLTree::END_TAG) {
+                        depth--;
+                        if (depth == 0) {
+                            break;
+                        }
+                    }
+                }
             }
         }
     }
 
+    if (hasErrors) {
+        return UNKNOWN_ERROR;
+    }
+
     if (resFile != NULL) {
         // These resources are now considered to be a part of the included
         // resources, for others to reference.