Merge "Fixing yet another build breackage"
diff --git a/api/current.txt b/api/current.txt
index e41b3fe..09102fb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4976,15 +4976,19 @@
     field public static final java.lang.String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
     field public static final java.lang.String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
     field public static final java.lang.String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
+    field public static final java.lang.String ACTION_APPWIDGET_HOST_RESTORED = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
     field public static final java.lang.String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
     field public static final java.lang.String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
+    field public static final java.lang.String ACTION_APPWIDGET_RESTORED = "android.appwidget.action.APPWIDGET_RESTORED";
     field public static final java.lang.String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
     field public static final java.lang.String EXTRA_APPWIDGET_ID = "appWidgetId";
     field public static final java.lang.String EXTRA_APPWIDGET_IDS = "appWidgetIds";
+    field public static final java.lang.String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds";
     field public static final java.lang.String EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions";
     field public static final java.lang.String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
     field public static final java.lang.String EXTRA_CUSTOM_EXTRAS = "customExtras";
     field public static final java.lang.String EXTRA_CUSTOM_INFO = "customInfo";
+    field public static final java.lang.String EXTRA_HOST_ID = "hostId";
     field public static final int INVALID_APPWIDGET_ID = 0; // 0x0
     field public static final java.lang.String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
     field public static final java.lang.String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
@@ -5001,6 +5005,7 @@
     method public void onDisabled(android.content.Context);
     method public void onEnabled(android.content.Context);
     method public void onReceive(android.content.Context, android.content.Intent);
+    method public void onRestored(android.content.Context, int[], int[]);
     method public void onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]);
   }
 
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index a4e2718..c4fe6cf 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -21,5 +21,8 @@
 
 LOCAL_MODULE:= bootanimation
 
+ifdef TARGET_32_BIT_SURFACEFLINGER
+LOCAL_32_BIT_ONLY := true
+endif
 
 include $(BUILD_EXECUTABLE)
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index 73fd660..2673031 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -68,6 +68,7 @@
         boolean saveObbs = false;
         boolean saveShared = false;
         boolean doEverything = false;
+        boolean doWidgets = false;
         boolean allIncludesSystem = true;
 
         String arg;
@@ -89,6 +90,10 @@
                     allIncludesSystem = true;
                 } else if ("-nosystem".equals(arg)) {
                     allIncludesSystem = false;
+                } else if ("-widgets".equals(arg)) {
+                    doWidgets = true;
+                } else if ("-nowidgets".equals(arg)) {
+                    doWidgets = false;
                 } else if ("-all".equals(arg)) {
                     doEverything = true;
                 } else {
@@ -114,8 +119,8 @@
         try {
             fd = ParcelFileDescriptor.adoptFd(socketFd);
             String[] packArray = new String[packages.size()];
-            mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doEverything,
-                    allIncludesSystem, packages.toArray(packArray));
+            mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doWidgets,
+                    doEverything, allIncludesSystem, packages.toArray(packArray));
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to invoke backup manager for backup");
         } finally {
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 087f83c..4ca06ed 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -76,8 +76,9 @@
      * @param callbackBinder Binder on which to indicate operation completion,
      *        passed here as a convenience to the agent.
      */
-    void doRestore(in ParcelFileDescriptor data, int appVersionCode,
-            in ParcelFileDescriptor newState, int token, IBackupManager callbackBinder);
+    void doRestore(in ParcelFileDescriptor data,
+            int appVersionCode, in ParcelFileDescriptor newState,
+            int token, IBackupManager callbackBinder);
 
     /**
      * Perform a "full" backup to the given file descriptor.  The output file is presumed
@@ -112,8 +113,15 @@
      * @param path Relative path of the file within its semantic domain.
      * @param mode Access mode of the file system entity, e.g. 0660.
      * @param mtime Last modification time of the file system entity.
+     * @param token Opaque token identifying this transaction.  This must
+     *        be echoed back to the backup service binder once the agent is
+     *        finished restoring the application based on the restore data
+     *        contents.
+     * @param callbackBinder Binder on which to indicate operation completion,
+     *        passed here as a convenience to the agent.
      */
     void doRestoreFile(in ParcelFileDescriptor data, long size,
             int type, String domain, String path, long mode, long mtime,
             int token, IBackupManager callbackBinder);
+
 }
diff --git a/core/java/android/app/backup/BackupDataOutput.java b/core/java/android/app/backup/BackupDataOutput.java
index 3a070b6..845784f 100644
--- a/core/java/android/app/backup/BackupDataOutput.java
+++ b/core/java/android/app/backup/BackupDataOutput.java
@@ -17,6 +17,7 @@
 package android.app.backup;
 
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -76,13 +77,19 @@
     /**
      * Mark the beginning of one record in the backup data stream. This must be called before
      * {@link #writeEntityData}.
-     * @param key A string key that uniquely identifies the data record within the application
+     * @param key A string key that uniquely identifies the data record within the application.
+     *    Keys whose first character is \uFF00 or higher are not valid.
      * @param dataSize The size in bytes of this record's data.  Passing a dataSize
      *    of -1 indicates that the record under this key should be deleted.
      * @return The number of bytes written to the backup stream
      * @throws IOException if the write failed
      */
     public int writeEntityHeader(String key, int dataSize) throws IOException {
+        if (key != null && key.charAt(0) >= 0xff00) {
+            if (Process.myUid() != Process.SYSTEM_UID) {
+                throw new IllegalArgumentException("Invalid key " + key);
+            }
+        }
         int result = writeEntityHeader_native(mBackupWriter, key, dataSize);
         if (result >= 0) {
             return result;
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 12ee3b6..c629a2e 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -167,7 +167,7 @@
      *     are to be backed up.  The <code>allApps</code> parameter supersedes this.
      */
     void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
-            boolean includeShared, boolean allApps, boolean allIncludesSystem,
+            boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem,
             in String[] packageNames);
 
     /**
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 8a89cbc..dd3a871 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -214,6 +214,12 @@
     public static final String EXTRA_CUSTOM_INFO = "customInfo";
 
     /**
+     * An intent extra attached to the {@link #ACTION_APPWIDGET_HOST_RESTORED} broadcast,
+     * indicating the integer ID of the host whose widgets have just been restored.
+     */
+    public static final String EXTRA_HOST_ID = "hostId";
+
+    /**
      * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
      * {@link android.os.Bundle} objects to mix in to the list of AppWidgets that are
      * installed.  It will be added to the extras object on the {@link android.content.Intent}
@@ -310,6 +316,86 @@
     public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
 
     /**
+     * Sent to providers after AppWidget state related to the provider has been restored from
+     * backup. The intent contains information about how to translate AppWidget ids from the
+     * restored data to their new equivalents.
+     *
+     * <p>The intent will contain the following extras:
+     *
+     * <table>
+     *   <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td>
+     *     <td>The set of appWidgetIds represented in a restored backup that have been successfully
+     *     incorporated into the current environment.  This may be all of the AppWidgets known
+     *     to this application, or just a subset.  Each entry in this array of appWidgetIds has
+     *     a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td>
+     *  </tr>
+     *   <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_IDS}</td>
+     *     <td>The set of appWidgetIds now valid for this application.  The app should look at
+     *     its restored widget configuration and translate each appWidgetId in the
+     *     {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding
+     *     index within this array.</td>
+     *  </tr>
+     * </table>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see {@link #ACTION_APPWIDGET_HOST_RESTORED} for the corresponding host broadcast
+     */
+    public static final String ACTION_APPWIDGET_RESTORED
+            = "android.appwidget.action.APPWIDGET_RESTORED";
+
+    /**
+     * Sent to widget hosts after AppWidget state related to the host has been restored from
+     * backup. The intent contains information about how to translate AppWidget ids from the
+     * restored data to their new equivalents.  If an application maintains multiple separate
+     * widget hosts instances, it will receive this broadcast separately for each one.
+     *
+     * <p>The intent will contain the following extras:
+     *
+     * <table>
+     *   <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td>
+     *     <td>The set of appWidgetIds represented in a restored backup that have been successfully
+     *     incorporated into the current environment.  This may be all of the AppWidgets known
+     *     to this application, or just a subset.  Each entry in this array of appWidgetIds has
+     *     a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td>
+     *  </tr>
+     *   <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_IDS}</td>
+     *     <td>The set of appWidgetIds now valid for this application.  The app should look at
+     *     its restored widget configuration and translate each appWidgetId in the
+     *     {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding
+     *     index within this array.</td>
+     *  </tr>
+     *  <tr>
+     *     <td>{@link #EXTRA_HOST_ID}</td>
+     *     <td>The integer ID of the widget host instance whose state has just been restored.</td>
+     *  </tr>
+     * </table>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see {@link #ACTION_APPWIDGET_RESTORED} for the corresponding provider broadcast
+     */
+    public static final String ACTION_APPWIDGET_HOST_RESTORED
+            = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
+
+    /**
+     * An intent extra that contains multiple appWidgetIds.  These are id values as
+     * they were provided to the application during a recent restore from backup.  It is
+     * attached to the {@link #ACTION_APPWIDGET_RESTORED} broadcast intent.
+     *
+     * <p>
+     * The value will be an int array that can be retrieved like this:
+     * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS}
+     */
+    public static final String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds";
+
+    /**
      * Field for the manifest meta-data tag.
      *
      * @see AppWidgetProviderInfo
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index edf142b..ab91edf 100644
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -66,15 +66,13 @@
                     this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
                 }
             }
-        }
-        else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
+        } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
             Bundle extras = intent.getExtras();
             if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
                 final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                 this.onDeleted(context, new int[] { appWidgetId });
             }
-        }
-        else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
+        } else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
             Bundle extras = intent.getExtras();
             if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
                     && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
@@ -83,19 +81,28 @@
                 this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
                         appWidgetId, widgetExtras);
             }
-        }
-        else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
+        } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
             this.onEnabled(context);
-        }
-        else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
+        } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
             this.onDisabled(context);
+        } else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) {
+            Bundle extras = intent.getExtras();
+            if (extras != null) {
+                int[] oldIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
+                int[] newIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+                if (oldIds != null && oldIds.length > 0) {
+                    this.onRestored(context, oldIds, newIds);
+                    this.onUpdate(context, AppWidgetManager.getInstance(context), newIds);
+                }
+            }
         }
     }
     // END_INCLUDE(onReceive)
 
     /**
-     * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} broadcast when
-     * this AppWidget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
+     * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} and
+     * {@link AppWidgetManager#ACTION_APPWIDGET_RESTORED} broadcasts when this AppWidget
+     * provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
      * for a set of AppWidgets.  Override this method to implement your own AppWidget functionality.
      *
      * {@more}
@@ -123,8 +130,8 @@
      *                  running.
      * @param appWidgetManager A {@link AppWidgetManager} object you can call {@link
      *                  AppWidgetManager#updateAppWidget} on.
-     * @param appWidgetId The appWidgetId of the widget who's size changed.
-     * @param newOptions The appWidgetId of the widget who's size changed.
+     * @param appWidgetId The appWidgetId of the widget whose size changed.
+     * @param newOptions The appWidgetId of the widget whose size changed.
      *
      * @see AppWidgetManager#ACTION_APPWIDGET_OPTIONS_CHANGED
      */
@@ -181,4 +188,24 @@
      */
     public void onDisabled(Context context) {
     }
+
+    /**
+     * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_RESTORED} broadcast
+     * when instances of this AppWidget provider have been restored from backup.  If your
+     * provider maintains any persistent data about its widget instances, override this method
+     * to remap the old AppWidgetIds to the new values and update any other app state that may
+     * be relevant.
+     *
+     * <p>This callback will be followed immediately by a call to {@link #onUpdate} so your
+     * provider can immediately generate new RemoteViews suitable for its newly-restored set
+     * of instances.
+     *
+     * {@more}
+     *
+     * @param context
+     * @param oldWidgetIds
+     * @param newWidgetIds
+     */
+    public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+    }
 }
diff --git a/core/java/android/os/BatteryProperty.java b/core/java/android/os/BatteryProperty.java
index 346f5de..76b0dc4 100644
--- a/core/java/android/os/BatteryProperty.java
+++ b/core/java/android/os/BatteryProperty.java
@@ -29,6 +29,7 @@
     public static final int BATTERY_PROP_CHARGE_COUNTER = 1;
     public static final int BATTERY_PROP_CURRENT_NOW = 2;
     public static final int BATTERY_PROP_CURRENT_AVG = 3;
+    public static final int BATTERY_PROP_CAPACITY = 4;
 
     public int valueInt;
 
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 7ddd5d2..5214dd9 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -59,6 +59,5 @@
     void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection, int userId);
     void unbindRemoteViewsService(int appWidgetId, in Intent intent, int userId);
     int[] getAppWidgetIds(in ComponentName provider, int userId);
-
 }
 
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index a604d84..1bfad05 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -34,6 +34,8 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
 
 import libcore.io.ErrnoException;
 import libcore.io.Libcore;
@@ -55,20 +57,24 @@
     private static final String TRANSPORT_DESTINATION_STRING
             = "Backing up to debug-only private cache";
 
-    // The single hardcoded restore set always has the same (nonzero!) token
-    private static final long RESTORE_TOKEN = 1;
+    // The currently-active restore set always has the same (nonzero!) token
+    private static final long CURRENT_SET_TOKEN = 1;
 
     private Context mContext;
     private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
+    private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
+
     private PackageInfo[] mRestorePackages = null;
     private int mRestorePackage = -1;  // Index into mRestorePackages
+    private File mRestoreDataDir;
+    private long mRestoreToken;
 
 
     public LocalTransport(Context context) {
         mContext = context;
-        mDataDir.mkdirs();
-        if (!SELinux.restorecon(mDataDir)) {
-            Log.e(TAG, "SELinux restorecon failed for " + mDataDir);
+        mCurrentSetDir.mkdirs();
+        if (!SELinux.restorecon(mCurrentSetDir)) {
+            Log.e(TAG, "SELinux restorecon failed for " + mCurrentSetDir);
         }
     }
 
@@ -96,7 +102,7 @@
 
     public int initializeDevice() {
         if (DEBUG) Log.v(TAG, "wiping all data");
-        deleteContents(mDataDir);
+        deleteContents(mCurrentSetDir);
         return BackupConstants.TRANSPORT_OK;
     }
 
@@ -112,7 +118,7 @@
             }
         }
 
-        File packageDir = new File(mDataDir, packageInfo.packageName);
+        File packageDir = new File(mCurrentSetDir, packageInfo.packageName);
         packageDir.mkdirs();
 
         // Each 'record' in the restore set is kept in its own file, named by
@@ -193,7 +199,7 @@
     public int clearBackupData(PackageInfo packageInfo) {
         if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
 
-        File packageDir = new File(mDataDir, packageInfo.packageName);
+        File packageDir = new File(mCurrentSetDir, packageInfo.packageName);
         final File[] fileset = packageDir.listFiles();
         if (fileset != null) {
             for (File f : fileset) {
@@ -210,22 +216,38 @@
     }
 
     // Restore handling
+    static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 }; 
     public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
-        // one hardcoded restore set
-        RestoreSet set = new RestoreSet("Local disk image", "flash", RESTORE_TOKEN);
-        RestoreSet[] array = { set };
-        return array;
+        long[] existing = new long[POSSIBLE_SETS.length + 1];
+        int num = 0;
+
+        // see which possible non-current sets exist, then put the current set at the end
+        for (long token : POSSIBLE_SETS) {
+            if ((new File(mDataDir, Long.toString(token))).exists()) {
+                existing[num++] = token;
+            }
+        }
+        // and always the currently-active set last
+        existing[num++] = CURRENT_SET_TOKEN;
+
+        RestoreSet[] available = new RestoreSet[num];
+        for (int i = 0; i < available.length; i++) {
+            available[i] = new RestoreSet("Local disk image", "flash", existing[i]);
+        }
+        return available;
     }
 
     public long getCurrentRestoreSet() {
-        // The hardcoded restore set always has the same token
-        return RESTORE_TOKEN;
+        // The current restore set always has the same token
+        return CURRENT_SET_TOKEN;
     }
 
     public int startRestore(long token, PackageInfo[] packages) {
         if (DEBUG) Log.v(TAG, "start restore " + token);
         mRestorePackages = packages;
         mRestorePackage = -1;
+        mRestoreToken = token;
+        mRestoreDataDir = new File(mDataDir, Long.toString(token));
         return BackupConstants.TRANSPORT_OK;
     }
 
@@ -234,7 +256,7 @@
         while (++mRestorePackage < mRestorePackages.length) {
             String name = mRestorePackages[mRestorePackage].packageName;
             // skip packages where we have a data dir but no actual contents
-            String[] contents = (new File(mDataDir, name)).list();
+            String[] contents = (new File(mRestoreDataDir, name)).list();
             if (contents != null && contents.length > 0) {
                 if (DEBUG) Log.v(TAG, "  nextRestorePackage() = " + name);
                 return name;
@@ -248,29 +270,32 @@
     public int getRestoreData(ParcelFileDescriptor outFd) {
         if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
         if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
-        File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
+        File packageDir = new File(mRestoreDataDir, mRestorePackages[mRestorePackage].packageName);
 
         // The restore set is the concatenation of the individual record blobs,
-        // each of which is a file in the package's directory
-        File[] blobs = packageDir.listFiles();
+        // each of which is a file in the package's directory.  We return the
+        // data in lexical order sorted by key, so that apps which use synthetic
+        // keys like BLOB_1, BLOB_2, etc will see the date in the most obvious
+        // order.
+        ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
         if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
-            Log.e(TAG, "Error listing directory: " + packageDir);
+            Log.e(TAG, "No keys for package: " + packageDir);
             return BackupConstants.TRANSPORT_ERROR;
         }
 
         // We expect at least some data if the directory exists in the first place
-        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.length + " key files");
+        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.size() + " key files");
         BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
         try {
-            for (File f : blobs) {
+            for (DecodedFilename keyEntry : blobs) {
+                File f = keyEntry.file;
                 FileInputStream in = new FileInputStream(f);
                 try {
                     int size = (int) f.length();
                     byte[] buf = new byte[size];
                     in.read(buf);
-                    String key = new String(Base64.decode(f.getName()));
-                    if (DEBUG) Log.v(TAG, "    ... key=" + key + " size=" + size);
-                    out.writeEntityHeader(key, size);
+                    if (DEBUG) Log.v(TAG, "    ... key=" + keyEntry.key + " size=" + size);
+                    out.writeEntityHeader(keyEntry.key, size);
                     out.writeEntityData(buf, size);
                 } finally {
                     in.close();
@@ -283,6 +308,39 @@
         }
     }
 
+    static class DecodedFilename implements Comparable<DecodedFilename> {
+        public File file;
+        public String key;
+
+        public DecodedFilename(File f) {
+            file = f;
+            key = new String(Base64.decode(f.getName()));
+        }
+
+        @Override
+        public int compareTo(DecodedFilename other) {
+            // sorts into ascending lexical order by decoded key
+            return key.compareTo(other.key);
+        }
+    }
+
+    // Return a list of the files in the given directory, sorted lexically by
+    // the Base64-decoded file name, not by the on-disk filename
+    private ArrayList<DecodedFilename> contentsByKey(File dir) {
+        File[] allFiles = dir.listFiles();
+        if (allFiles == null || allFiles.length == 0) {
+            return null;
+        }
+
+        // Decode the filenames into keys then sort lexically by key
+        ArrayList<DecodedFilename> contents = new ArrayList<DecodedFilename>();
+        for (File f : allFiles) {
+            contents.add(new DecodedFilename(f));
+        }
+        Collections.sort(contents);
+        return contents;
+    }
+
     public void finishRestore() {
         if (DEBUG) Log.v(TAG, "finishRestore()");
     }
diff --git a/core/java/com/android/server/AppWidgetBackupBridge.java b/core/java/com/android/server/AppWidgetBackupBridge.java
new file mode 100644
index 0000000..2ea2f79
--- /dev/null
+++ b/core/java/com/android/server/AppWidgetBackupBridge.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.util.List;
+
+/**
+ * Runtime bridge between the Backup Manager Service and the App Widget Service,
+ * since those two modules are intentionally decoupled for modularity.
+ *
+ * @hide
+ */
+public class AppWidgetBackupBridge {
+    private static WidgetBackupProvider sAppWidgetService;
+
+    public static void register(WidgetBackupProvider instance) {
+        sAppWidgetService = instance;
+    }
+
+    public static List<String> getWidgetParticipants(int userId) {
+        return (sAppWidgetService != null)
+                ? sAppWidgetService.getWidgetParticipants(userId)
+                : null;
+    }
+
+    public static byte[] getWidgetState(String packageName, int userId) {
+        return (sAppWidgetService != null)
+                ? sAppWidgetService.getWidgetState(packageName, userId)
+                : null;
+    }
+
+    public static void restoreStarting(int userId) {
+        if (sAppWidgetService != null) {
+            sAppWidgetService.restoreStarting(userId);
+        }
+    }
+
+    public static void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+        if (sAppWidgetService != null) {
+            sAppWidgetService.restoreWidgetState(packageName, restoredState, userId);
+        }
+    }
+
+    public static void restoreFinished(int userId) {
+        if (sAppWidgetService != null) {
+            sAppWidgetService.restoreFinished(userId);
+        }
+    }
+}
diff --git a/core/java/com/android/server/WidgetBackupProvider.java b/core/java/com/android/server/WidgetBackupProvider.java
new file mode 100644
index 0000000..a2efbdd
--- /dev/null
+++ b/core/java/com/android/server/WidgetBackupProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.util.List;
+
+/**
+ * Shim to allow core/backup to communicate with the app widget service
+ * about various important events without needing to be able to see the
+ * implementation of the service.
+ *
+ * @hide
+ */
+public interface WidgetBackupProvider {
+    public List<String> getWidgetParticipants(int userId);
+    public byte[] getWidgetState(String packageName, int userId);
+    public void restoreStarting(int userId);
+    public void restoreWidgetState(String packageName, byte[] restoredState, int userId);
+    public void restoreFinished(int userId);
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 37ef539b..4e1efc6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -83,6 +83,8 @@
     <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DELETED" />
     <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DISABLED" />
     <protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_RESTORED" />
 
     <protected-broadcast android:name="android.backup.intent.RUN" />
     <protected-broadcast android:name="android.backup.intent.CLEAR" />
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 682e0ea..0330b7b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -690,7 +690,7 @@
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"conèixer les observacions sobre les condicions de la xarxa"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Permet que una aplicació conegui les observacions sobre les condicions de la xarxa. No s\'ha de necessitar mai per a aplicacions normals."</string>
     <string name="permlab_setInputCalibration" msgid="4902620118878467615">"canviar el calibratge del dispositiu d\'entrada"</string>
-    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permet que l\'aplicació modifiqui els paràmetres de calibratge de la pantalla tàctil. S\'ha de procurar no fer servir mai aquesta opció per a les aplicacions normals."</string>
+    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permet que l\'aplicació modifiqui els paràmetres de calibratge de la pantalla tàctil. No ha de ser mai necessari per a aplicacions normals."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Definir les normes de contrasenya"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controla la longitud i els caràcters permesos a les contrasenyes de desbloqueig de pantalla."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Controlar intents de desbloqueig de pantalla"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ffe25013..e144e9d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1435,8 +1435,8 @@
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
     <string name="activitychooserview_choose_application" msgid="2125168057199941199">"Επιλέξτε κάποια εφαρμογή"</string>
     <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Δεν ήταν δυνατή η εκκίνηση του <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινή χρήση με"</string>
-    <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινή χρήση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινοποίηση με"</string>
+    <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινοποίηση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="content_description_sliding_handle" msgid="415975056159262248">"Στοιχείο χειρισμού με δυνατότητα ολίσθησης. Αγγίξτε και πατήστε παρατεταμένα."</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Σύρετε για ξεκλείδωμα."</string>
     <string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"Συνδέστε ακουστικά για να ακούσετε τα πλήκτρα του κωδικού πρόσβασης να εκφωνούνται."</string>
@@ -1480,7 +1480,7 @@
     <string name="sha1_fingerprint" msgid="7930330235269404581">"Αποτύπωμα SHA-1"</string>
     <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Εμφάνιση όλων"</string>
     <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Επιλογή δραστηριότητας"</string>
-    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινή χρήση με"</string>
+    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινοποίηση με"</string>
     <string name="list_delimeter" msgid="3975117572185494152">", "</string>
     <string name="sending" msgid="3245653681008218030">"Γίνεται αποστολή…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Εκκίνηση προγράμματος περιήγησης;"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index ec4ed33..2c9599c 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -689,7 +689,7 @@
     <string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite al propietario ejecutar la aplicación de configuración proporcionada por el proveedor. Las aplicaciones normales no deberían necesitar este permiso."</string>
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Detectar cambios en el estado de la red"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Permite que una aplicación detecte cambios en el estado de la red. Las aplicaciones normales no deberían necesitar este permiso."</string>
-    <string name="permlab_setInputCalibration" msgid="4902620118878467615">"Cambiar la calibración del dispositivo de entrada"</string>
+    <string name="permlab_setInputCalibration" msgid="4902620118878467615">"cambiar la calibración del dispositivo de entrada"</string>
     <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite que la aplicación modifique los parámetros de calibración de la pantalla táctil. Las aplicaciones normales no deberían necesitar este permiso."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Establecer reglas de contraseña"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas para desbloquear la pantalla"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 857a1ea..308ea59 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -690,7 +690,7 @@
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"گوش دادن برای بررسی شرایط شبکه"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"به برنامه امکان می‌دهد برای بررسی شرایط شبکه گوش دهد. این امکان هرگز نباید برای برنامه‌های معمولی مورد نیاز باشد."</string>
     <string name="permlab_setInputCalibration" msgid="4902620118878467615">"تغییر کالیبراسیون دستگاه ورودی"</string>
-    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"به برنامه امکان می‌دهد پارامترهای صفحه لمسی را کالیبره کند. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
+    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"به برنامه امکان می‌دهد پارامترهای کالیبراسیون صفحه لمسی را تغییر دهد. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"‏طول و نویسه‎های مجاز در گذرواژه‌های بازکردن قفل صفحه را کنترل کنید."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاش‌های قفل گشایی صفحه"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 1d6b16d..b40117b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -690,7 +690,7 @@
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"hálózati körülményekkel kapcsolatos észrevételek figyelemmel kísérése"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Lehetővé teszi egy alkalmazás számára, hogy figyelemmel kísérje a hálózati körülményekkel kapcsolatos észrevételeket. A normál alkalmazásoknak erre soha nincs szükségük."</string>
     <string name="permlab_setInputCalibration" msgid="4902620118878467615">"beviteli eszköz kalibrációjának módosítása"</string>
-    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lehetővé teszi, hogy alkalmazás módosítsa az érintőképernyő kalibrációs paramétereit. A normál alkalmazásoknál erre elvileg soha nincs szükség."</string>
+    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lehetővé teszi, hogy az alkalmazás módosítsa az érintőképernyő kalibrációs paramétereit. A normál alkalmazásoknál erre elvileg soha nincs szükség."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Jelszavakkal kapcsolatos szabályok beállítása"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"A képernyőzár-feloldási jelszavakban engedélyezett karakterek és hosszúság vezérlése."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Képernyőzár-feloldási kísérletek figyelése"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index c8f0dd5..f330aaa 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1111,7 +1111,7 @@
     <string name="cut" msgid="3092569408438626261">"חתוך"</string>
     <string name="copy" msgid="2681946229533511987">"העתק"</string>
     <string name="paste" msgid="5629880836805036433">"הדבק"</string>
-    <string name="replace" msgid="5781686059063148930">"להחליף..."</string>
+    <string name="replace" msgid="5781686059063148930">"החלף..."</string>
     <string name="delete" msgid="6098684844021697789">"מחק"</string>
     <string name="copyUrl" msgid="2538211579596067402">"העתק כתובת אתר"</string>
     <string name="selectTextMode" msgid="1018691815143165326">"בחר טקסט"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 22fc1c4..4284032 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -690,7 +690,7 @@
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"vykdyti tinklo sąlygų stebėjimą"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Leidžiama programai vykdyti tinklo sąlygų stebėjimą. To niekada neturėtų prireikti naudojant įprastas programas."</string>
     <string name="permlab_setInputCalibration" msgid="4902620118878467615">"keisti įvesties įrenginio kalibravimą"</string>
-    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Leidžiama programai keisti jutiklinio ekrano kalibravimo parametrus. Neturėti prireikti naudojant įprastas programas."</string>
+    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Leidžiama programai keisti jutiklinio ekrano kalibravimo parametrus. Neturėtų prireikti naudojant įprastas programas."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Nustatyti slaptažodžio taisykles"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Valdyti leidžiamą ekrano atrakinimo slaptažodžių ilgį ir leidžiamus naudoti simbolius."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Stebėti bandymus atrakinti ekraną"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f8e9782..4df9785 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -38,7 +38,7 @@
     <string name="mmiFdnError" msgid="5224398216385316471">"Operácia je obmedzená len na režim čísla pevného vytáčania."</string>
     <string name="serviceEnabled" msgid="8147278346414714315">"Služba bola povolená."</string>
     <string name="serviceEnabledFor" msgid="6856228140453471041">"Služba bola povolená pre:"</string>
-    <string name="serviceDisabled" msgid="1937553226592516411">"Služba bola zakázaná."</string>
+    <string name="serviceDisabled" msgid="1937553226592516411">"Služba bola vypnutá."</string>
     <string name="serviceRegistered" msgid="6275019082598102493">"Registrácia prebehla úspešne."</string>
     <string name="serviceErased" msgid="1288584695297200972">"Vymazanie prebehlo úspešne."</string>
     <string name="passwordIncorrect" msgid="7612208839450128715">"Nesprávne heslo."</string>
@@ -528,14 +528,14 @@
     <string name="permdesc_mount_format_filesystems" msgid="8784268246779198627">"Umožňuje aplikácii formátovať vymeniteľný ukladací priestor."</string>
     <string name="permlab_asec_access" msgid="3411338632002193846">"získať informácie o internom ukladacom priestore"</string>
     <string name="permdesc_asec_access" msgid="3094563844593878548">"Umožňuje aplikácii získať informácie o internom ukladacom priestore."</string>
-    <string name="permlab_asec_create" msgid="6414757234789336327">"vytvoriť interný ukladací priestor"</string>
-    <string name="permdesc_asec_create" msgid="4558869273585856876">"Umožňuje aplikácii vytvoriť interný ukladací priestor."</string>
-    <string name="permlab_asec_destroy" msgid="526928328301618022">"zničiť interný ukladací priestor"</string>
-    <string name="permdesc_asec_destroy" msgid="7218749286145526537">"Umožňuje aplikácii zničiť interný ukladací priestor."</string>
-    <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"pripojiť alebo odpojiť interný ukladací priestor"</string>
-    <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"Umožňuje aplikácii pripojiť alebo odpojiť interný ukladací priestor."</string>
-    <string name="permlab_asec_rename" msgid="7496633954080472417">"premenovať interný ukladací priestor"</string>
-    <string name="permdesc_asec_rename" msgid="1794757588472127675">"Umožňuje aplikácii premenovať interný ukladací priestor."</string>
+    <string name="permlab_asec_create" msgid="6414757234789336327">"vytvoriť interné úložisko"</string>
+    <string name="permdesc_asec_create" msgid="4558869273585856876">"Umožňuje aplikácii vytvoriť interné úložisko."</string>
+    <string name="permlab_asec_destroy" msgid="526928328301618022">"zničiť interné úložisko"</string>
+    <string name="permdesc_asec_destroy" msgid="7218749286145526537">"Umožňuje aplikácii zničiť interné úložisko."</string>
+    <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"pripojiť alebo odpojiť interné úložisko"</string>
+    <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"Umožňuje aplikácii pripojiť alebo odpojiť interné úložisko."</string>
+    <string name="permlab_asec_rename" msgid="7496633954080472417">"premenovať interné úložisko"</string>
+    <string name="permdesc_asec_rename" msgid="1794757588472127675">"Umožňuje aplikácii premenovať interné úložisko."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ovládať vibrovanie"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Umožňuje aplikácii ovládať vibrácie."</string>
     <string name="permlab_flashlight" msgid="2155920810121984215">"ovládanie kontrolky"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index e07bd7d..305036d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1667,7 +1667,7 @@
     <item quantity="other" msgid="4730868920742952817">"ลองอีกใน <xliff:g id="COUNT">%d</xliff:g> วินาที"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"ลองอีกครั้งในภายหลัง"</string>
-    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"กวาดนิ้วจากบนลงล่างเพื่อออกจากโหมดเต็มหน้าจอ"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"กวาดนิ้วบนลงล่างเพื่อออกจากโหมดเต็มหน้าจอ"</string>
     <string name="done_label" msgid="2093726099505892398">"เสร็จสิ้น"</string>
     <string name="hour_picker_description" msgid="6698199186859736512">"ตัวเลื่อนหมุนระบุชั่วโมง"</string>
     <string name="minute_picker_description" msgid="8606010966873791190">"ตัวเลื่อนหมุนระบุนาที"</string>
diff --git a/docs/downloads/training/BitmapFun.zip b/docs/downloads/training/BitmapFun.zip
deleted file mode 100644
index d3dd02a..0000000
--- a/docs/downloads/training/BitmapFun.zip
+++ /dev/null
Binary files differ
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 5f0779c..fc60e1f 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -313,6 +313,9 @@
 
 # -------------------- MISC ----------------------
 
+- from: /shareables/training/BitmapFun.zip
+  to: /downloads/samples/DisplayingBitmaps.zip
+
 - from: /shareables/...
   to: http://commondatastorage.googleapis.com/androiddevelopers/shareables/...
 
diff --git a/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html b/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
index 94b5977..6375d9a 100644
--- a/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
+++ b/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
@@ -1030,7 +1030,17 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification. </p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>id</td>
+          <td>the ID of the notification
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1059,7 +1069,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification. </p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>tag</td>
+          <td>the string identifier of the notification.</td>
+        </tr>
+        <tr>
+          <th>id</td>
+          <td>the ID of the notification
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1177,7 +1201,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc. </p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>id</td>
+          <td>the ID of the notification</td>
+        </tr>
+        <tr>
+          <th>notification</td>
+          <td>the notification to post to the system
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1206,7 +1244,25 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc. </p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>tag</td>
+          <td>the string identifier for a notification. Can be <code>null</code>.</td>
+        </tr>
+        <tr>
+          <th>id</td>
+          <td>the ID of the notification. The pair (tag, id) must be unique within your app.</td>
+        </tr>
+        <tr>
+          <th>notification</td>
+          <td>the notification to post to the system
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
index 307fc2a..6fbf8b6 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
@@ -886,12 +886,15 @@
       <table class="jd-tagtable">
         <tr>
           <th>returnKey</td>
-          <td>the extras key to be set with input collected from the user
-         when the intent is sent.
-</td>
+          <td>the intent extras key that refers to the input collected from the user</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -966,10 +969,24 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Specifies whether the user can provide arbitrary values.  The
- default is <code>true</code>.  If this is set to <code>false</code>, a
- non-null non-empty value should be passed to <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])">setChoices(String[])</a></code>.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Specifies whether the user can provide arbitrary values.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>allowFreeFormInput</td>
+          <td>The default is <code>true</code>.
+         If you specify <code>false</code>, you must
+         provide a non-null and non-empty array to <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])">setChoices(String[])</a></code> or
+         an <code><a href="http://developer.android.com/reference/java/lang/IllegalArgumentException.html">IllegalArgumentException</a></code> is thrown.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -998,8 +1015,23 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Specifies choices available to the user to satisfy this input.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Specifies choices available to the user to satisfy this input.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>choices</td>
+          <td>an array of pre-defined choices for users input.
+        You must provide a non-null and non-empty array if
+        you set <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#allowFreeFormInput">allowFreeFormInput</a></code> to <code>false</code>.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1028,8 +1060,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Set a label to be displayed to the user when collecting this input.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Set a label to be displayed to the user when collecting this input.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>label</td>
+          <td>The label to show to users when they input a response.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
index e8aa651..0e1cebe 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
@@ -467,22 +467,27 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">A RemoteInput specifies a response to be collected from the user as part of an intent being
- sent. For example, when used with a notification Action, a response may be collected
- when the user triggers the action, and the results sent as data along with the action's
- PendingIntent. The result value is set in the extras of the triggered Intent with the key
- <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></code>.
+<p itemprop="articleBody">A <code>RemoteInput</code> object collects a response from users and sets the
+ response as an intent extra inside the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> that is sent.
+ Always use <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html">RemoteInput.Builder</a></code> to create instances of this class.
+ <p class="note"> See
+ <a href="/wear/notifications/remote-input.html">Receiving Voice Input from
+ a Notification</a> for more information on how to use this class.
 
- <p>Use the builder class <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html">RemoteInput.Builder</a></code> to create this object.
-
- <p>Example which adds a RemoteInput to an Action:
+ <p>The following example adds a <code>RemoteInput</code> to a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>,
+ sets the intent extra key as <code>quick_reply</code>, and sets the label as <code>Quick Reply</code>.
+ Users are prompted to input a response when they trigger the action. The results are sent as an
+ intent extra with the key of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></code> in the action's
+ <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code>.
 
  <pre class="prettyprint">
+ public static final String EXTRA_QUICK_REPLY_TEXT = "quick_reply";
  WearableNotifications.Action action = new WearableNotifications.Action.Builder(
          R.drawable.reply, &quot;Reply&quot;, actionIntent)
-         .addRemoteInput(new RemoteInput.Builder(EXTRA_QUICK_REPLY_TEXT)
-                 .setLabel("Quick reply").build())
+         <b>.addRemoteInput(new RemoteInput.Builder(EXTRA_QUICK_REPLY_TEXT)
+                 .setLabel("Quick reply").build()</b>)
          .build();</pre>
+
 </p>
 
 
@@ -619,8 +624,8 @@
           final
           boolean</nobr></td>
           <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#allowFreeFormInput">allowFreeFormInput</a></td>
-          <td class="jd-descrcol" width="100%">Indicates whether or not the user may provide an arbitrary value for
- this input.</td>
+          <td class="jd-descrcol" width="100%">Indicates whether or not users can provide an arbitrary value for
+ input.</td>
       </tr>
       
     
@@ -631,7 +636,7 @@
           final
           <a href="http://developer.android.com/reference/java/lang/String.html">String[]</a></nobr></td>
           <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></td>
-          <td class="jd-descrcol" width="100%">The choices available to the user.</td>
+          <td class="jd-descrcol" width="100%">Possible input choices.</td>
       </tr>
       
     
@@ -642,7 +647,7 @@
           final
           <a href="http://developer.android.com/reference/java/lang/String.html">String</a></nobr></td>
           <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#label">label</a></td>
-          <td class="jd-descrcol" width="100%">The label to be displayed to the user when collecting this input.</td>
+          <td class="jd-descrcol" width="100%">The label to display to users when collecting this input.</td>
       </tr>
       
     
@@ -653,8 +658,7 @@
           final
           <a href="http://developer.android.com/reference/java/lang/String.html">String</a></nobr></td>
           <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></td>
-          <td class="jd-descrcol" width="100%">The extras key to be populated with input from the user when the
- intent is sent.</td>
+          <td class="jd-descrcol" width="100%">The lookup key for the intent extra that the response is set in.</td>
       </tr>
       
     
@@ -1062,10 +1066,10 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Indicates whether or not the user may provide an arbitrary value for
- this input.  If set to false, then the user should select one of the
- provided choices.  It is an error to set this to <code>false</code> and
- not provide <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code>.
+  <div class="jd-tagdata jd-tagdescr"><p>Indicates whether or not users can provide an arbitrary value for
+ input. If you set this to <code>false</code>, users must select one of the
+ provided <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code>. An <code><a href="http://developer.android.com/reference/java/lang/IllegalArgumentException.html">IllegalArgumentException</a></code> is thrown
+ if you set this to false and <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code> is <code>null</code> or empty.
 </p></div>
 
     
@@ -1094,8 +1098,8 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>The choices available to the user.  May be null if there are no choices
- to present to the user.
+  <div class="jd-tagdata jd-tagdescr"><p>Possible input choices. This can be <code>null</code>
+ if there are no choices to present.
 </p></div>
 
     
@@ -1124,7 +1128,7 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>The label to be displayed to the user when collecting this input.
+  <div class="jd-tagdata jd-tagdescr"><p>The label to display to users when collecting this input.
 </p></div>
 
     
@@ -1153,8 +1157,8 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>The extras key to be populated with input from the user when the
- intent is sent.
+  <div class="jd-tagdata jd-tagdescr"><p>The lookup key for the intent extra that the response is set in. This is populated
+ when the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> is sent.
 </p></div>
 
     
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
index 884de4a..f27d406 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
@@ -448,15 +448,6 @@
 
 <h2>Class Overview</h2>
 <p itemprop="articleBody">Builder class for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> objects.
-
- <p>Example:
-
- <pre class="prettyprint">
- WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
-         .addAction(new WearableNotifications.Action.Builder(
-                 R.drawable.navigate, &quot;Navigate&quot, pendingIntent)
-                 .build());
- Notification notif = builder.build();</pre>
 </p>
 
 
@@ -525,7 +516,7 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html#WearableNotifications.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent)">WearableNotifications.Action.Builder</a></span>(int icon, <a href="http://developer.android.com/reference/java/lang/CharSequence.html">CharSequence</a> title, <a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a> intent)</nobr>
         
-        <div class="jd-descrdiv">Construct a new builder for an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</div>
+        <div class="jd-descrdiv">Construct a new builder for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</div>
   
   </td></tr>
 
@@ -872,8 +863,25 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Construct a new builder for an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Construct a new builder for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>icon</td>
+          <td>icon to show for this action</td>
+        </tr>
+        <tr>
+          <th>title</td>
+          <td>the title of the action</td>
+        </tr>
+        <tr>
+          <th>intent</td>
+          <td>the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> to fire when users trigger this action
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -918,9 +926,22 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Add an input to be collected from the user when this action is sent.
- Response values are sent as extras to this Action's pending intent when
- sent.
-</p></div>
+ Response values are sent as extras to this action's pending intent when
+ sent.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>remoteInput</td>
+          <td>a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> to add to the action</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -950,8 +971,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Combine all of the options that have been set and return a new <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>
- object.
-</p></div>
+ object.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the built action
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
index e073881..ff9c904 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
@@ -474,14 +474,11 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> which adds support for additional
- wearable extensions.
-
- <p>To create a new Action, use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html">WearableNotifications.Action.Builder</a></code> class and then call
+<p itemprop="articleBody">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> that adds additional
+ wearable extensions for actions.
+ <p>Always use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html">WearableNotifications.Action.Builder</a></code> to build instances of this class and call
  <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#addAction(android.preview.support.wearable.notifications.WearableNotifications.Action)">addAction(WearableNotifications.Action)</a></code> to add the action to a notification.
 
- <p>Example:
-
  <pre class="prettyprint">
  WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
          .addAction(new WearableNotifications.Action.Builder(
@@ -991,8 +988,12 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Get a list of inputs to be collected from the user when this action is sent.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Get a list of inputs to be collected from the user when this action is sent.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the array of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with this action
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
index 25e4520..d6ec260 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
@@ -447,29 +447,11 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">Builder object that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to provide
- methods for adding wearable extensions to a notification.
+<p itemprop="articleBody">Builder class that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to add
+ wearable extensions for a notification.
 
- <p>Methods on the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> and this object
- can be called in any order, but the final Notification must be built with
- the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> method of this class.
-
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
-
- <pre class="prettyprint">
- NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
-         .setContentTitle(&quot;New mail from &quot; + sender.toString())
-         .setContentText(subject)
-         .setSmallIcon(R.drawable.new_mail);
- Notification notif = new WearableNotifications.Builder(builder)
-         .setLocalOnly(true)
-         .setMinPriority()
-         .build();
- NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
+ <p>You can chain the "set" methods for this builder in any order,
+ but you must call the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> method last to build the final notification.
 </p>
 
 
@@ -662,7 +644,7 @@
         
         <div class="jd-descrdiv">Combine all of the options that have been set by both this builder and
  the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> object and return a new
- <code><a href="/http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</div>
+ <code><a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</div>
   
   </td></tr>
 
@@ -826,7 +808,7 @@
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setMinPriority()">setMinPriority</a></span>()</nobr>
         
         <div class="jd-descrdiv">Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
   
   </td></tr>
 
@@ -1105,14 +1087,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Construct a builder to be used for adding wearable extensions to notifications. Both the
- wrapped builder (accessible via <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#getCompatBuilder()">getCompatBuilder()</a></code> and this builder can be used
+ wrapped builder (accessible via <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#getCompatBuilder()">getCompatBuilder()</a></code>) and this builder can be used
  simultaneously, but the build() method from this object must be called in the end.
 
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
+ <p>Always post notifications to the notification
+ system with the <code>NotificationManagerCompat.notify(...)</code> methods
+ instead of the <code>NotificationManager.notify(...)</code> methods.
 
  <pre class="prettyprint">
  WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
@@ -1156,12 +1136,9 @@
  a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>. Both the wrapped builder and this
  builder can be used simultaneously, but the build() method from this object must be
  called in the end.
-
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
+ <p>Always post notifications to the notification
+ system with the <code>NotificationManagerCompat.notify(...)</code> methods
+ instead of the <code>NotificationManager.notify(...)</code> methods.
 
  <pre class="prettyprint">
  NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
@@ -1221,6 +1198,19 @@
  accepts <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> extension wrappers. Actions added by this function
  are appended when <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> is called.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>action</td>
+          <td>the action to add to this notification</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code></li>
       </ul>
@@ -1258,6 +1248,19 @@
  subsequent pages. This field can be used to separate a notification into multiple
  sections.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>page</td>
+          <td>the notification to add as another page</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getPages(android.app.Notification)">getPages(Notification)</a></code></li>
       </ul>
@@ -1295,6 +1298,19 @@
  subsequent pages. This field can be used to separate a notification into multiple
  sections.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>pages</td>
+          <td>a collection of notifications</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getPages(android.app.Notification)">getPages(Notification)</a></code></li>
       </ul>
@@ -1328,8 +1344,21 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Adds a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> for the content intent.  The collected
- data will be overlayed onto the content intent.
-</p></div>
+ data will be overlayed onto the content intent.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>input</td>
+          <td>a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> object to obtain a user response</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1360,8 +1389,12 @@
       
   <div class="jd-tagdata jd-tagdescr"><p>Combine all of the options that have been set by both this builder and
  the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> object and return a new
- <code><a href="/http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.
-</p></div>
+ <code><a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the notification
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1390,8 +1423,12 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Return the <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> being wrapped by this object.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Return the <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> being wrapped by this object.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1423,8 +1460,12 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the current metadata Bundle used by this Builder, creating a new one
  as necessary.
 
- <p>The returned Bundle is shared with this Builder.
-</p></div>
+ <p>The returned Bundle is shared with this Builder.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the extras bundle
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1460,15 +1501,19 @@
       <table class="jd-tagtable">
         <tr>
           <th>icon</td>
-          <td>Icon to display for the content action.</td>
+          <td>icon to display for the content action.</td>
         </tr>
         <tr>
           <th>subtext</td>
-          <td>Optional subtext to display with the big action icon.
-</td>
+          <td>Optional subtext to display with the big action icon.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1504,11 +1549,15 @@
       <table class="jd-tagtable">
         <tr>
           <th>icon</td>
-          <td>Icon to display for the content action.
-</td>
+          <td>icon to display for the content action.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1551,11 +1600,15 @@
           <th>groupOrder</td>
           <td>The 0-indexed sort order within the group. Can also be set
          to the sentinel value <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> to mark this
-         notification as being the group summary.
-</td>
+         notification as being the group summary.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1592,11 +1645,15 @@
       <table class="jd-tagtable">
         <tr>
           <th>groupKey</td>
-          <td>The group key of the group. Unique within a package.
-</td>
+          <td>The group key of the group. Unique within a package.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1625,8 +1682,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>hintHideIcon</td>
+          <td><code>true</code> to hide the icon, <code>false</code> otherwise.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1660,6 +1730,20 @@
  <p>Some notifications can be bridged to other devices for remote display.
  This hint can be set to recommend this notification not be bridged.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>localOnly</td>
+          <td><code>true</code> to keep the notification on this device,
+ <code>false</code> otherwise. Default value is <code>false</code>.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getLocalOnly(android.app.Notification)">getLocalOnly(Notification)</a></code></li>
       </ul>
@@ -1693,10 +1777,14 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
  minimum priority notifications will bypass the notification manager on platforms
- that do not support ambient level notifications.
-</p></div>
+ that do not support ambient level notifications.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
index 45b77c6..e03e16e 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
@@ -450,15 +450,39 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">Helper providing extensions to android notifications for use with wearable devices.
+<p itemprop="articleBody">Helper class that contains wearable extensions for notifications.
+ Always use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code> to create instances of this class.
+ <p class="note"> See
+ <a href="/wear/notifications/creating.html">Creating Notifications
+ for Android Wear</a> for more information on how to use this class.
+ <p>
+ To create a notification with wearable extensions:
+ <ol>
+   <li>Create a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>, setting any desired
+   properties.
+   <li>Create a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code>, passing in the
+   <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> as a starting point.
+   <li>Set wearable-specific properties using the
+   <code>add</code> and <code>set</code> methods of <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code>.
+   <li>Call <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> to create the notification.
+   <li>Post the notification to the notification
+   system with the <code>NotificationManagerCompat.notify(...)</code> methods
+   and not the <code>NotificationManager.notify(...)</code> methods.
+ </ol>
 
- <p>To build notifications with wearable extensions, use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code> class.
- Notifications created using Builder should be posted to the notification system
- using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Once a notification is built, a variety of methods are provide in this utility to access
- the value of various notification fields in a backwards compatible manner.
+ <pre class="prettyprint">
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
+         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+         .setContentText(subject)
+         .setSmallIcon(R.drawable.new_mail);
+ Notification notif = new WearableNotifications.Builder(builder)
+         .setLocalOnly(true)
+         .setMinPriority()
+         .build();
+ NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
+ <p>When you receive a notification object from the builder, the methods in
+ this class let you access the values of various notification fields in
+ a backward-compatible manner.
 </p>
 
 
@@ -502,8 +526,8 @@
         
         class</nobr></td>
       <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></td>
-      <td class="jd-descrcol" width="100%">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> which adds support for additional
- wearable extensions.&nbsp;</td>
+      <td class="jd-descrcol" width="100%">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> that adds additional
+ wearable extensions for actions.&nbsp;</td>
     </tr>
     
     
@@ -515,8 +539,8 @@
         
         class</nobr></td>
       <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></td>
-      <td class="jd-descrcol" width="100%">Builder object that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to provide
- methods for adding wearable extensions to a notification.&nbsp;</td>
+      <td class="jd-descrcol" width="100%">Builder class that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to add
+ wearable extensions for a notification.&nbsp;</td>
     </tr>
     
     
@@ -548,8 +572,9 @@
     <tr class=" api apilevel-" >
         <td class="jd-typecol">int</td>
         <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></td>
-        <td class="jd-descrcol" width="100%">Sentinel value provided to the groupOrder parameter <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String)">setGroup(Notification, String)</a></code> to indicate that
- this member of a notification group is the summary of the group.</td>
+        <td class="jd-descrcol" width="100%">Sentinel value provided to the <code>groupOrder</code> parameter of the
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String, int)">setGroup(android.app.Notification, java.lang.String, int)</a></code>
+ method.</td>
     </tr>
     
     
@@ -589,8 +614,8 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getAction(android.app.Notification, int)">getAction</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif, int actionIndex)</nobr>
         
-        <div class="jd-descrdiv">Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at index <code>actionIndex</code>
- in the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</div>
+        <div class="jd-descrdiv">Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at <code>actionIndex</code>
+ in the <code><a href="http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</div>
   
   </td></tr>
 
@@ -662,7 +687,7 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getContentIntentRemoteInputs(android.app.Notification)">getContentIntentRemoteInputs</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
         
-        <div class="jd-descrdiv">Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s associated with the content intent.</div>
+        <div class="jd-descrdiv">Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with the content intent.</div>
   
   </td></tr>
 
@@ -680,8 +705,8 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getExtras(android.app.Notification)">getExtras</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
         
-        <div class="jd-descrdiv">Gets the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backwards
- compatible manner.</div>
+        <div class="jd-descrdiv">Gets the <code><a href="http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backward-compatible
+ manner.</div>
   
   </td></tr>
 
@@ -919,7 +944,7 @@
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setMinPriority(android.app.Notification)">setMinPriority</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
         
         <div class="jd-descrdiv">Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
   
   </td></tr>
 
@@ -1246,8 +1271,10 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Sentinel value provided to the groupOrder parameter <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String)">setGroup(Notification, String)</a></code> to indicate that
- this member of a notification group is the summary of the group.
+  <div class="jd-tagdata jd-tagdescr"><p>Sentinel value provided to the <code>groupOrder</code> parameter of the
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String, int)">setGroup(android.app.Notification, java.lang.String, int)</a></code>
+ method. This value indicates that this index of the
+ notification group is the summary of the group.
 </p></div>
 
     
@@ -1309,9 +1336,22 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at index <code>actionIndex</code>
- in the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at <code>actionIndex</code>
+ in the <code><a href="http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+        <tr>
+          <th>actionIndex</td>
+          <td>the index of the desired action
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1342,6 +1382,20 @@
       
   <div class="jd-tagdata jd-tagdescr"><p>Get the number of actions present on this notification.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the number of actions for this notification
+</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code></li>
       </ul>
@@ -1377,6 +1431,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the big action icon to be displayed with this notification. Big actions show
  a hint to users about the action taken when the content intent is triggered.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the icon or <code>0</code> if it wasn't set</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setBigActionIcon(android.app.Notification, int)">setBigActionIcon(Notification, int)</a></code></li>
       </ul>
@@ -1411,6 +1478,19 @@
       
   <div class="jd-tagdata jd-tagdescr"><p>Get the big action icon subtext to be shown with a big action icon.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the subtext of the big action icon or <code>null</code> if it wasn't exist.</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setBigActionIcon(android.app.Notification, int)">setBigActionIcon(Notification, int)</a></code></li>
       </ul>
@@ -1443,8 +1523,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s associated with the content intent.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with the content intent.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>array of RemoteInput objects or <code>null</code> if it doesn't exist
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1473,10 +1566,23 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backwards
- compatible manner. Extras field was supported from JellyBean (Api level 16)
- forwards. This function will return null on older api levels.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backward-compatible
+ manner. Extras field was supported from JellyBean (API level 16)
+ forwards. This function will return null on older API levels.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the extras associated with this notification.
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1506,8 +1612,17 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Get the key used to group this notification into a cluster or stack
- with other notifications. This key is unique within a package.
-</p></div>
+ with other notifications. This key is unique within a package.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1539,8 +1654,21 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the sort order of this notification within a group of notifications
  with the same group key set. Group orders are 0-indexed integers that are used
  to sort notifications in ascending order. Can also be the sentinel value
- <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> if this is the summary notification for a group.
-</p></div>
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> if this is the summary notification for a group.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the sort order of this notification within this group
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1569,8 +1697,22 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Get a hint that this notification's icon should not be displayed.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Get a hint that this notification's icon should not be displayed.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li><code>true</code> if this icon should not be displayed, false otherwise.
+ The default value is <code>false</code> if this was never set.
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1602,8 +1744,22 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get whether or not this notification is only relevant to the current device.
 
  <p>Some notifications can be bridged to other devices for remote display.
- If this hint is set, it is recommended that this notification not be bridged.
-</p></div>
+ If this hint is set, it is recommended that this notification not be bridged.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li><code>true</code> if this notification is local only, <code>false</code> otherwise.
+        Default value is <code>false</code> if not set.
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1635,8 +1791,21 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the array of additional pages of content for displaying this notification. The
  current notification forms the first page, and elements within this array form
  subsequent pages. This field can be used to separate a notification into multiple
- sections.
-</p></div>
+ sections.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the pages for this notification
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1671,8 +1840,12 @@
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
         <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
           <th>icon</td>
-          <td>Icon to display for the content action.
+          <td>icon to display for the content action.
 </td>
         </tr>
       </table>
@@ -1711,8 +1884,12 @@
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
         <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
           <th>icon</td>
-          <td>Icon to display for the content action.</td>
+          <td>icon to display for the content action.</td>
         </tr>
         <tr>
           <th>subtext</td>
@@ -1751,8 +1928,21 @@
       
   <div class="jd-tagdata jd-tagdescr"><p>Sets <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s to be collected when the user triggers the
  <code>contentIntent</code>.  These function just as if they were attached to
- an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>.
-</p></div>
+ an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
+          <th>inputs</td>
+          <td>array of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects desired from the user.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1788,6 +1978,10 @@
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
         <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
           <th>groupKey</td>
           <td>The group key of the group. Unique within a package.
 </td>
@@ -1829,6 +2023,10 @@
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
         <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
           <th>groupKey</td>
           <td>The group key of the group. Unique within a package.</td>
         </tr>
@@ -1869,8 +2067,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
+          <th>hintHideIcon</td>
+          <td><code>true</code> to hide this icon, <code>false</code> otherwise.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1902,8 +2113,22 @@
   <div class="jd-tagdata jd-tagdescr"><p>Set whether or not this notification is only relevant to the current device.
 
  <p>Some notifications can be bridged to other devices for remote display.
- This hint can be set to recommend this notification not be bridged.
-</p></div>
+ This hint can be set to recommend this notification not be bridged.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
+          <th>localOnly</td>
+          <td>set to <code>true</code> to keep the notification on this device only,
+        <code>false</code> otherwise.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1933,10 +2158,19 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
  minimum priority notifications will bypass the notification manager on platforms
- that do not support ambient level notifications.
-</p></div>
+ that do not support ambient level notifications.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1968,8 +2202,22 @@
   <div class="jd-tagdata jd-tagdescr"><p>Set additional pages of content to display with this notification. The current
  notification forms the first page, and pages set using this function form
  subsequent pages. This field can be used to separate a notification into multiple
- sections.
-</p></div>
+ sections.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
+          <th>pages</td>
+          <td>the pages to add to the current notification. Replaces any
+ existing pages with this value.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
index ff9c3a0..7b9216e 100644
--- a/docs/html/training/displaying-bitmaps/cache-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -24,8 +24,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/display-bitmap.jd b/docs/html/training/displaying-bitmaps/display-bitmap.jd
index ed1836c..4147f83 100644
--- a/docs/html/training/displaying-bitmaps/display-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/display-bitmap.jd
@@ -24,8 +24,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/index.jd b/docs/html/training/displaying-bitmaps/index.jd
index 7003585..831c64d 100644
--- a/docs/html/training/displaying-bitmaps/index.jd
+++ b/docs/html/training/displaying-bitmaps/index.jd
@@ -18,8 +18,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/load-bitmap.jd b/docs/html/training/displaying-bitmaps/load-bitmap.jd
index 938901f..f963baa 100644
--- a/docs/html/training/displaying-bitmaps/load-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/load-bitmap.jd
@@ -18,8 +18,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/manage-memory.jd b/docs/html/training/displaying-bitmaps/manage-memory.jd
index 7f2b4c5..5a5c2cd 100644
--- a/docs/html/training/displaying-bitmaps/manage-memory.jd
+++ b/docs/html/training/displaying-bitmaps/manage-memory.jd
@@ -26,8 +26,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/process-bitmap.jd b/docs/html/training/displaying-bitmaps/process-bitmap.jd
index ed0b368..a557619 100644
--- a/docs/html/training/displaying-bitmaps/process-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/process-bitmap.jd
@@ -26,8 +26,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/wear/css/wear.css b/docs/html/wear/css/wear.css
index 9d9d7a7..40afeaa 100644
--- a/docs/html/wear/css/wear.css
+++ b/docs/html/wear/css/wear.css
@@ -2,6 +2,7 @@
  * UTILITIES
  */
 
+
 .border-box {
   box-sizing: border-box;
 }
@@ -92,6 +93,10 @@
  * LAYOUT
  */
 
+#body-content,
+.fullpage,
+#jd-content,
+.jd-descr,
 .wear-body-content {
   height: 100%;
 }
@@ -134,13 +139,11 @@
 }
 
 .wear-hero-container {
-  height: 800px;
-  height: 100vh;
+  height: 100%;
 }
 
 .wear-hero {
-  height: 100%;
-  height: calc(100% - 72px);
+  height: calc(100% - 70px);
   min-height: 504px;
   margin-top: -4px;
   padding-top: 0;
@@ -184,6 +187,7 @@
 }
 
 .wear-button {
+  white-space: nowrap;
   display: inline-block;
   padding: 16px 32px;
   font-size: 18px;
@@ -233,6 +237,7 @@
 }
 
 .wear-video-link {
+  white-space: nowrap;
   display: inline-block;
   padding: 16px 32px 16px 82px;
   font-size: 18px;
@@ -436,7 +441,7 @@
 #icon-video-close {
 background-image: url("../images/close.png");
 background-position: 0 0;
-height: 48px;
-width: 48px;
+height: 36px;
+width: 36px;
 display:block;
-}
\ No newline at end of file
+}
diff --git a/docs/html/wear/design/index.html b/docs/html/wear/design/index.html
index 4bbbf29..9952490 100644
--- a/docs/html/wear/design/index.html
+++ b/docs/html/wear/design/index.html
@@ -379,11 +379,15 @@
 }
 </style>
 <p>
-Android Wear devices provide just the right information at just the right time, allowing you to be connected to the virtual world and present in the real world.</p>
+Android wearables provide just the right information at just the right time, allowing you to be connected to the virtual world and present in the real world.</p>
 
 <img src="/wear/images/05_images.png" height="200" width="169" style="float:right;clear:right;margin:0 0 60px 60px" />
 
-<p>Here you’ll find some guidelines for designing great user experiences on the Android Wear platform. Designing for Android Wear is substantially different than designing for phones or tablets, so we’ll start by describing how your content can work in tandem with the overall Android Wear vision.</p>
+<p>Here you’ll find some guidelines for designing great user experiences on the Android Wear
+platform. Designing for Android Wear is substantially different than designing for phones or
+tablets, so we’ll start by describing how your content can work in tandem with the overall
+Android Wear vision. To better understand the user experience on Android Wear, also be sure
+to read the <a href="/wear/design/user-interface.html">UI Overview</a>.</p>
 
 
 <img src="/wear/images/02_notifications.png" height="200" width="169" style="float:right;clear:right;margin:0 0 20px 60px" />
@@ -393,7 +397,7 @@
 <p>Android Wear experiences are:</p>
 
 <ul>
-  <li><strong>Contextually aware and smart.</strong> These devices bring a new level of awareness to computing. Rather than requiring attention and input from users, Android Wear devices are aware of their situation and state, and helpfully display the right information at the right time. <em>Timely, relevant, specific</em>.</li>
+  <li><strong>Contextually aware and smart.</strong> These devices bring a new level of awareness to computing. Rather than requiring attention and input from users, Android wearables are aware of their situation and state, and helpfully display the right information at the right time. <em>Timely, relevant, specific</em>.</li>
 
   <li><strong>Glanceable.</strong> Wearable devices are used all throughout the day, even when they sit in our peripheral vision. Effective apps provide the maximum payload of information with a minimum of fuss, optimized to provide tiny snippets of relevant information throughout the day. <em>Short, sharp, immediate.</em></li>
 
@@ -462,7 +466,8 @@
 
 <img src="/wear/images/07_appicons.png" height="200" style="float:right;margin:0 0 20px 60px" />
 
-<p>Your application’s launcher icon will be automatically placed on the card, identifying your notification. Do not use the notification title or background image to identify or brand your application. Instead, allow your icon to identify itself and focus on delivering a clear, succinct message in the card and image. You can choose not to display this icon.
+<p>Your application’s launcher icon will be automatically placed on the card, identifying your notification. Do not use the notification title or background image to identify or brand your application. Instead, allow your icon to identify itself and focus on delivering a clear, succinct message in the card and image. You can choose not to display this icon using
+ <a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setHintHideIcon(boolean)"><code>setHintHideIcon()</code></a>.
 </p>
 
 
diff --git a/docs/html/wear/design/user-interface.html b/docs/html/wear/design/user-interface.html
index f87b9da..c23d79c 100644
--- a/docs/html/wear/design/user-interface.html
+++ b/docs/html/wear/design/user-interface.html
@@ -404,9 +404,9 @@
 <p>Cards in the stream are more than simple notifications. They can be swiped horizontally to
 reveal additional pages. Further horizontal swiping may reveal tappable buttons, allowing the user
 to take action on the notification. Cards can also be dismissed by swiping left to right, removing
-them from the stream until the next time they have useful information to display. In the emulator,
-hovering the mouse over the screen illuminates a blue bar at the top of the device
-that takes you home when clicked.</p>
+them from the stream until the next time they have useful information to display.
+In the emulator, hovering the mouse over the top of the screen illuminates a blue bar at
+the top of the device that takes you home when clicked.</p>
 
 
 
diff --git a/docs/html/wear/images/close.png b/docs/html/wear/images/close.png
index bd473d2..7e45fb7 100644
--- a/docs/html/wear/images/close.png
+++ b/docs/html/wear/images/close.png
Binary files differ
diff --git a/docs/html/wear/index.html b/docs/html/wear/index.html
index 5ea793b..712e1af 100644
--- a/docs/html/wear/index.html
+++ b/docs/html/wear/index.html
@@ -280,7 +280,17 @@
 
 
     <div class="jd-descr" itemprop="articleBody">
-    <div id="video-container">
+    <style>
+.fullpage>#footer,
+#jd-content>.content-footer.wrap {
+  display:none;
+}
+</style>
+
+
+
+
+<div id="video-container">
   <div id="video-frame">
     <div class="video-close">
       <span id="icon-video-close">&nbsp;</span>
@@ -294,6 +304,7 @@
 </div>
 
 
+
 <div class="wear-body-content">
   <div class="wear-hero-container">
     <div class="wear-section wear-hero">
@@ -319,7 +330,7 @@
                 <a href="/wear/preview/start.html" class="wear-button wear-primary" style="margin-top: 40px;">
                   Get the Developer Preview
                 </a>
-                <a id="watchVideo" href="https://youtube.googleapis.com/v/i2uvYI6blEE">
+                <a id="watchVideo" href="https://youtube.googleapis.com/v/0xQ3y902DEQ">
                   <div class="wear-video-link">Watch the video</div>
                 </a>
 <script>
@@ -328,7 +339,7 @@
 
   var params = { allowScriptAccess: "always"};
   var atts = { id: "ytapiplayer" };
-  swfobject.embedSWF("//www.youtube.com/v/i2uvYI6blEE?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1;autoplay=1",
+  swfobject.embedSWF("//www.youtube.com/v/0xQ3y902DEQ?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1;autoplay=1",
     "ytapiplayer", "940", "526.4", "8", null, null, params, atts);
 
   e.preventDefault();
@@ -491,7 +502,7 @@
                 <img src="/wear/images/features/ts2.png" alt="">
                 <p>Send Data</p>
                 <p class="wear-small">
-                  Send data and actions between a phone and a wearable with a data replication APIs and RPCs.
+                  Send data and actions between a phone and a wearable with data replication APIs and RPCs.
                 </p>
               </div>
               <div class="col-4">
@@ -505,7 +516,7 @@
                 <img src="/wear/images/features/ts4.png" alt="">
                 <p>Voice Actions</p>
                 <p class="wear-small">
-                  Register your app to handle voice actions, like "Ok Google, take a note.""
+                  Register your app to handle voice actions, like "Ok Google, take a note."
                 </p>
               </div>
             </div>
@@ -590,30 +601,30 @@
           <div class="cols">
             <div class="wear-body">
               <div class="col-3-wide">
-                  <a href="/TODO">
+                  <a target="_blank" href="https://www.youtube.com/playlist?list=PLWz5rJ2EKKc-kIrPiq098QH9dOle-fLef">
                     <img class="wear-social-image" src="//www.google.com/images/icons/product/youtube-128.png" alt="">
                   </a>
                 <div class="wear-social-copy">
                   <p>DevBytes</p>
                   <p class="wear-small">
-                    Learn how to optimize your app notifications for wearable devices in this <a href="/TODO">DevBytes video</a> using the Android Wear Developer Preview.
+                    Learn how to optimize your app notifications for wearable devices in this <a target="_blank" href="https://www.youtube.com/playlist?list=PLWz5rJ2EKKc-kIrPiq098QH9dOle-fLef">DevBytes video</a> using the Android Wear Developer Preview.
                   </p>
                 </div>
               </div>
               <div class="col-3-wide">
-                <a href="http://android-developers.blogspot.com/">
+                <a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-wear-developer-preview.html">
                   <img class="wear-social-image" src="/wear/images/blogger.png" alt="">
                 </a>
                 <div class="wear-social-copy">
                   <p>Blog Post</p>
                   <p class="wear-small">
                     Read more about the Android Wear Developer Preview announcement
-                    at the <a href="http://android-developers.blogspot.com/">Android Developers Blog</a>.
+                    at the <a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-wear-developer-preview.html">Android Developers Blog</a>.
                   </p>
                 </div>
               </div>
               <div class="col-3-wide">
-                <a href="http://g.co/androidweardev">
+                <a target="_blank" href="http://g.co/androidweardev">
                   <img class="wear-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="+Android Wear Developers">
                 </a>
                 <div class="wear-social-copy">
@@ -622,7 +633,7 @@
                     Follow us on Google+ to stay up-to-date on Android Wear development and join the discussion!
                   </p>
                   <p class="wear-small">
-                    <a href="http://g.co/androidweardev">+Android Wear Developers</a>
+                    <a target="_blank" href="http://g.co/androidweardev">+Android Wear Developers</a>
                   </p>
                 </div>
               </div>
@@ -631,6 +642,25 @@
         </div> <!-- end .wrap -->
       </div> <!-- end .wear-section -->
     </div> <!-- end .wear-rest-of-page -->
+
+
+    <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
+      <div class="layout-content-col col-16" style="padding-top:4px">
+        <style>#___plusone_0 {float:right !important;}</style>
+        <div class="g-plusone" data-size="medium"></div>
+      </div>
+    </div>
+    <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px">
+      <div id="copyright">
+        Except as noted, this content is
+        licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+        Creative Commons Attribution 2.5</a>. For details and
+        restrictions, see the <a href="/license.html">Content
+        License</a>.
+      </div>
+    </div>
+
+
   </div> <!-- end wear-body-content -->
 
   <script>
@@ -641,6 +671,8 @@
     e.preventDefault();
   });
   </script>
+
+
     </div>
 
       <div class="content-footer wrap"
diff --git a/docs/html/wear/notifications/pages.html b/docs/html/wear/notifications/pages.html
index abff8fa..ce568eb 100644
--- a/docs/html/wear/notifications/pages.html
+++ b/docs/html/wear/notifications/pages.html
@@ -377,7 +377,7 @@
 <img src="/wear/images/08_pages.png" height="200" style="float:right;margin:0 0 20px 40px" />
 
 <p>When you'd like to provide more information without requiring users
-to open your app on their phones, you can
+to open your app on their handheld device, you can
 add one or more pages to the notification on Android Wear. The additional pages
 appear immediately to the right of the main notification card.
 For information about when to use and how to design
diff --git a/docs/html/wear/notifications/remote-input.html b/docs/html/wear/notifications/remote-input.html
index 6500233..c8f6621f9 100644
--- a/docs/html/wear/notifications/remote-input.html
+++ b/docs/html/wear/notifications/remote-input.html
@@ -373,20 +373,24 @@
 
 
     <div class="jd-descr" itemprop="articleBody">
-    <img src="/wear/images/13_voicereply.png" height="200" width="169" style="float:right;margin:0 0 20px 60px" />
+    <img src="/wear/images/13_voicereply.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
 
 <img src="/wear/images/03_actions.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
 
 <p>If your notification includes an action to respond with text,
     such as to reply to an email, it should normally launch an activity
-    on the handheld device. However, when your notification appears on an Android Wear device, you can
+    on the handheld device. However, when your notification appears on an Android wearable, you can
     allow users to dictate a reply with voice input. You can also provide pre-defined text
-    replies for the user to select.</p>
+    messages for the user to select.</p>
 
 <p>When the user replies with voice or selects one of the available
-responses, the system delivers your app on the handheld the
-message as a string extra in the <code><a href="/reference/android/content/Intent.html">Intent</a></code> you specified
-to be used for the action.</p>
+messages, the system sends the message to your app on the connected handheld device.
+The message is attached as an extra in the <code><a href="/reference/android/content/Intent.html">Intent</a></code> you specified
+to be used for the notification action.</p>
+
+<p class="note"><strong>Note:</strong> When developing with the Android emulator,
+you must type text replies into the voice input field, so be sure you have enabled
+<strong>Hardware keyboard present</strong> in the AVD settings.</p>
 
 
 <h2 id="RemoteInput">Define the Remote Input</h2>
@@ -397,10 +401,10 @@
   <a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> APIs.
     The
   <a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> constructor takes a string that the system
-    will use as a key for the <code><a href="/reference/android/content/Intent.html">Intent</a></code> extra that caries the reply message
+    will use as a key for the <code><a href="/reference/android/content/Intent.html">Intent</a></code> extra that carries the reply message
     to your app on the handheld.</p>
 
-<p>For example, here's a new
+<p>For example, here's how to create a new
   <a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">
 <code>RemoteInput</code></a> object that provides a custom
     label for the voice input prompt:</p>
@@ -422,7 +426,7 @@
 <img src="/wear/images/12_voicereply.png" height="200" style="float:right;margin:0 0 20px 40px" />
 
 <p>In addition to allowing voice input, you can
-    provide up to five text responses the user can select for quick replies. Call
+    provide up to five text responses that the user can select for quick replies. Call
   <a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])"><code>setChoices()</code></a> and pass it a string array.</p>
 
 <p>For example, you may define some responses in a resource array:</p>
@@ -465,9 +469,9 @@
 
 <pre>
 // Create intent for reply action
-Intent replyIntent = new Intent(this, ReplyService.class);
+Intent replyIntent = new Intent(this, ReplyActivity.class);
 PendingIntent replyPendingIntent =
-        PendingIntent.getService(this, 0, replyIntent, 0);
+        PendingIntent.getActivity(this, 0, replyIntent, 0);
 
 // Build the notification
 NotificationCompat.Builder replyNotificationBuilder =
@@ -563,8 +567,8 @@
         .build();
 </pre>
 
-<p>Now, when the user selects "Reply" from an Android wearable,
-    the system prompts for voice input (and provides the list of pre-defined replies, if provided).
+<p>Now, when the user selects "Reply" from an Android wearable, the system prompts the user
+    for voice input (and shows the list of pre-defined replies, if provided).
     Once the user completes a response, the system invokes
     the <code><a href="/reference/android/content/Intent.html">Intent</a></code> attached to the action and adds the
 <code>EXTRA_VOICE_REPLY</code> extra (the string
diff --git a/docs/html/wear/notifications/stacks.html b/docs/html/wear/notifications/stacks.html
index 5d10165..e4f74a0 100644
--- a/docs/html/wear/notifications/stacks.html
+++ b/docs/html/wear/notifications/stacks.html
@@ -376,15 +376,20 @@
     <img src="/wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
 <img src="/wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
 
-<p>When your app creates more than one notification about the same type, you should traditionally
-update the existing notification with a summary of all the notifications instead of creating multiple notifications. For instance, instead
-of three notifications for each received email, you should create one with a summary such as "3 new
-messages." To view the contents of each message, the user must then touch the notification to open
-your app.</p>
+<p>When creating notifications for a handheld device, you should always aggregate similar
+notifications into a single summary notification. For example, if your app creates notifications
+for received messages, you should not show more than one notification
+on a handheld device&mdash;when more than one is message is received, use a single notification
+to provide a summary such as "2 new messages."</p>
 
-<p>However, when a user is viewing your notifications on a wearable device, you can create
-a stack that collects all the notifications for immediate access without creating multiple
-cards in the card stream.</p>
+<p>However, a summary notification is less useful on an Android wearable because users
+are not able to read details from each message on the wearable (they must open your app on the
+handheld to view more information). So for the wearable device, you should
+group all the notifications together in a stack. The stack of notifications appears as a single
+card, which users can expand to view the details from each notification separately. The new
+<a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setGroup(java.lang.String, int)">
+<code>setGroup()</code></a> method makes this possible while allowing you to still provide
+only one summary notification on the handheld device.</p>
 
 <p>For details about designing notification stacks, see the
 <a href="/wear/design/index.html#NotificationStacks">Design Principles of Android
@@ -420,12 +425,10 @@
 
 <h2 id="AddSummary">Add a Summary Notification</h2>
 
-<p>It's important that you still provide a summary notification for handheld devices. So in
-addition to adding each unique notification to the same stack group, also add the summary
-notification but set its order position to be <a
-href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.
-The notification in this position does not appear in the stack on the wearable but
-appears as the only notification on the handheld.</p>
+<p>It's important that you still provide a summary notification that appears on handheld devices.
+So in addition to adding each unique notification to the same stack group, also add a summary
+notification, but set its order position to be <a
+href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p>
 
 <pre>
 Notification summaryNotification = new WearableNotifications.Builder(builder)
@@ -433,6 +436,8 @@
          .build();
 </pre>
 
+<p>This notification will not appear in your stack of notifications on the wearable, but
+appears as the only notification on the handheld device.
 
 </body>
 </html>
diff --git a/docs/html/wear/preview/start.html b/docs/html/wear/preview/start.html
index ce51c66..4aec648 100644
--- a/docs/html/wear/preview/start.html
+++ b/docs/html/wear/preview/start.html
@@ -376,12 +376,11 @@
     <div class="cols">
 
   <div class="col-5">
-<p>The Android Wear Developer Preview provides tools and APIs that allow you to
+<p>The Android Wear Developer Preview includes tools and APIs that allow you to
 enhance your app notifications
-to provide an optimized user experience on Android Wear.</p>
+to provide an optimized user experience on Android wearables.</p>
 
-<p>With the Android Wear
-Developer Preview, you can:</p>
+<p>With the Android Wear Developer Preview, you can:</p>
 
 <ul>
   <li>Run the Android Wear platform in the Android emulator.</li>
@@ -438,12 +437,12 @@
 </ol>
 
 <p class="note"><strong>Note:</strong>
-If you're already using the ADT plugin for Eclipse, you must update to version 22.6.1 or higher.
-To check for updates, select <strong>Help &gt; Check for updates</strong> in the Eclipse toolbar. </p>
+If you're using the ADT plugin for Eclipse, you must update to version 22.6.1 or higher.
+If you're using Android Studio, you must update to version 0.5.1 or higher</p>
 
 
 
-<h2 id="Install">1. Install the Android Wear system image</h2>
+<h2 id="Install">1. Install the Android Wear System Image</h2>
 
 
 <ol>
@@ -464,7 +463,9 @@
     </ol>
   </li>
 
-  <li>Below Android 4.4.2, select <strong>Android Wear ARM EABI v7a System Image</strong>.</li>
+  <li>Below Android 4.4.2, select <strong>Android Wear ARM EABI v7a System Image</strong>.
+<p class="note"><strong>Note:</strong> Android Wear is designed to support multiple processor architectures.
+</p></li>
   <li>Below Extras, ensure that you have the latest version of the
 <a href="/tools/support-library/index.html">Android Support Library</a>.
     If an update is available, select <strong>Android Support Library</strong>. If you're using Android Studio, also select <strong>Android Support Repository</strong>.</li>
@@ -490,11 +491,13 @@
 <li>For the Device, select <strong>Android Wear Square</strong> or
 	<strong>Android Wear Round</strong>.</li>
 <li>For the Target, select <strong>Android 4.4.2 - API Level 19</strong> (or higher).</li>
-<li>For the CPU/ABI, select <strong>Android Wear ARM (armeabi-v7a)</strong>.</li>
+<li>For the CPU/ABI, select <strong>Android Wear ARM (armeabi-v7a)</strong>.
+<p class="note"><strong>Note:</strong> Android Wear is designed to support multiple processor architectures.
+</p></li>
 <li>For the Skin, select <strong>AndroidWearSquare</strong> or
 <strong>AndroidWearRound</strong>.</li>
 <li>Leave all other options set to their defaults and click <strong>OK</strong>.
-  <p>Although real Android Wear devices do not provide a keyboard as an input method,
+  <p>Although real Android wearables do not provide a keyboard as an input method,
     you should keep <strong>Hardware keyboard present</strong> selected so you can
     provide text input on screens where users will instead provide voice input.</p>
 </li>
@@ -526,8 +529,8 @@
 
 <h2 id="SetupApp">3. Set Up the Android Wear Preview App</h2>
 
-<p>The <em>Android Wear Preview</em> app is an app you must have installed on your Android
-device (a phone or tablet) in order to deliver app notifications to the Android Wear emulator.</p>
+<p>To view your app's notifications on the Android Wear emulator, you must have the
+<em>Android Wear Preview</em> app installed on your Android device (a phone or tablet).</p>
 
 <p>To receive the Android Wear Preview app, you must <a
 href="/wear/preview/signup.html">sign up for the Developer Preview</a> using the same
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index da3f74d..0e7d83e 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -106,7 +106,7 @@
             env, env->FindClass("android/media/MediaHTTPConnection"));
     CHECK(clazz.get() != NULL);
 
-    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I");
+    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
     CHECK(gFields.context != NULL);
 
     gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I");
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 3b448d9..e43a1e2 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -47,7 +47,7 @@
     <string name="pref_advanced_devices" msgid="903257239609301276">"Erweiterte Geräte anzeigen"</string>
     <string name="pref_file_size" msgid="2826879315743961459">"Dateigröße anzeigen"</string>
     <string name="pref_device_size" msgid="3542106883278997222">"Geräteabmessungen anzeigen"</string>
-    <string name="empty" msgid="7858882803708117596">"Keine Elemente"</string>
+    <string name="empty" msgid="7858882803708117596">"Keine Dokumente"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"Datei kann nicht geöffnet werden."</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"Einige Dokumente konnten nicht gelöscht werden."</string>
     <string name="share_via" msgid="8966594246261344259">"Teilen über"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index aec3318..5a91484 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -27,7 +27,7 @@
     <string name="menu_settings" msgid="6008033148948428823">"Ρυθμίσεις"</string>
     <string name="menu_open" msgid="432922957274920903">"Άνοιγμα"</string>
     <string name="menu_save" msgid="2394743337684426338">"Αποθήκευση"</string>
-    <string name="menu_share" msgid="3075149983979628146">"Κοινή χρήση"</string>
+    <string name="menu_share" msgid="3075149983979628146">"Κοινοποίηση"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Διαγραφή"</string>
     <string name="mode_selected_count" msgid="459111894725594625">"Επιλέχθηκαν <xliff:g id="COUNT">%1$d</xliff:g>"</string>
     <string name="sort_name" msgid="9183560467917256779">"Κατά όνομα"</string>
@@ -50,5 +50,5 @@
     <string name="empty" msgid="7858882803708117596">"Δεν υπάρχουν στοιχεία"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"Δεν είναι δυνατό το άνοιγμα του αρχείου"</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"Δεν είναι δυνατή η διαγραφή ορισμένων εγγράφων"</string>
-    <string name="share_via" msgid="8966594246261344259">"Κοινή χρήση μέσω"</string>
+    <string name="share_via" msgid="8966594246261344259">"Κοινοποίηση μέσω"</string>
 </resources>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 519b936..d9f4475 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -22,7 +22,7 @@
     <string name="menu_create_dir" msgid="5947289605844398389">"Buat folder"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Tampilan kisi"</string>
     <string name="menu_list" msgid="7279285939892417279">"Tampilan daftar"</string>
-    <string name="menu_sort" msgid="7677740407158414452">"Sortir menurut"</string>
+    <string name="menu_sort" msgid="7677740407158414452">"Urutkan menurut"</string>
     <string name="menu_search" msgid="3816712084502856974">"Telusuri"</string>
     <string name="menu_settings" msgid="6008033148948428823">"Setelan"</string>
     <string name="menu_open" msgid="432922957274920903">"Buka"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 2a96b1a..4b5ebcd 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -44,7 +44,7 @@
     <string name="root_type_shortcut" msgid="3318760609471618093">"Skratky"</string>
     <string name="root_type_device" msgid="7121342474653483538">"Zariadenia"</string>
     <string name="root_type_apps" msgid="8838065367985945189">"Ďalšie aplikácie"</string>
-    <string name="pref_advanced_devices" msgid="903257239609301276">"Zobraziť rozšírené zariadenia"</string>
+    <string name="pref_advanced_devices" msgid="903257239609301276">"Zobraziť pokročilé zariadenia"</string>
     <string name="pref_file_size" msgid="2826879315743961459">"Zobraziť veľkosť súboru"</string>
     <string name="pref_device_size" msgid="3542106883278997222">"Zobraziť veľkosť zariadenia"</string>
     <string name="empty" msgid="7858882803708117596">"Žiadne položky"</string>
diff --git a/packages/ExternalStorageProvider/res/values-sk/strings.xml b/packages/ExternalStorageProvider/res/values-sk/strings.xml
index fd424c8..9be7b79 100644
--- a/packages/ExternalStorageProvider/res/values-sk/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sk/strings.xml
@@ -17,6 +17,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7123375275748530234">"Externý ukladací priestor"</string>
-    <string name="root_internal_storage" msgid="827844243068584127">"Interný ukladací priestor"</string>
+    <string name="root_internal_storage" msgid="827844243068584127">"Interné úložisko"</string>
     <string name="root_documents" msgid="4051252304075469250">"Dokumenty"</string>
 </resources>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index cfb557e..17fabdc 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -33,7 +33,7 @@
     <string name="page_count_unknown" msgid="6058852665954511124">"Pages"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"Génération tâche impression…"</string>
     <string name="save_as_pdf" msgid="5718454119847596853">"Enregistrer au format .PDF"</string>
-    <string name="all_printers" msgid="5018829726861876202">"Toutes les imprimantes…"</string>
+    <string name="all_printers" msgid="5018829726861876202">"Toutes les imprim."</string>
     <string name="print_dialog" msgid="32628687461331979">"Boîte de dialogue d\'impression"</string>
     <string name="search" msgid="5421724265322228497">"Rechercher"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Toutes les imprimantes"</string>
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 3669f78..9b1eb7b 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Κέλυφος"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Η λήψη της αναφοράς ήταν επιτυχής"</string>
-    <string name="bugreport_finished_text" msgid="3559904746859400732">"Αγγίξτε για κοινή χρήση της αναφοράς σας σφαλμάτων"</string>
+    <string name="bugreport_finished_text" msgid="3559904746859400732">"Αγγίξτε για να μοιραστείτε τη αναφορά σφαλμάτων"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Οι αναφορές σφαλμάτων περιέχουν δεδομένα από τα διάφορα αρχεία καταγραφής του συστήματος, συμπεριλαμβανομένων προσωπικών και ιδιωτικών πληροφοριών. Να μοιράζεστε αναφορές σφαλμάτων μόνο με εφαρμογές και άτομα που εμπιστεύεστε."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Εμφάνιση αυτού του μηνύματος την επόμενη φορά"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 71a644d..7671ef1 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -206,7 +206,7 @@
     <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Modalità inversione colori"</string>
     <string name="quick_settings_contrast_label" msgid="3319507551689108692">"Modalità di contrasto avanzata"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"Modalità di correzione del colore"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ELEMENTI RECENTI"</string>
+    <string name="recents_empty_message" msgid="2269156590813544104">"MESSAGGI RECENTI"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La rete potrebbe\nessere monitorata"</string>
     <string name="description_target_search" msgid="3091587249776033139">"Ricerca"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"Su per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 6e09131..032fa34 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -204,7 +204,7 @@
     <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Modus voor kleurinversie"</string>
     <string name="quick_settings_contrast_label" msgid="3319507551689108692">"Modus voor verbeterd contrast"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"Modus voor kleurcorrectie"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTEN"</string>
+    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk kan\nworden gecontroleerd"</string>
     <string name="description_target_search" msgid="3091587249776033139">"Zoeken"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"Veeg omhoog voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 0d9be4c..0188563 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -151,7 +151,7 @@
     <string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g> <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"มือถือ <xliff:g id="SIGNAL">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> <xliff:g id="NETWORK">%3$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"แบตเตอรี่ <xliff:g id="STATE">%s</xliff:g>"</string>
-    <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"โหมดใช้งานบนเครื่องบิน <xliff:g id="STATE">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"โหมดใช้บนเครื่องบิน <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"บลูทูธ <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"สถานที่ <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ตั้งเวลาปลุกไว้ที่ <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -174,7 +174,7 @@
     <string name="dessert_case" msgid="1295161776223959221">"ชั้นแสดงของหวาน"</string>
     <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"อีเทอร์เน็ต"</string>
-    <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้งานบนเครื่องบิน"</string>
+    <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้บนเครื่องบิน"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"กำลังชาร์จ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
     <string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"ชาร์จแล้ว"</string>
     <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"บลูทูธ"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 55399c2..163ef2a 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -199,7 +199,7 @@
     <string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
     <string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未連線"</string>
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網路"</string>
-    <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi:關閉"</string>
+    <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 已關閉"</string>
     <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"投放螢幕"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
index e208677..77d5076 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,18 +37,19 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.AppWidgetBackupBridge;
+import com.android.server.WidgetBackupProvider;
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.List;
-import java.util.Locale;
 
 
 /**
  * SystemService that publishes an IAppWidgetService.
  */
-public class AppWidgetService extends SystemService {
+public class AppWidgetService extends SystemService implements WidgetBackupProvider {
 
     static final String TAG = "AppWidgetService";
 
@@ -72,6 +72,7 @@
     @Override
     public void onStart() {
         publishBinderService(Context.APPWIDGET_SERVICE, mServiceImpl);
+        AppWidgetBackupBridge.register(this);
     }
 
     @Override
@@ -81,13 +82,40 @@
         }
     }
 
+
+    // backup <-> app widget service bridge surface
+    @Override
+    public List<String> getWidgetParticipants(int userId) {
+        return mServiceImpl.getWidgetParticipants(userId);
+    }
+
+    @Override
+    public byte[] getWidgetState(String packageName, int userId) {
+        return mServiceImpl.getWidgetState(packageName, userId);
+    }
+
+    @Override
+    public void restoreStarting(int userId) {
+        mServiceImpl.restoreStarting(userId);
+    }
+
+    @Override
+    public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+        mServiceImpl.restoreWidgetState(packageName, restoredState, userId);
+    }
+
+    @Override
+    public void restoreFinished(int userId) {
+        mServiceImpl.restoreFinished(userId);
+    }
+
+
+    // implementation entry point and binder service
     private final AppWidgetServiceStub mServiceImpl = new AppWidgetServiceStub();
 
-    private class AppWidgetServiceStub extends IAppWidgetService.Stub {
+    class AppWidgetServiceStub extends IAppWidgetService.Stub {
 
         private boolean mSafeMode;
-        private Locale mLocale;
-        private PackageManager mPackageManager;
 
         public void systemRunning(boolean safeMode) {
             mSafeMode = safeMode;
@@ -227,6 +255,29 @@
             }
         }
 
+
+        // support of the widget/backup bridge
+        public List<String> getWidgetParticipants(int userId) {
+            return getImplForUser(userId).getWidgetParticipants();
+        }
+
+        public byte[] getWidgetState(String packageName, int userId) {
+            return getImplForUser(userId).getWidgetState(packageName);
+        }
+
+        public void restoreStarting(int userId) {
+            getImplForUser(userId).restoreStarting();
+        }
+
+        public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+            getImplForUser(userId).restoreWidgetState(packageName, restoredState);
+        }
+
+        public void restoreFinished(int userId) {
+            getImplForUser(userId).restoreFinished();
+        }
+
+
         private void checkPermission(int userId) {
             int realUserId = ActivityManager.handleIncomingUser(
                     Binder.getCallingPid(),
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b6391b6..b84df79 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
@@ -51,6 +52,7 @@
 import android.util.AttributeSet;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.Display;
@@ -66,6 +68,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -79,8 +83,11 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map.Entry;
 import java.util.Set;
 
+import libcore.util.MutableInt;
+
 class AppWidgetServiceImpl {
 
     private static final String KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
@@ -89,8 +96,10 @@
     private static final String SETTINGS_FILENAME = "appwidgets.xml";
     private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
     private static final int CURRENT_VERSION = 1; // Bump if the stored widgets need to be upgraded.
+    private static final int WIDGET_STATE_VERSION = 1;  // version of backed-up widget state
 
-    private static boolean DBG = false;
+    private static boolean DBG = true;
+    private static boolean DEBUG_BACKUP = DBG || true;
 
     /*
      * When identifying a Host or Provider based on the calling process, use the uid field. When
@@ -105,6 +114,25 @@
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
 
         int tag; // for use while saving state (the index)
+
+        // is there an instance of this provider hosted by the given app?
+        public boolean isHostedBy(String packageName) {
+            final int N = instances.size();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = instances.get(i);
+                if (packageName.equals(id.host.packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "Provider{" + ((info == null) ? "null" : info.provider)
+                    + (zombie ? " Z" : "")
+                    + '}';
+        }
     }
 
     static class Host {
@@ -125,14 +153,62 @@
                 return this.uid == callingUid;
             }
         }
+
+        boolean hostsPackage(String pkg) {
+            final int N = instances.size();
+            for (int i = 0; i < N; i++) {
+                Provider p = instances.get(i).provider;
+                if (p.info != null && pkg.equals(p.info.provider.getPackageName())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "Host{" + packageName + ":" + hostId + '}';
+        }
     }
 
     static class AppWidgetId {
         int appWidgetId;
+        int restoredId;  // tracking & remapping any restored state
         Provider provider;
         RemoteViews views;
         Bundle options;
         Host host;
+
+        @Override
+        public String toString() {
+            return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}';
+        }
+    }
+
+    AppWidgetId findRestoredWidgetLocked(int restoredId, Host host, Provider p) {
+        if (DEBUG_BACKUP) {
+            Slog.i(TAG, "Find restored widget: id=" + restoredId
+                    + " host=" + host + " provider=" + p);
+        }
+
+        if (p == null || host == null) {
+            return null;
+        }
+
+        final int N = mAppWidgetIds.size();
+        for (int i = 0; i < N; i++) {
+            AppWidgetId widget = mAppWidgetIds.get(i);
+            if (widget.restoredId == restoredId
+                    && widget.host.hostId == host.hostId
+                    && widget.host.packageName.equals(host.packageName)
+                    && widget.provider.info.provider.equals(p.info.provider)) {
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "   Found at " + i + " : " + widget);
+                }
+                return widget;
+            }
+        }
+        return null;
     }
 
     /**
@@ -194,6 +270,22 @@
     boolean mStateLoaded;
     int mMaxWidgetBitmapMemory;
 
+    // Map old (restored) widget IDs to new AppWidgetId instances.  This object is used
+    // as the lock around manipulation of the overall restored-widget state, just as
+    // mAppWidgetIds is used as the lock object around all "live" widget state
+    // manipulations.  Methods that must be called with this lock held are decorated
+    // with the suffix "Lr".
+    //
+    // In cases when both of those locks must be held concurrently, mRestoredWidgetIds
+    // must be acquired *first.*
+    private final SparseArray<AppWidgetId> mRestoredWidgetIds = new SparseArray<AppWidgetId>();
+
+    // We need to make sure to wipe the pre-restore widget state only once for
+    // a given package.  Keep track of what we've done so far here; the list is
+    // cleared at the start of every system restore pass, but preserved through
+    // any install-time restore operations.
+    HashSet<String> mPrunedApps = new HashSet<String>();
+
     private final Handler mSaveStateHandler;
 
     // These are for debugging only -- widgets are going missing in some rare instances
@@ -300,10 +392,19 @@
                         providersModified |= updateProvidersForPackageLocked(pkgName, null);
                     }
                 } else {
-                    // The package was just added
+                    // The package was just added.  Fix up the providers...
                     for (String pkgName : pkgList) {
                         providersModified |= addProvidersForPackageLocked(pkgName);
                     }
+                    // ...and see if these are hosts we've been awaiting
+                    for (String pkg : pkgList) {
+                        try {
+                            int uid = getUidForPackage(pkg);
+                            resolveHostUidLocked(pkg, uid);
+                        } catch (NameNotFoundException e) {
+                            // shouldn't happen; we just installed it!
+                        }
+                    }
                 }
                 saveStateAsync();
             }
@@ -331,6 +432,19 @@
         }
     }
 
+    void resolveHostUidLocked(String pkg, int uid) {
+        final int N = mHosts.size();
+        for (int i = 0; i < N; i++) {
+            Host h = mHosts.get(i);
+            if (h.uid == -1 && pkg.equals(h.packageName)) {
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "host " + pkg + ":" + h.hostId + " resolved to uid " + uid);
+                }
+                h.uid = uid;
+            }
+        }
+    }
+
     private void dumpProvider(Provider p, int index, PrintWriter pw) {
         AppWidgetProviderInfo info = p.info;
         pw.print("  ["); pw.print(index); pw.print("] provider ");
@@ -433,7 +547,7 @@
             if (!mHasFeature) {
                 return;
             }
-            loadAppWidgetListLocked();
+            loadWidgetProviderListLocked();
             loadStateLocked();
             mStateLoaded = true;
         }
@@ -516,6 +630,7 @@
     }
 
     void deleteHostLocked(Host host) {
+        if (DBG) log("Deleting host " + host);
         final int N = host.instances.size();
         for (int i = N - 1; i >= 0; i--) {
             AppWidgetId id = host.instances.get(i);
@@ -719,7 +834,7 @@
             }
             final ComponentName componentName = intent.getComponent();
             try {
-                final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
+                final ServiceInfo si = mPm.getServiceInfo(componentName,
                         PackageManager.GET_PERMISSIONS, mUserId);
                 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
                     throw new SecurityException("Selected service does not require "
@@ -981,7 +1096,6 @@
             if (!mHasFeature) {
                 return;
             }
-            options = cloneIfLocalBinder(options);
             ensureStateLoadedLocked();
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
 
@@ -991,7 +1105,7 @@
 
             Provider p = id.provider;
             // Merge the options
-            id.options.putAll(options);
+            id.options.putAll(cloneIfLocalBinder(options));
 
             // send the broacast saying that this appWidgetId has been deleted
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
@@ -1278,9 +1392,13 @@
     }
 
     Provider lookupProviderLocked(ComponentName provider) {
-        final int N = mInstalledProviders.size();
+        return lookupProviderLocked(provider, mInstalledProviders);
+    }
+
+    Provider lookupProviderLocked(ComponentName provider, ArrayList<Provider> installedProviders) {
+        final int N = installedProviders.size();
         for (int i = 0; i < N; i++) {
-            Provider p = mInstalledProviders.get(i);
+            Provider p = installedProviders.get(i);
             if (p.info.provider.equals(provider)) {
                 return p;
             }
@@ -1317,11 +1435,12 @@
 
     void pruneHostLocked(Host host) {
         if (host.instances.size() == 0 && host.callbacks == null) {
+            if (DBG) log("Pruning host " + host);
             mHosts.remove(host);
         }
     }
 
-    void loadAppWidgetListLocked() {
+    void loadWidgetProviderListLocked() {
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         try {
             List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
@@ -1348,7 +1467,23 @@
         Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
                 ri.activityInfo.name), ri);
         if (p != null) {
-            mInstalledProviders.add(p);
+            // we might have an inactive entry for this provider already due to
+            // a preceding restore operation.  if so, fix it up in place; otherwise
+            // just add this new one.
+            Provider existing = lookupProviderLocked(p.info.provider);
+            if (existing != null) {
+                if (existing.zombie && !mSafeMode) {
+                    // it's a placeholder that was set up during an app restore
+                    existing.zombie = false;
+                    existing.info = p.info; // the real one filled out from the ResolveInfo
+                    existing.uid = p.uid;
+                    if (DEBUG_BACKUP) {
+                        Slog.i(TAG, "Provider placeholder now reified: " + existing);
+                    }
+                }
+            } else {
+                mInstalledProviders.add(p);
+            }
             return true;
         } else {
             return false;
@@ -1463,6 +1598,554 @@
         }
     }
 
+    public List<String> getWidgetParticipants() {
+        HashSet<String> packages = new HashSet<String>();
+        synchronized (mAppWidgetIds) {
+            final int N = mAppWidgetIds.size();
+            for (int i = 0; i < N; i++) {
+                final AppWidgetId id = mAppWidgetIds.get(i);
+                packages.add(id.host.packageName);
+                packages.add(id.provider.info.provider.getPackageName());
+            }
+        }
+        return new ArrayList<String>(packages);
+    }
+
+    private void serializeProvider(XmlSerializer out, Provider p) throws IOException {
+        out.startTag(null, "p");
+        out.attribute(null, "pkg", p.info.provider.getPackageName());
+        out.attribute(null, "cl", p.info.provider.getClassName());
+        out.endTag(null, "p");
+    }
+
+    private void serializeHost(XmlSerializer out, Host host) throws IOException {
+        out.startTag(null, "h");
+        out.attribute(null, "pkg", host.packageName);
+        out.attribute(null, "id", Integer.toHexString(host.hostId));
+        out.endTag(null, "h");
+    }
+
+    private void serializeAppWidgetId(XmlSerializer out, AppWidgetId id) throws IOException {
+        out.startTag(null, "g");
+        out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
+        out.attribute(null, "rid", Integer.toHexString(id.restoredId));
+        out.attribute(null, "h", Integer.toHexString(id.host.tag));
+        if (id.provider != null) {
+            out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+        }
+        if (id.options != null) {
+            out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
+            out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
+            out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
+            out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
+            out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+        }
+        out.endTag(null, "g");
+    }
+
+    private Bundle parseWidgetIdOptions(XmlPullParser parser) {
+        Bundle options = new Bundle();
+        String minWidthString = parser.getAttributeValue(null, "min_width");
+        if (minWidthString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+                    Integer.parseInt(minWidthString, 16));
+        }
+        String minHeightString = parser.getAttributeValue(null, "min_height");
+        if (minHeightString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+                    Integer.parseInt(minHeightString, 16));
+        }
+        String maxWidthString = parser.getAttributeValue(null, "max_width");
+        if (maxWidthString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+                    Integer.parseInt(maxWidthString, 16));
+        }
+        String maxHeightString = parser.getAttributeValue(null, "max_height");
+        if (maxHeightString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+                    Integer.parseInt(maxHeightString, 16));
+        }
+        String categoryString = parser.getAttributeValue(null, "host_category");
+        if (categoryString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                    Integer.parseInt(categoryString, 16));
+        }
+        return options;
+    }
+
+    // Does this package either host or provide any active widgets?
+    private boolean packageNeedsWidgetBackupLocked(String packageName) {
+        int N = mAppWidgetIds.size();
+        for (int i = 0; i < N; i++) {
+            AppWidgetId id = mAppWidgetIds.get(i);
+            if (packageName.equals(id.host.packageName)) {
+                // this package is hosting widgets, so it knows widget IDs
+                return true;
+            }
+            Provider p = id.provider;
+            if (p != null && packageName.equals(p.info.provider.getPackageName())) {
+                // someone is hosting this app's widgets, so it knows widget IDs
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // build the widget-state blob that we save for the app during backup.
+    public byte[] getWidgetState(String backupTarget) {
+        if (!mHasFeature) {
+            return null;
+        }
+
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        synchronized (mAppWidgetIds) {
+            // Preflight: if this app neither hosts nor provides any live widgets
+            // we have no work to do.
+            if (!packageNeedsWidgetBackupLocked(backupTarget)) {
+                return null;
+            }
+
+            try {
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(stream, "utf-8");
+                out.startDocument(null, true);
+                out.startTag(null, "ws");      // widget state
+                out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION));
+                out.attribute(null, "pkg", backupTarget);
+
+                // Remember all the providers that are currently hosted or published
+                // by this package: that is, all of the entities related to this app
+                // which will need to be told about id remapping.
+                int N = mInstalledProviders.size();
+                int index = 0;
+                for (int i = 0; i < N; i++) {
+                    Provider p = mInstalledProviders.get(i);
+                    if (p.instances.size() > 0) {
+                        if (backupTarget.equals(p.info.provider.getPackageName())
+                                || p.isHostedBy(backupTarget)) {
+                            serializeProvider(out, p);
+                            p.tag = index++;
+                        }
+                    }
+                }
+
+                N = mHosts.size();
+                index = 0;
+                for (int i = 0; i < N; i++) {
+                    Host host = mHosts.get(i);
+                    if (backupTarget.equals(host.packageName)
+                            || host.hostsPackage(backupTarget)) {
+                        serializeHost(out, host);
+                        host.tag = index++;
+                    }
+                }
+
+                // All widget instances involving this package,
+                // either as host or as provider
+                N = mAppWidgetIds.size();
+                for (int i = 0; i < N; i++) {
+                    AppWidgetId id = mAppWidgetIds.get(i);
+                    if (backupTarget.equals(id.host.packageName)
+                            || backupTarget.equals(id.provider.info.provider.getPackageName())) {
+                        serializeAppWidgetId(out, id);
+                    }
+                }
+
+                out.endTag(null, "ws");
+                out.endDocument();
+            } catch (IOException e) {
+                Slog.w(TAG, "Unable to save widget state for " + backupTarget);
+                return null;
+            }
+
+        }
+        return stream.toByteArray();
+    }
+
+    public void restoreStarting() {
+        if (DEBUG_BACKUP) {
+            Slog.i(TAG, "restore starting for user " + mUserId);
+        }
+        synchronized (mRestoredWidgetIds) {
+            // We're starting a new "system" restore operation, so any widget restore
+            // state that we see from here on is intended to replace the current
+            // widget configuration of any/all of the affected apps.
+            mPrunedApps.clear();
+            mUpdatesByProvider.clear();
+            mUpdatesByHost.clear();
+        }
+    }
+
+    // We're restoring widget state for 'pkg', so we start by wiping (a) all widget
+    // instances that are hosted by that app, and (b) all instances in other hosts
+    // for which 'pkg' is the provider.  We assume that we'll be restoring all of
+    // these hosts & providers, so will be reconstructing a correct live state.
+    private void pruneWidgetStateLr(String pkg) {
+        if (!mPrunedApps.contains(pkg)) {
+            if (DEBUG_BACKUP) {
+                Slog.i(TAG, "pruning widget state for restoring package " + pkg);
+            }
+            for (int i = mAppWidgetIds.size() - 1; i >= 0; i--) {
+                AppWidgetId id = mAppWidgetIds.get(i);
+                Provider p = id.provider;
+                if (id.host.packageName.equals(pkg)
+                        || p.info.provider.getPackageName().equals(pkg)) {
+                    // 'pkg' is either the host or the provider for this instances,
+                    // so we tear it down in anticipation of it (possibly) being
+                    // reconstructed due to the restore
+                    p.instances.remove(id);
+
+                    unbindAppWidgetRemoteViewsServicesLocked(id);
+                    mAppWidgetIds.remove(i);
+                }
+            }
+            mPrunedApps.add(pkg);
+        } else {
+            if (DEBUG_BACKUP) {
+                Slog.i(TAG, "already pruned " + pkg + ", continuing normally");
+            }
+        }
+    }
+
+    // Accumulate a list of updates that affect the given provider for a final
+    // coalesced notification broadcast once restore is over.
+    class RestoreUpdateRecord {
+        public int oldId;
+        public int newId;
+        public boolean notified;
+
+        public RestoreUpdateRecord(int theOldId, int theNewId) {
+            oldId = theOldId;
+            newId = theNewId;
+            notified = false;
+        }
+    }
+
+    HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider
+            = new HashMap<Provider, ArrayList<RestoreUpdateRecord>>();
+    HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost
+            = new HashMap<Host, ArrayList<RestoreUpdateRecord>>();
+
+    private boolean alreadyStashed(ArrayList<RestoreUpdateRecord> stash,
+            final int oldId, final int newId) {
+        final int N = stash.size();
+        for (int i = 0; i < N; i++) {
+            RestoreUpdateRecord r = stash.get(i);
+            if (r.oldId == oldId && r.newId == newId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void stashProviderRestoreUpdateLr(Provider provider, int oldId, int newId) {
+        ArrayList<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider);
+        if (r == null) {
+            r = new ArrayList<RestoreUpdateRecord>();
+            mUpdatesByProvider.put(provider, r);
+        } else {
+            // don't duplicate
+            if (alreadyStashed(r, oldId, newId)) {
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "ID remap " + oldId + " -> " + newId
+                            + " already stashed for " + provider);
+                }
+                return;
+            }
+        }
+        r.add(new RestoreUpdateRecord(oldId, newId));
+    }
+
+    private void stashHostRestoreUpdateLr(Host host, int oldId, int newId) {
+        ArrayList<RestoreUpdateRecord> r = mUpdatesByHost.get(host);
+        if (r == null) {
+            r = new ArrayList<RestoreUpdateRecord>();
+            mUpdatesByHost.put(host, r);
+        } else {
+            if (alreadyStashed(r, oldId, newId)) {
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "ID remap " + oldId + " -> " + newId
+                            + " already stashed for " + host);
+                }
+                return;
+            }
+        }
+        r.add(new RestoreUpdateRecord(oldId, newId));
+    }
+
+    public void restoreWidgetState(String packageName, byte[] restoredState) {
+        if (!mHasFeature) {
+            return;
+        }
+
+        if (DEBUG_BACKUP) {
+            Slog.i(TAG, "Restoring widget state for " + packageName);
+        }
+
+        ByteArrayInputStream stream = new ByteArrayInputStream(restoredState);
+        try {
+            // Providers mentioned in the widget dataset by ordinal
+            ArrayList<Provider> restoredProviders = new ArrayList<Provider>();
+
+            // Hosts mentioned in the widget dataset by ordinal
+            ArrayList<Host> restoredHosts = new ArrayList<Host>();
+
+            //HashSet<String> toNotify = new HashSet<String>();
+
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+
+            synchronized (mAppWidgetIds) {
+                synchronized (mRestoredWidgetIds) {
+                    int type;
+                    do {
+                        type = parser.next();
+                        if (type == XmlPullParser.START_TAG) {
+                            final String tag = parser.getName();
+                            if ("ws".equals(tag)) {
+                                String v = parser.getAttributeValue(null, "version");
+                                String pkg = parser.getAttributeValue(null, "pkg");
+
+                                // TODO: fix up w.r.t. canonical vs current package names
+                                if (!packageName.equals(pkg)) {
+                                    Slog.w(TAG, "Package mismatch in ws");
+                                    return;
+                                }
+
+                                int version = Integer.parseInt(v);
+                                if (version > WIDGET_STATE_VERSION) {
+                                    Slog.w(TAG, "Unable to process state version " + version);
+                                    return;
+                                }
+                            } else if ("p".equals(tag)) {
+                                String pkg = parser.getAttributeValue(null, "pkg");
+                                String cl = parser.getAttributeValue(null, "cl");
+
+                                // hostedProviders index will match 'p' attribute in widget's
+                                // entry in the xml file being restored
+                                // If there's no live entry for this provider, add an inactive one
+                                // so that widget IDs referring to them can be properly allocated
+                                final ComponentName cn = new ComponentName(pkg, cl);
+                                Provider p = lookupProviderLocked(cn, mInstalledProviders);
+                                if (p == null) {
+                                    p = new Provider();
+                                    p.info = new AppWidgetProviderInfo();
+                                    p.info.provider = cn;
+                                    p.zombie = true;
+                                    mInstalledProviders.add(p);
+                                }
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "   provider " + cn);
+                                }
+                                restoredProviders.add(p);
+                            } else if ("h".equals(tag)) {
+                                // The host app may not yet exist on the device.  If it's here we
+                                // just use the existing Host entry, otherwise we create a
+                                // placeholder whose uid will be fixed up at PACKAGE_ADDED time.
+                                String pkg = parser.getAttributeValue(null, "pkg");
+                                int uid;
+                                try {
+                                    uid = getUidForPackage(pkg);
+                                } catch (NameNotFoundException e) {
+                                    uid = -1;
+                                }
+                                int hostId = Integer.parseInt(
+                                        parser.getAttributeValue(null, "id"), 16);
+                                Host h = lookupOrAddHostLocked(uid, pkg, hostId);
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "   host[" + restoredHosts.size()
+                                            + "]: {" + h.packageName + ":" + h.hostId + "}");
+                                }
+                                restoredHosts.add(h);
+                            } else if ("g".equals(tag)) {
+                                int restoredId = Integer.parseInt(
+                                        parser.getAttributeValue(null, "id"), 16);
+                                int hostIndex = Integer.parseInt(
+                                        parser.getAttributeValue(null, "h"), 16);
+                                Host host = restoredHosts.get(hostIndex);
+                                Provider p = null;
+                                String prov = parser.getAttributeValue(null, "p");
+                                if (prov != null) {
+                                    // could have been null if the app had allocated an id
+                                    // but not yet established a binding under that id
+                                    int which = Integer.parseInt(prov, 16);
+                                    p = restoredProviders.get(which);
+                                }
+
+                                // We'll be restoring widget state for both the host and
+                                // provider sides of this widget ID, so make sure we are
+                                // beginning from a clean slate on both fronts.
+                                pruneWidgetStateLr(host.packageName);
+                                if (p != null) {
+                                    pruneWidgetStateLr(p.info.provider.getPackageName());
+                                }
+
+                                // Have we heard about this ancestral widget instance before?
+                                AppWidgetId id = findRestoredWidgetLocked(restoredId, host, p);
+                                if (id == null) {
+                                    id = new AppWidgetId();
+                                    id.appWidgetId = mNextAppWidgetId++;
+                                    id.restoredId = restoredId;
+                                    id.options = parseWidgetIdOptions(parser);
+                                    id.host = host;
+                                    id.host.instances.add(id);
+                                    id.provider = p;
+                                    if (id.provider != null) {
+                                        id.provider.instances.add(id);
+                                    }
+                                    if (DEBUG_BACKUP) {
+                                        Slog.i(TAG, "New restored id " + restoredId
+                                                + " now " + id);
+                                    }
+                                    mAppWidgetIds.add(id);
+                                }
+                                if (id.provider.info != null) {
+                                    stashProviderRestoreUpdateLr(id.provider,
+                                            restoredId, id.appWidgetId);
+                                } else {
+                                    Slog.w(TAG, "Missing provider for restored widget " + id);
+                                }
+                                stashHostRestoreUpdateLr(id.host, restoredId, id.appWidgetId);
+
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "   instance: " + restoredId
+                                            + " -> " + id.appWidgetId
+                                            + " :: p=" + id.provider);
+                                }
+                            }
+                        }
+                    } while (type != XmlPullParser.END_DOCUMENT);
+
+                    // We've updated our own bookkeeping.  We'll need to notify the hosts and
+                    // providers about the changes, but we can't do that yet because the restore
+                    // target is not necessarily fully live at this moment.  Set aside the
+                    // information for now; the backup manager will call us once more at the
+                    // end of the process when all of the targets are in a known state, and we
+                    // will update at that point.
+                }
+            }
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Unable to restore widget state for " + packageName);
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to restore widget state for " + packageName);
+        } finally {
+            saveStateAsync();
+        }
+    }
+
+    // Called once following the conclusion of a restore operation.  This is when we
+    // send out updates to apps involved in widget-state restore telling them about
+    // the new widget ID space.
+    public void restoreFinished() {
+        if (DEBUG_BACKUP) {
+            Slog.i(TAG, "restoreFinished for " + mUserId);
+        }
+
+        final UserHandle userHandle = new UserHandle(mUserId);
+        synchronized (mRestoredWidgetIds) {
+            // Build the providers' broadcasts and send them off
+            Set<Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
+                    = mUpdatesByProvider.entrySet();
+            for (Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
+                // For each provider there's a list of affected IDs
+                Provider provider = e.getKey();
+                ArrayList<RestoreUpdateRecord> updates = e.getValue();
+                final int pending = countPendingUpdates(updates);
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+                }
+                if (pending > 0) {
+                    int[] oldIds = new int[pending];
+                    int[] newIds = new int[pending];
+                    final int N = updates.size();
+                    int nextPending = 0;
+                    for (int i = 0; i < N; i++) {
+                        RestoreUpdateRecord r = updates.get(i);
+                        if (!r.notified) {
+                            r.notified = true;
+                            oldIds[nextPending] = r.oldId;
+                            newIds[nextPending] = r.newId;
+                            nextPending++;
+                            if (DEBUG_BACKUP) {
+                                Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
+                            }
+                        }
+                    }
+                    sendWidgetRestoreBroadcast(AppWidgetManager.ACTION_APPWIDGET_RESTORED,
+                            provider, null, oldIds, newIds, userHandle);
+                }
+            }
+
+            // same thing per host
+            Set<Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
+                    = mUpdatesByHost.entrySet();
+            for (Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
+                Host host = e.getKey();
+                if (host.uid > 0) {
+                    ArrayList<RestoreUpdateRecord> updates = e.getValue();
+                    final int pending = countPendingUpdates(updates);
+                    if (DEBUG_BACKUP) {
+                        Slog.i(TAG, "Host " + host + " pending: " + pending);
+                    }
+                    if (pending > 0) {
+                        int[] oldIds = new int[pending];
+                        int[] newIds = new int[pending];
+                        final int N = updates.size();
+                        int nextPending = 0;
+                        for (int i = 0; i < N; i++) {
+                            RestoreUpdateRecord r = updates.get(i);
+                            if (!r.notified) {
+                                r.notified = true;
+                                oldIds[nextPending] = r.oldId;
+                                newIds[nextPending] = r.newId;
+                                nextPending++;
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
+                                }
+                            }
+                        }
+                        sendWidgetRestoreBroadcast(AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
+                                null, host, oldIds, newIds, userHandle);
+                    }
+                }
+            }
+        }
+    }
+
+    private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
+        int pending = 0;
+        final int N = updates.size();
+        for (int i = 0; i < N; i++) {
+            RestoreUpdateRecord r = updates.get(i);
+            if (!r.notified) {
+                pending++;
+            }
+        }
+        return pending;
+    }
+
+    void sendWidgetRestoreBroadcast(String action, Provider provider, Host host,
+            int[] oldIds, int[] newIds, UserHandle userHandle) {
+        Intent intent = new Intent(action);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds);
+        if (provider != null) {
+            intent.setComponent(provider.info.provider);
+            mContext.sendBroadcastAsUser(intent, userHandle);
+        }
+        if (host != null) {
+            intent.setComponent(null);
+            intent.setPackage(host.packageName);
+            intent.putExtra(AppWidgetManager.EXTRA_HOST_ID, host.hostId);
+            mContext.sendBroadcastAsUser(intent, userHandle);
+        }
+    }
+
     private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
         Provider p = null;
 
@@ -1660,10 +2343,7 @@
             for (int i = 0; i < N; i++) {
                 Provider p = mInstalledProviders.get(i);
                 if (p.instances.size() > 0) {
-                    out.startTag(null, "p");
-                    out.attribute(null, "pkg", p.info.provider.getPackageName());
-                    out.attribute(null, "cl", p.info.provider.getClassName());
-                    out.endTag(null, "p");
+                    serializeProvider(out, p);
                     p.tag = providerIndex;
                     providerIndex++;
                 }
@@ -1672,35 +2352,14 @@
             N = mHosts.size();
             for (int i = 0; i < N; i++) {
                 Host host = mHosts.get(i);
-                out.startTag(null, "h");
-                out.attribute(null, "pkg", host.packageName);
-                out.attribute(null, "id", Integer.toHexString(host.hostId));
-                out.endTag(null, "h");
+                serializeHost(out, host);
                 host.tag = i;
             }
 
             N = mAppWidgetIds.size();
             for (int i = 0; i < N; i++) {
                 AppWidgetId id = mAppWidgetIds.get(i);
-                out.startTag(null, "g");
-                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
-                out.attribute(null, "h", Integer.toHexString(id.host.tag));
-                if (id.provider != null) {
-                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
-                }
-                if (id.options != null) {
-                    out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
-                    out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
-                    out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
-                    out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
-                    out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
-                }
-                out.endTag(null, "g");
+                serializeAppWidgetId(out, id);
             }
 
             Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
@@ -1801,6 +2460,11 @@
                             mNextAppWidgetId = id.appWidgetId + 1;
                         }
 
+                        // restored ID is allowed to be absent
+                        String restoredIdString = parser.getAttributeValue(null, "rid");
+                        id.restoredId = (restoredIdString == null) ? 0
+                                : Integer.parseInt(restoredIdString, 16);
+
                         Bundle options = new Bundle();
                         String minWidthString = parser.getAttributeValue(null, "min_width");
                         if (minWidthString != null) {
@@ -1975,8 +2639,7 @@
                 continue;
             }
             if (pkgName.equals(ai.packageName)) {
-                addProviderLocked(ri);
-                providersAdded = true;
+                providersAdded = addProviderLocked(ri);
             }
         }
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8eaefef..1a1512f 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -24,6 +24,7 @@
 import android.app.IBackupAgent;
 import android.app.PendingIntent;
 import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.FullBackup;
 import android.app.backup.RestoreSet;
@@ -82,12 +83,14 @@
 import com.android.internal.backup.BackupConstants;
 import com.android.internal.backup.IBackupTransport;
 import com.android.internal.backup.IObbBackupService;
+import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
 import com.android.server.SystemService;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -115,10 +118,13 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Random;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.zip.Deflater;
 import java.util.zip.DeflaterOutputStream;
@@ -136,22 +142,43 @@
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
 
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
 public class BackupManagerService extends IBackupManager.Stub {
 
     private static final String TAG = "BackupManagerService";
     private static final boolean DEBUG = true;
     private static final boolean MORE_DEBUG = false;
 
+    // System-private key used for backing up an app's widget state.  Must
+    // begin with U+FFxx by convention (we reserve all keys starting
+    // with U+FF00 or higher for system use).
+    static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
+
     // Historical and current algorithm names
     static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
     static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
 
     // Name and current contents version of the full-backup manifest file
+    //
+    // Manifest version history:
+    //
+    // 1 : initial release
     static final String BACKUP_MANIFEST_FILENAME = "_manifest";
     static final int BACKUP_MANIFEST_VERSION = 1;
+
+    // External archive format version history:
+    //
+    // 1 : initial release
+    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
+    // 3 : introduced "_meta" metadata file; no other format change per se
+    static final int BACKUP_FILE_VERSION = 3;
     static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
-    static final int BACKUP_FILE_VERSION = 2;
     static final int BACKUP_PW_FILE_VERSION = 2;
+    static final String BACKUP_METADATA_FILENAME = "_meta";
+    static final int BACKUP_METADATA_VERSION = 1;
+    static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
     static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
 
     static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
@@ -186,6 +213,7 @@
     private static final int MSG_RUN_FULL_RESTORE = 10;
     private static final int MSG_RETRY_INIT = 11;
     private static final int MSG_RETRY_CLEAR = 12;
+    private static final int MSG_WIDGET_BROADCAST = 13;
 
     // backup task state machine tick
     static final int MSG_BACKUP_RESTORE_STEP = 20;
@@ -337,42 +365,47 @@
         public long token;
         public PackageInfo pkgInfo;
         public int pmToken; // in post-install restore, the PM's token for this transaction
-        public boolean needFullBackup;
+        public boolean isSystemRestore;
         public String[] filterSet;
 
+        // Restore a single package
         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-                long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
+                long _token, PackageInfo _pkg, int _pmToken) {
             transport = _transport;
             dirName = _dirName;
             observer = _obs;
             token = _token;
             pkgInfo = _pkg;
             pmToken = _pmToken;
-            needFullBackup = _needFullBackup;
+            isSystemRestore = false;
             filterSet = null;
         }
 
+        // Restore everything possible.  This is the form that Setup Wizard or similar
+        // restore UXes use.
         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-                long _token, boolean _needFullBackup) {
+                long _token) {
             transport = _transport;
             dirName = _dirName;
             observer = _obs;
             token = _token;
             pkgInfo = null;
             pmToken = 0;
-            needFullBackup = _needFullBackup;
+            isSystemRestore = true;
             filterSet = null;
         }
 
+        // Restore some set of packages.  Leave this one up to the caller to specify
+        // whether it's to be considered a system-level restore.
         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-                long _token, String[] _filterSet, boolean _needFullBackup) {
+                long _token, String[] _filterSet, boolean _isSystemRestore) {
             transport = _transport;
             dirName = _dirName;
             observer = _obs;
             token = _token;
             pkgInfo = null;
             pmToken = 0;
-            needFullBackup = _needFullBackup;
+            isSystemRestore = _isSystemRestore;
             filterSet = _filterSet;
         }
     }
@@ -413,16 +446,19 @@
         public boolean includeApks;
         public boolean includeObbs;
         public boolean includeShared;
+        public boolean doWidgets;
         public boolean allApps;
         public boolean includeSystem;
         public String[] packages;
 
         FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
-                boolean saveShared, boolean doAllApps, boolean doSystem, String[] pkgList) {
+                boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
+                String[] pkgList) {
             fd = output;
             includeApks = saveApks;
             includeObbs = saveObbs;
             includeShared = saveShared;
+            doWidgets = alsoWidgets;
             allApps = doAllApps;
             includeSystem = doSystem;
             packages = pkgList;
@@ -618,7 +654,8 @@
                 FullBackupParams params = (FullBackupParams)msg.obj;
                 PerformFullBackupTask task = new PerformFullBackupTask(params.fd,
                         params.observer, params.includeApks, params.includeObbs,
-                        params.includeShared, params.curPassword, params.encryptPassword,
+                        params.includeShared, params.doWidgets,
+                        params.curPassword, params.encryptPassword,
                         params.allApps, params.includeSystem, params.packages, params.latch);
                 (new Thread(task)).start();
                 break;
@@ -631,7 +668,7 @@
                 PerformRestoreTask task = new PerformRestoreTask(
                         params.transport, params.dirName, params.observer,
                         params.token, params.pkgInfo, params.pmToken,
-                        params.needFullBackup, params.filterSet);
+                        params.isSystemRestore, params.filterSet);
                 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
                 sendMessage(restoreMsg);
                 break;
@@ -770,6 +807,13 @@
                 }
                 break;
             }
+
+            case MSG_WIDGET_BROADCAST:
+            {
+                final Intent intent = (Intent) msg.obj;
+                mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+                break;
+            }
             }
         }
     }
@@ -2360,10 +2404,40 @@
 
         @Override
         public void operationComplete() {
-            // Okay, the agent successfully reported back to us.  Spin the data off to the
+            // Okay, the agent successfully reported back to us.  The next thing we do is
+            // push the app widget state for the app, if any.
+            final String pkgName = mCurrentPackage.packageName;
+            final long filepos = mBackupDataName.length();
+            FileDescriptor fd = mBackupData.getFileDescriptor();
+            try {
+                BackupDataOutput out = new BackupDataOutput(fd);
+                byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
+                        UserHandle.USER_OWNER);
+                if (widgetState != null) {
+                    out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
+                    out.writeEntityData(widgetState, widgetState.length);
+                } else {
+                    // No widget state for this app, but push a 'delete' operation for it
+                    // in case they're trying to play games with the payload.
+                    out.writeEntityHeader(KEY_WIDGET_STATE, -1);
+                }
+            } catch (IOException e) {
+                // Hard disk error; recovery/failure policy TBD.  For now roll back,
+                // but we may want to consider this a transport-level failure (i.e.
+                // we're in such a bad state that we can't contemplate doing backup
+                // operations any more during this pass).
+                Slog.w(TAG, "Unable to save widget state for " + pkgName);
+                try {
+                    Libcore.os.ftruncate(fd, filepos);
+                } catch (ErrnoException ee) {
+                    Slog.w(TAG, "Unable to roll back!");
+                }
+            }
+
+            // Spin the data off to the
             // transport and proceed with the next stage.
             if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
-                    + mCurrentPackage.packageName);
+                    + pkgName);
             mBackupHandler.removeMessages(MSG_TIMEOUT);
             clearAgentState();
             addBackupTrace("operation complete");
@@ -2402,17 +2476,14 @@
                 if (mStatus == BackupConstants.TRANSPORT_OK) {
                     mBackupDataName.delete();
                     mNewStateName.renameTo(mSavedStateName);
-                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE,
-                            mCurrentPackage.packageName, size);
-                    logBackupComplete(mCurrentPackage.packageName);
+                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
+                    logBackupComplete(pkgName);
                 } else {
-                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
-                            mCurrentPackage.packageName);
+                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
                 }
             } catch (Exception e) {
-                Slog.e(TAG, "Transport error backing up " + mCurrentPackage.packageName, e);
-                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
-                        mCurrentPackage.packageName);
+                Slog.e(TAG, "Transport error backing up " + pkgName, e);
+                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
                 mStatus = BackupConstants.TRANSPORT_ERROR;
             } finally {
                 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
@@ -2631,18 +2702,21 @@
         boolean mIncludeApks;
         boolean mIncludeObbs;
         boolean mIncludeShared;
+        boolean mDoWidgets;
         boolean mAllApps;
         final boolean mIncludeSystem;
-        String[] mPackages;
+        ArrayList<String> mPackages;
         String mCurrentPassword;
         String mEncryptPassword;
         AtomicBoolean mLatchObject;
         File mFilesDir;
         File mManifestFile;
+        File mMetadataFile;
         
 
         class FullBackupRunner implements Runnable {
             PackageInfo mPackage;
+            byte[] mWidgetData;
             IBackupAgent mAgent;
             ParcelFileDescriptor mPipe;
             int mToken;
@@ -2650,8 +2724,10 @@
             boolean mWriteManifest;
 
             FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
-                    int token, boolean sendApk, boolean writeManifest)  throws IOException {
+                    int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
+                            throws IOException {
                 mPackage = pack;
+                mWidgetData = widgetData;
                 mAgent = agent;
                 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
                 mToken = token;
@@ -2666,12 +2742,24 @@
                             mPipe.getFileDescriptor());
 
                     if (mWriteManifest) {
+                        final boolean writeWidgetData = mWidgetData != null;
                         if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
-                        writeAppManifest(mPackage, mManifestFile, mSendApk);
+                        writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
                         FullBackup.backupToTar(mPackage.packageName, null, null,
                                 mFilesDir.getAbsolutePath(),
                                 mManifestFile.getAbsolutePath(),
                                 output);
+                        mManifestFile.delete();
+
+                        // We only need to write a metadata file if we have widget data to stash
+                        if (writeWidgetData) {
+                            writeMetadata(mPackage, mMetadataFile, mWidgetData);
+                            FullBackup.backupToTar(mPackage.packageName, null, null,
+                                    mFilesDir.getAbsolutePath(),
+                                    mMetadataFile.getAbsolutePath(),
+                                    output);
+                            mMetadataFile.delete();
+                        }
                     }
 
                     if (mSendApk) {
@@ -2696,16 +2784,19 @@
 
         PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, 
                 boolean includeApks, boolean includeObbs, boolean includeShared,
-                String curPassword, String encryptPassword, boolean doAllApps,
+                boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
                 boolean doSystem, String[] packages, AtomicBoolean latch) {
             mOutputFile = fd;
             mObserver = observer;
             mIncludeApks = includeApks;
             mIncludeObbs = includeObbs;
             mIncludeShared = includeShared;
+            mDoWidgets = doWidgets;
             mAllApps = doAllApps;
             mIncludeSystem = doSystem;
-            mPackages = packages;
+            mPackages = (packages == null)
+                    ? new ArrayList<String>()
+                    : new ArrayList<String>(Arrays.asList(packages));
             mCurrentPassword = curPassword;
             // when backing up, if there is a current backup password, we require that
             // the user use a nonempty encryption password as well.  if one is supplied
@@ -2720,13 +2811,28 @@
 
             mFilesDir = new File("/data/system");
             mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
+            mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
+        }
+
+        void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
+            for (String pkgName : pkgNames) {
+                if (!set.containsKey(pkgName)) {
+                    try {
+                        PackageInfo info = mPackageManager.getPackageInfo(pkgName,
+                                PackageManager.GET_SIGNATURES);
+                        set.put(pkgName, info);
+                    } catch (NameNotFoundException e) {
+                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
+                    }
+                }
+            }
         }
 
         @Override
         public void run() {
             Slog.i(TAG, "--- Performing full-dataset backup ---");
 
-            List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>();
+            TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
             FullBackupObbConnection obbConnection = new FullBackupObbConnection();
             obbConnection.establish();  // we'll want this later
 
@@ -2734,63 +2840,70 @@
 
             // doAllApps supersedes the package set if any
             if (mAllApps) {
-                packagesToBackup = mPackageManager.getInstalledPackages(
+                List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
                         PackageManager.GET_SIGNATURES);
-                // Exclude system apps if we've been asked to do so
-                if (mIncludeSystem == false) {
-                    for (int i = 0; i < packagesToBackup.size(); ) {
-                        PackageInfo pkg = packagesToBackup.get(i);
-                        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                            packagesToBackup.remove(i);
-                        } else {
-                            i++;
-                        }
+                for (int i = 0; i < allPackages.size(); i++) {
+                    PackageInfo pkg = allPackages.get(i);
+                    // Exclude system apps if we've been asked to do so
+                    if (mIncludeSystem == true
+                            || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+                        packagesToBackup.put(pkg.packageName, pkg);
                     }
                 }
             }
 
+            // If we're doing widget state as well, ensure that we have all the involved
+            // host & provider packages in the set
+            if (mDoWidgets) {
+                List<String> pkgs =
+                        AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_OWNER);
+                if (pkgs != null) {
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Adding widget participants to backup set:");
+                        StringBuilder sb = new StringBuilder(128);
+                        sb.append("   ");
+                        for (String s : pkgs) {
+                            sb.append(' ');
+                            sb.append(s);
+                        }
+                        Slog.i(TAG, sb.toString());
+                    }
+                    addPackagesToSet(packagesToBackup, pkgs);
+                }
+            }
+
             // Now process the command line argument packages, if any. Note that explicitly-
             // named system-partition packages will be included even if includeSystem was
             // set to false.
             if (mPackages != null) {
-                for (String pkgName : mPackages) {
-                    try {
-                        packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
-                                PackageManager.GET_SIGNATURES));
-                    } catch (NameNotFoundException e) {
-                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
-                    }
-                }
+                addPackagesToSet(packagesToBackup, mPackages);
             }
 
-            // Cull any packages that have indicated that backups are not permitted, as well
-            // as any explicit mention of the 'special' shared-storage agent package (we
-            // handle that one at the end).
-            for (int i = 0; i < packagesToBackup.size(); ) {
-                PackageInfo pkg = packagesToBackup.get(i);
+            // Now we cull any inapplicable / inappropriate packages from the set
+            Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
+            while (iter.hasNext()) {
+                PackageInfo pkg = iter.next().getValue();
                 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0
                         || pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) {
-                    packagesToBackup.remove(i);
-                } else {
-                    i++;
-                }
-            }
-
-            // Cull any packages that run as system-domain uids but do not define their
-            // own backup agents
-            for (int i = 0; i < packagesToBackup.size(); ) {
-                PackageInfo pkg = packagesToBackup.get(i);
-                if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
+                    // Cull any packages that have indicated that backups are not permitted, as well
+                    // as any explicit mention of the 'special' shared-storage agent package (we
+                    // handle that one at the end).
+                    iter.remove();
+                } else if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
                         && (pkg.applicationInfo.backupAgentName == null)) {
+                    // Cull any packages that run as system-domain uids but do not define their
+                    // own backup agents
                     if (MORE_DEBUG) {
                         Slog.i(TAG, "... ignoring non-agent system package " + pkg.packageName);
                     }
-                    packagesToBackup.remove(i);
-                } else {
-                    i++;
+                    iter.remove();
                 }
             }
 
+            // flatten the set of packages now so we can explicitly control the ordering
+            ArrayList<PackageInfo> backupQueue =
+                    new ArrayList<PackageInfo>(packagesToBackup.values());
+
             FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
             OutputStream out = null;
 
@@ -2866,16 +2979,16 @@
                 if (mIncludeShared) {
                     try {
                         pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
-                        packagesToBackup.add(pkg);
+                        backupQueue.add(pkg);
                     } catch (NameNotFoundException e) {
                         Slog.e(TAG, "Unable to find shared-storage backup handler");
                     }
                 }
 
                 // Now back up the app data via the agent mechanism
-                int N = packagesToBackup.size();
+                int N = backupQueue.size();
                 for (int i = 0; i < N; i++) {
-                    pkg = packagesToBackup.get(i);
+                    pkg = backupQueue.get(i);
                     backupOnePackage(pkg, out);
 
                     // after the app's agent runs to handle its private filesystem
@@ -3006,11 +3119,13 @@
                             && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
                                 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
 
+                    byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
+                            UserHandle.USER_OWNER);
                     sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
 
                     final int token = generateToken();
                     FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
-                            token, sendApk, !isSharedStorage);
+                            token, sendApk, !isSharedStorage, widgetBlob);
                     pipes[1].close();   // the runner has dup'd it
                     pipes[1] = null;
                     Thread t = new Thread(runner);
@@ -3086,8 +3201,8 @@
             }
         }
 
-        private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
-                throws IOException {
+        private void writeAppManifest(PackageInfo pkg, File manifestFile,
+                boolean withApk, boolean withWidgets) throws IOException {
             // Manifest format. All data are strings ending in LF:
             //     BACKUP_MANIFEST_VERSION, currently 1
             //
@@ -3125,6 +3240,43 @@
             outstream.close();
         }
 
+        // Widget metadata format. All header entries are strings ending in LF:
+        //
+        // Version 1 header:
+        //     BACKUP_METADATA_VERSION, currently "1"
+        //     package name
+        //
+        // File data (all integers are binary in network byte order)
+        // *N: 4 : integer token identifying which metadata blob
+        //     4 : integer size of this blob = N
+        //     N : raw bytes of this metadata blob
+        //
+        // Currently understood blobs (always in network byte order):
+        //
+        //     widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
+        //
+        // Unrecognized blobs are *ignored*, not errors.
+        private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
+                throws IOException {
+            StringBuilder b = new StringBuilder(512);
+            StringBuilderPrinter printer = new StringBuilderPrinter(b);
+            printer.println(Integer.toString(BACKUP_METADATA_VERSION));
+            printer.println(pkg.packageName);
+
+            FileOutputStream fout = new FileOutputStream(destination);
+            BufferedOutputStream bout = new BufferedOutputStream(fout);
+            DataOutputStream out = new DataOutputStream(bout);
+            bout.write(b.toString().getBytes());    // bypassing DataOutputStream
+
+            if (widgetData != null && widgetData.length > 0) {
+                out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
+                out.writeInt(widgetData.length);
+                out.write(widgetData);
+            }
+            bout.flush();
+            out.close();
+        }
+
         private void tearDown(PackageInfo pkg) {
             if (pkg != null) {
                 final ApplicationInfo app = pkg.applicationInfo;
@@ -3228,6 +3380,7 @@
         ApplicationInfo mTargetApp;
         FullBackupObbConnection mObbConnection = null;
         ParcelFileDescriptor[] mPipes = null;
+        byte[] mWidgetData = null;
 
         long mBytes;
 
@@ -3524,7 +3677,8 @@
                         // Clean up the previous agent relationship if necessary,
                         // and let the observer know we're considering a new app.
                         if (mAgent != null) {
-                            if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
+                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
+                            // Now we're really done
                             tearDownPipes();
                             tearDownAgent(mTargetApp);
                             mTargetApp = null;
@@ -3540,6 +3694,9 @@
                         // input file
                         skipTarPadding(info.size, instream);
                         sendOnRestorePackage(pkg);
+                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
+                        // Metadata blobs!
+                        readMetadata(info, instream);
                     } else {
                         // Non-manifest, so it's actual file data.  Is this a package
                         // we're ignoring?
@@ -3996,6 +4153,71 @@
             }
         }
 
+        // Read a widget metadata file, returning the restored blob
+        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
+            byte[] data = null;
+
+            // Fail on suspiciously large widget dump files
+            if (info.size > 64 * 1024) {
+                throw new IOException("Metadata too big; corrupt? size=" + info.size);
+            }
+
+            byte[] buffer = new byte[(int) info.size];
+            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
+                mBytes += info.size;
+            } else throw new IOException("Unexpected EOF in widget data");
+
+            String[] str = new String[1];
+            int offset = extractLine(buffer, 0, str);
+            int version = Integer.parseInt(str[0]);
+            if (version == BACKUP_MANIFEST_VERSION) {
+                offset = extractLine(buffer, offset, str);
+                final String pkg = str[0];
+                if (info.packageName.equals(pkg)) {
+                    // Data checks out -- the rest of the buffer is a concatenation of
+                    // binary blobs as described in the comment at writeAppWidgetData()
+                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
+                            offset, buffer.length - offset);
+                    DataInputStream in = new DataInputStream(bin);
+                    while (bin.available() > 0) {
+                        int token = in.readInt();
+                        int size = in.readInt();
+                        if (size > 64 * 1024) {
+                            throw new IOException("Datum "
+                                    + Integer.toHexString(token)
+                                    + " too big; corrupt? size=" + info.size);
+                        }
+                        switch (token) {
+                            case BACKUP_WIDGET_METADATA_TOKEN:
+                            {
+                                if (MORE_DEBUG) {
+                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
+                                }
+                                mWidgetData = new byte[size];
+                                in.read(mWidgetData);
+                                break;
+                            }
+                            default:
+                            {
+                                if (DEBUG) {
+                                    Slog.i(TAG, "Ignoring metadata blob "
+                                            + Integer.toHexString(token)
+                                            + " for " + info.packageName);
+                                }
+                                in.skipBytes(size);
+                                break;
+                            }
+                        }
+                    }
+                } else {
+                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
+                            + " but widget data for " + pkg);
+                }
+            } else {
+                Slog.w(TAG, "Unsupported metadata version " + version);
+            }
+        }
+
         // Returns a policy constant; takes a buffer arg to reduce memory churn
         RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
                 throws IOException {
@@ -4486,7 +4708,7 @@
         private PackageInfo mTargetPackage;
         private File mStateDir;
         private int mPmToken;
-        private boolean mNeedFullBackup;
+        private boolean mIsSystemRestore;
         private HashSet<String> mFilterSet;
         private long mStartRealtime;
         private PackageManagerBackupAgent mPmAgent;
@@ -4497,11 +4719,13 @@
         private boolean mFinished;
         private int mStatus;
         private File mBackupDataName;
+        private File mStageName;
         private File mNewStateName;
         private File mSavedStateName;
         private ParcelFileDescriptor mBackupData;
         private ParcelFileDescriptor mNewState;
         private PackageInfo mCurrentPackage;
+        private byte[] mWidgetData;
 
 
         class RestoreRequest {
@@ -4516,7 +4740,7 @@
 
         PerformRestoreTask(IBackupTransport transport, String dirName, IRestoreObserver observer,
                 long restoreSetToken, PackageInfo targetPackage, int pmToken,
-                boolean needFullBackup, String[] filterSet) {
+                boolean isSystemRestore, String[] filterSet) {
             mCurrentState = RestoreState.INITIAL;
             mFinished = false;
             mPmAgent = null;
@@ -4526,7 +4750,7 @@
             mToken = restoreSetToken;
             mTargetPackage = targetPackage;
             mPmToken = pmToken;
-            mNeedFullBackup = needFullBackup;
+            mIsSystemRestore = isSystemRestore;
 
             if (filterSet != null) {
                 mFilterSet = new HashSet<String>();
@@ -4696,8 +4920,7 @@
                 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                 mPmAgent = new PackageManagerBackupAgent(
                         mPackageManager, mAgentPackages);
-                initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()),
-                        mNeedFullBackup);
+                initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
                 // The PM agent called operationComplete() already, because our invocation
                 // of it is process-local and therefore synchronous.  That means that a
                 // RUNNING_QUEUE message is already enqueued.  Only if we're unable to
@@ -4727,6 +4950,12 @@
 
             // Metadata is intact, so we can now run the restore queue.  If we get here,
             // we have already enqueued the necessary next-step message on the looper.
+            // We've deferred telling the App Widget service that we might be replacing
+            // the widget environment with something else, but now we know we've got
+            // data coming, so we do it here.
+            if (mIsSystemRestore) {
+                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER);
+            }
         }
 
         void restoreNextAgent() {
@@ -4835,7 +5064,7 @@
 
                 // And then finally start the restore on this agent
                 try {
-                    initiateOneRestore(packageInfo, metaInfo.versionCode, agent, mNeedFullBackup);
+                    initiateOneRestore(packageInfo, metaInfo.versionCode, agent);
                     ++mCount;
                 } catch (Exception e) {
                     Slog.e(TAG, "Error when attempting restore: " + e.toString());
@@ -4889,6 +5118,9 @@
             mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
                     TIMEOUT_RESTORE_INTERVAL);
 
+            // Kick off any work that may be needed regarding app widget restores
+            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER);
+
             // done; we can finally release the wakelock
             Slog.i(TAG, "Restore complete.");
             mWakelock.release();
@@ -4896,43 +5128,92 @@
 
         // Call asynchronously into the app, passing it the restore data.  The next step
         // after this is always a callback, either operationComplete() or handleTimeout().
-        void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
-                boolean needFullBackup) {
+        void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
             mCurrentPackage = app;
+            mWidgetData = null;
             final String packageName = app.packageName;
 
             if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
 
             // !!! TODO: get the dirs from the transport
             mBackupDataName = new File(mDataDir, packageName + ".restore");
+            mStageName = new File(mDataDir, packageName + ".stage");
             mNewStateName = new File(mStateDir, packageName + ".new");
             mSavedStateName = new File(mStateDir, packageName);
 
+            // don't stage the 'android' package where the wallpaper data lives.  this is
+            // an optimization: we know there's no widget data hosted/published by that
+            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
+            // data following the download.
+            boolean staging = !packageName.equals("android");
+            ParcelFileDescriptor stage;
+            File downloadFile = (staging) ? mStageName : mBackupDataName;
+
             final int token = generateToken();
             try {
                 // Run the transport's restore pass
-                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
+                stage = ParcelFileDescriptor.open(downloadFile,
                             ParcelFileDescriptor.MODE_READ_WRITE |
                             ParcelFileDescriptor.MODE_CREATE |
                             ParcelFileDescriptor.MODE_TRUNCATE);
 
                 if (!SELinux.restorecon(mBackupDataName)) {
-                    Slog.e(TAG, "SElinux restorecon failed for " + mBackupDataName);
+                    Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
                 }
 
-                if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
+                if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) {
                     // Transport-level failure, so we wind everything up and
                     // terminate the restore operation.
                     Slog.e(TAG, "Error getting restore data for " + packageName);
                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
-                    mBackupData.close();
-                    mBackupDataName.delete();
+                    stage.close();
+                    downloadFile.delete();
                     executeNextState(RestoreState.FINAL);
                     return;
                 }
 
+                // We have the data from the transport. Now we extract and strip
+                // any per-package metadata (typically widget-related information)
+                // if appropriate
+                if (staging) {
+                    stage.close();
+                    stage = ParcelFileDescriptor.open(downloadFile,
+                            ParcelFileDescriptor.MODE_READ_ONLY);
+
+                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+
+                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
+                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
+                    byte[] buffer = new byte[8192]; // will grow when needed
+                    while (in.readNextHeader()) {
+                        final String key = in.getKey();
+                        final int size = in.getDataSize();
+
+                        // is this a special key?
+                        if (key.equals(KEY_WIDGET_STATE)) {
+                            if (DEBUG) {
+                                Slog.i(TAG, "Restoring widget state for " + packageName);
+                            }
+                            mWidgetData = new byte[size];
+                            in.readEntityData(mWidgetData, 0, size);
+                        } else {
+                            if (size > buffer.length) {
+                                buffer = new byte[size];
+                            }
+                            in.readEntityData(buffer, 0, size);
+                            out.writeEntityHeader(key, size);
+                            out.writeEntityData(buffer, size);
+                        }
+                    }
+
+                    mBackupData.close();
+                }
+
                 // Okay, we have the data.  Now have the agent do the restore.
-                mBackupData.close();
+                stage.close();
                 mBackupData = ParcelFileDescriptor.open(mBackupDataName,
                             ParcelFileDescriptor.MODE_READ_ONLY);
 
@@ -4943,7 +5224,8 @@
 
                 // Kick off the restore, checking for hung agents
                 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
-                agent.doRestore(mBackupData, appVersionCode, mNewState, token, mBackupManagerBinder);
+                agent.doRestore(mBackupData, appVersionCode, mNewState,
+                        token, mBackupManagerBinder);
             } catch (Exception e) {
                 Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
@@ -4966,6 +5248,7 @@
 
         void agentCleanup() {
             mBackupDataName.delete();
+            mStageName.delete();
             try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
             try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
             mBackupData = mNewState = null;
@@ -5024,9 +5307,17 @@
         public void operationComplete() {
             int size = (int) mBackupDataName.length();
             EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
+
             // Just go back to running the restore queue
             agentCleanup();
 
+            // If there was widget state associated with this app, get the OS to
+            // incorporate it into current bookeeping and then pass that along to
+            // the app as part of the restore operation.
+            if (mWidgetData != null) {
+                restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+            }
+
             executeNextState(RestoreState.RUNNING_QUEUE);
         }
 
@@ -5050,6 +5341,12 @@
         }
     }
 
+    // Used by both incremental and full restore
+    void restoreWidgetData(String packageName, byte[] widgetData) {
+        // Apply the restored widget state and generate the ID update for the app
+        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
+    }
+
     class PerformClearTask implements Runnable {
         IBackupTransport mTransport;
         PackageInfo mPackage;
@@ -5345,8 +5642,9 @@
     // Run a *full* backup pass for the given package, writing the resulting data stream
     // to the supplied file descriptor.  This method is synchronous and does not return
     // to the caller until the backup has been completed.
+    @Override
     public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
-            boolean includeObbs, boolean includeShared,
+            boolean includeObbs, boolean includeShared, boolean doWidgets,
             boolean doAllApps, boolean includeSystem, String[] pkgList) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
 
@@ -5382,7 +5680,7 @@
             Slog.i(TAG, "Beginning full backup...");
 
             FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
-                    includeShared, doAllApps, includeSystem, pkgList);
+                    includeShared, doWidgets, doAllApps, includeSystem, pkgList);
             final int token = generateToken();
             synchronized (mFullConfirmations) {
                 mFullConfirmations.put(token, params);
@@ -5839,7 +6137,7 @@
                 mWakelock.acquire();
                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                 msg.obj = new RestoreParams(transport, dirName, null,
-                        restoreSet, pkg, token, true);
+                        restoreSet, pkg, token);
                 mBackupHandler.sendMessage(msg);
             } catch (RemoteException e) {
                 // Binding to the transport broke; back off and proceed with the installation.
@@ -6020,7 +6318,7 @@
                         mWakelock.acquire();
                         Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                         msg.obj = new RestoreParams(mRestoreTransport, dirName,
-                                observer, token, true);
+                                observer, token);
                         mBackupHandler.sendMessage(msg);
                         Binder.restoreCallingIdentity(oldId);
                         return 0;
@@ -6032,6 +6330,7 @@
             return -1;
         }
 
+        // Restores of more than a single package are treated as 'system' restores
         public synchronized int restoreSome(long token, IRestoreObserver observer,
                 String[] packages) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -6090,7 +6389,7 @@
                         mWakelock.acquire();
                         Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                         msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
-                                packages, true);
+                                packages, packages.length > 1);
                         mBackupHandler.sendMessage(msg);
                         Binder.restoreCallingIdentity(oldId);
                         return 0;
@@ -6169,7 +6468,7 @@
             mWakelock.acquire();
             Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
             msg.obj = new RestoreParams(mRestoreTransport, dirName,
-                    observer, token, app, 0, false);
+                    observer, token, app, 0);
             mBackupHandler.sendMessage(msg);
             Binder.restoreCallingIdentity(oldId);
             return 0;