Merge "AudioEffect types in SDK" into jb-mr2-dev
diff --git a/Android.mk b/Android.mk
index 8090448..2ad7a72 100644
--- a/Android.mk
+++ b/Android.mk
@@ -195,7 +195,6 @@
 	core/java/com/android/internal/view/IInputContext.aidl \
 	core/java/com/android/internal/view/IInputContextCallback.aidl \
 	core/java/com/android/internal/view/IInputMethod.aidl \
-	core/java/com/android/internal/view/IInputMethodCallback.aidl \
 	core/java/com/android/internal/view/IInputMethodClient.aidl \
 	core/java/com/android/internal/view/IInputMethodManager.aidl \
 	core/java/com/android/internal/view/IInputMethodSession.aidl \
@@ -307,7 +306,6 @@
 	frameworks/base/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputContext.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputMethod.aidl \
-	frameworks/base/core/java/com/android/internal/view/IInputMethodCallback.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputMethodClient.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputMethodManager.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputMethodSession.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e715a9f..fc63866 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -157,6 +157,8 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/SmsRawData.*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodSession.*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/api/current.txt b/api/current.txt
index a2f1e63..5ab97ea 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -339,8 +339,10 @@
     field public static final int checkedTextViewStyle = 16843720; // 0x10103c8
     field public static final int childDivider = 16843025; // 0x1010111
     field public static final int childIndicator = 16843020; // 0x101010c
+    field public static final int childIndicatorEnd = 16843732; // 0x10103d4
     field public static final int childIndicatorLeft = 16843023; // 0x101010f
     field public static final int childIndicatorRight = 16843024; // 0x1010110
+    field public static final int childIndicatorStart = 16843731; // 0x10103d3
     field public static final int choiceMode = 16843051; // 0x101012b
     field public static final int clearTaskOnLaunch = 16842773; // 0x1010015
     field public static final int clickable = 16842981; // 0x10100e5
@@ -574,8 +576,10 @@
     field public static final int indeterminateDuration = 16843069; // 0x101013d
     field public static final int indeterminateOnly = 16843066; // 0x101013a
     field public static final int indeterminateProgressStyle = 16843544; // 0x1010318
+    field public static final int indicatorEnd = 16843730; // 0x10103d2
     field public static final int indicatorLeft = 16843021; // 0x101010d
     field public static final int indicatorRight = 16843022; // 0x101010e
+    field public static final int indicatorStart = 16843729; // 0x10103d1
     field public static final int inflatedId = 16842995; // 0x10100f3
     field public static final int initOrder = 16842778; // 0x101001a
     field public static final int initialKeyguardLayout = 16843714; // 0x10103c2
@@ -4333,6 +4337,7 @@
     method public boolean hasGrantedPolicy(android.content.ComponentName, int);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
+    method public boolean isDeviceOwner(java.lang.String);
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean resetPassword(java.lang.String, int);
@@ -16792,9 +16797,20 @@
     method public int getUserCount();
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
+    method public android.os.Bundle getUserRestrictions();
+    method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean isUserAGoat();
+    method public boolean isUserRestricted();
     method public boolean isUserRunning(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
+    method public void setUserRestriction(java.lang.String, boolean);
+    method public void setUserRestrictions(android.os.Bundle);
+    method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
+    field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
+    field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
+    field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
+    field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
+    field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
   }
 
   public abstract class Vibrator {
@@ -17927,6 +17943,13 @@
     field public static final java.lang.String TYPE = "data2";
   }
 
+  public static final class ContactsContract.CommonDataKinds.Contactables implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+    ctor public ContactsContract.CommonDataKinds.Contactables();
+    field public static final android.net.Uri CONTENT_FILTER_URI;
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
+  }
+
   public static final class ContactsContract.CommonDataKinds.Email implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
@@ -18220,6 +18243,7 @@
     method public static android.net.Uri getContactLookupUri(android.content.ContentResolver, android.net.Uri);
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/data";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
   }
 
   protected static abstract interface ContactsContract.DataColumns {
@@ -28624,8 +28648,10 @@
     method public void setChildDivider(android.graphics.drawable.Drawable);
     method public void setChildIndicator(android.graphics.drawable.Drawable);
     method public void setChildIndicatorBounds(int, int);
+    method public void setChildIndicatorBoundsRelative(int, int);
     method public void setGroupIndicator(android.graphics.drawable.Drawable);
     method public void setIndicatorBounds(int, int);
+    method public void setIndicatorBoundsRelative(int, int);
     method public void setOnChildClickListener(android.widget.ExpandableListView.OnChildClickListener);
     method public void setOnGroupClickListener(android.widget.ExpandableListView.OnGroupClickListener);
     method public void setOnGroupCollapseListener(android.widget.ExpandableListView.OnGroupCollapseListener);
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index f48764f..31eba96 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -19,6 +19,7 @@
 package com.android.commands.wm;
 
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -87,9 +88,22 @@
     }
 
     private void runDisplaySize() throws Exception {
-        String size = nextArgRequired();
+        String size = nextArg();
         int w, h;
-        if ("reset".equals(size)) {
+        if (size == null) {
+            Point initialSize = new Point();
+            Point baseSize = new Point();
+            try {
+                mWm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
+                mWm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
+                System.out.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+                if (!initialSize.equals(baseSize)) {
+                    System.out.println("Override size: " + baseSize.x + "x" + baseSize.y);
+                }
+            } catch (RemoteException e) {
+            }
+            return;
+        } else if ("reset".equals(size)) {
             w = h = -1;
         } else {
             int div = size.indexOf('x');
@@ -120,9 +134,20 @@
     }
 
     private void runDisplayDensity() throws Exception {
-        String densityStr = nextArgRequired();
+        String densityStr = nextArg();
         int density;
-        if ("reset".equals(densityStr)) {
+        if (densityStr == null) {
+            try {
+                int initialDensity = mWm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
+                int baseDensity = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+                System.out.println("Physical density: " + initialDensity);
+                if (initialDensity != baseDensity) {
+                    System.out.println("Override density: " + baseDensity);
+                }
+            } catch (RemoteException e) {
+            }
+            return;
+        } else if ("reset".equals(densityStr)) {
             density = -1;
         } else {
             try {
@@ -231,7 +256,7 @@
                 "       wm density [reset|DENSITY]\n" +
                 "       wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
                 "\n" +
-                "wm size: override display size.\n" +
+                "wm size: return or override display size.\n" +
                 "\n" +
                 "wm density: override display density.\n" +
                 "\n" +
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 26dc60d..165c3db 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -461,39 +461,63 @@
         }
 
         /**
-         * Set the local destination for the downloaded file to a path within the application's
-         * external files directory (as returned by {@link Context#getExternalFilesDir(String)}.
+         * Set the local destination for the downloaded file to a path within
+         * the application's external files directory (as returned by
+         * {@link Context#getExternalFilesDir(String)}.
          * <p>
-         * The downloaded file is not scanned by MediaScanner.
-         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
+         * The downloaded file is not scanned by MediaScanner. But it can be
+         * made scannable by calling {@link #allowScanningByMediaScanner()}.
          *
-         * @param context the {@link Context} to use in determining the external files directory
-         * @param dirType the directory type to pass to {@link Context#getExternalFilesDir(String)}
-         * @param subPath the path within the external directory, including the destination filename
+         * @param context the {@link Context} to use in determining the external
+         *            files directory
+         * @param dirType the directory type to pass to
+         *            {@link Context#getExternalFilesDir(String)}
+         * @param subPath the path within the external directory, including the
+         *            destination filename
          * @return this object
+         * @throws IllegalStateException If the external storage directory
+         *             cannot be found or created.
          */
         public Request setDestinationInExternalFilesDir(Context context, String dirType,
                 String subPath) {
-            setDestinationFromBase(context.getExternalFilesDir(dirType), subPath);
+            final File file = context.getExternalFilesDir(dirType);
+            if (file == null) {
+                throw new IllegalStateException("Failed to get external storage files directory");
+            } else if (file.exists()) {
+                if (!file.isDirectory()) {
+                    throw new IllegalStateException(file.getAbsolutePath() +
+                            " already exists and is not a directory");
+                }
+            } else {
+                if (!file.mkdirs()) {
+                    throw new IllegalStateException("Unable to create directory: "+
+                            file.getAbsolutePath());
+                }
+            }
+            setDestinationFromBase(file, subPath);
             return this;
         }
 
         /**
-         * Set the local destination for the downloaded file to a path within the public external
-         * storage directory (as returned by
-         * {@link Environment#getExternalStoragePublicDirectory(String)}.
-         *<p>
-         * The downloaded file is not scanned by MediaScanner.
-         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
+         * Set the local destination for the downloaded file to a path within
+         * the public external storage directory (as returned by
+         * {@link Environment#getExternalStoragePublicDirectory(String)}).
+         * <p>
+         * The downloaded file is not scanned by MediaScanner. But it can be
+         * made scannable by calling {@link #allowScanningByMediaScanner()}.
          *
-         * @param dirType the directory type to pass to
-         *        {@link Environment#getExternalStoragePublicDirectory(String)}
-         * @param subPath the path within the external directory, including the destination filename
+         * @param dirType the directory type to pass to {@link Environment#getExternalStoragePublicDirectory(String)}
+         * @param subPath the path within the external directory, including the
+         *            destination filename
          * @return this object
+         * @throws IllegalStateException If the external storage directory
+         *             cannot be found or created.
          */
         public Request setDestinationInExternalPublicDir(String dirType, String subPath) {
             File file = Environment.getExternalStoragePublicDirectory(dirType);
-            if (file.exists()) {
+            if (file == null) {
+                throw new IllegalStateException("Failed to get external storage public directory");
+            } else if (file.exists()) {
                 if (!file.isDirectory()) {
                     throw new IllegalStateException(file.getAbsolutePath() +
                             " already exists and is not a directory");
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 396f910..51867bc 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -353,8 +353,7 @@
     }
     
     void preDispatchKeyEvent(KeyEvent event, int seq) {
-        mIMM.dispatchKeyEvent(this, seq, event,
-                mInputMethodCallback);
+        mIMM.dispatchInputEvent(this, seq, event, mInputMethodCallback);
     }
 
     void setWindowFlags(int flags, int mask) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4c0eba0..8284b2c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1513,4 +1513,57 @@
             }
         }
     }
+
+    /**
+     * @hide
+     * Sets the given package as the device owner. The package must already be installed and there
+     * shouldn't be an existing device owner registered, for this call to succeed. Also, this
+     * method must be called before the device is provisioned.
+     * @param packageName the package name of the application to be registered as the device owner.
+     * @return whether the package was successfully registered as the device owner.
+     * @throws IllegalArgumentException if the package name is null or invalid
+     * @throws IllegalStateException if a device owner is already registered or the device has
+     *         already been provisioned.
+     */
+    public boolean setDeviceOwner(String packageName) throws IllegalArgumentException,
+            IllegalStateException {
+        if (mService != null) {
+            try {
+                return mService.setDeviceOwner(packageName);
+            } catch (RemoteException re) {
+                Log.w(TAG, "Failed to set device owner");
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Used to determine if a particular package has been registered as a Device Owner admin.
+     * Device Owner admins cannot be deactivated by the user unless the Device Owner itself allows
+     * it. And Device Owner packages cannot be uninstalled, once registered.
+     * @param packageName the package name to check against the registered device owner.
+     * @return whether or not the package is registered as the Device Owner.
+     */
+    public boolean isDeviceOwner(String packageName) {
+        if (mService != null) {
+            try {
+                return mService.isDeviceOwner(packageName);
+            } catch (RemoteException re) {
+                Log.w(TAG, "Failed to check device owner");
+            }
+        }
+        return false;
+    }
+
+    /** @hide */
+    public String getDeviceOwner() {
+        if (mService != null) {
+            try {
+                return mService.getDeviceOwner();
+            } catch (RemoteException re) {
+                Log.w(TAG, "Failed to get device owner");
+            }
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e061ab3..b2a65bf 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -97,4 +97,8 @@
         int numbers, int symbols, int nonletter, int userHandle);
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
+
+    boolean setDeviceOwner(String packageName);
+    boolean isDeviceOwner(String packageName);
+    String getDeviceOwner();
 }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index e68d23a..1166e4b 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -49,8 +49,8 @@
     static final String TAG = "AppWidgetManager";
 
     /**
-     * Send this from your {@link AppWidgetHost} activity when you want to pick an AppWidget to display.
-     * The AppWidget picker activity will be launched.
+     * Activity action to launch from your {@link AppWidgetHost} activity when you want to
+     * pick an AppWidget to display.  The AppWidget picker activity will be launched.
      * <p>
      * You must supply the following extras:
      * <table>
@@ -89,8 +89,8 @@
             ACTION_KEYGUARD_APPWIDGET_PICK = "android.appwidget.action.KEYGUARD_APPWIDGET_PICK";
 
     /**
-     * Send this from your {@link AppWidgetHost} activity when you want to bind an AppWidget to
-     * display and bindAppWidgetIdIfAllowed returns false.
+     * Activity action to launch from your {@link AppWidgetHost} activity when you want to bind
+     * an AppWidget to display and bindAppWidgetIdIfAllowed returns false.
      * <p>
      * You must supply the following extras:
      * <table>
@@ -269,6 +269,9 @@
     /**
      * Sent when the custom extras for an AppWidget change.
      *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
      * @see AppWidgetProvider#onAppWidgetOptionsChanged
      *      AppWidgetProvider.onAppWidgetOptionsChanged(Context context,
      *      AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras)
@@ -278,6 +281,9 @@
     /**
      * Sent when an instance of an AppWidget is deleted from its host.
      *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
      * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds)
      */
     public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
@@ -285,6 +291,9 @@
     /**
      * Sent when an instance of an AppWidget is removed from the last host.
      *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
      */
     public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
@@ -294,6 +303,9 @@
      * This broadcast is sent at boot time if there is a AppWidgetHost installed with
      * an instance for this provider.
      *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
      */
     public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
diff --git a/core/java/android/content/UriMatcher.java b/core/java/android/content/UriMatcher.java
index 841c8f4..1a8ea47 100644
--- a/core/java/android/content/UriMatcher.java
+++ b/core/java/android/content/UriMatcher.java
@@ -69,6 +69,11 @@
         sURIMatcher.addURI("call_log", "calls/#", CALLS_ID);
     }
 </pre>
+<p>Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, paths can start
+ with a leading slash.  For example:
+<pre>
+        sURIMatcher.addURI("contacts", "/people", PEOPLE);
+</pre>
 <p>Then when you need to match against a URI, call {@link #match}, providing
 the URL that you have been given.  You can use the result to build a query,
 return a type, insert or delete a row, or whatever you need, without duplicating
@@ -143,6 +148,9 @@
      * matched. URI nodes may be exact match string, the token "*"
      * that matches any text, or the token "#" that matches only
      * numbers.
+     * <p>
+     * Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
+     * this method will accept leading slash in the path.
      *
      * @param authority the authority to match
      * @param path the path to match. * may be used as a wild card for
@@ -155,7 +163,17 @@
         if (code < 0) {
             throw new IllegalArgumentException("code " + code + " is invalid: it must be positive");
         }
-        String[] tokens = path != null ? PATH_SPLIT_PATTERN.split(path) : null;
+
+        String[] tokens = null;
+        if (path != null) {
+            String newPath = path;
+            // Strip leading slash if present.
+            if (path.length() > 0 && path.charAt(0) == '/') {
+                newPath = path.substring(1);
+            }
+            tokens = PATH_SPLIT_PATTERN.split(newPath);
+        }
+
         int numTokens = tokens != null ? tokens.length : 0;
         UriMatcher node = this;
         for (int i = -1; i < numTokens; i++) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 32cc7fd..02401dc 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -398,6 +398,15 @@
     public String[] resourceDirs;
 
     /**
+     * String retrieved from the seinfo tag found in selinux policy. This value
+     * is useful in setting an SELinux security context on the process as well
+     * as its data directory.
+     *
+     * {@hide}
+     */
+    public String seinfo;
+
+    /**
      * Paths to all shared libraries this application is linked against.  This
      * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
      * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
@@ -477,6 +486,9 @@
         if (resourceDirs != null) {
             pw.println(prefix + "resourceDirs=" + resourceDirs);
         }
+        if (seinfo != null) {
+            pw.println(prefix + "seinfo=" + seinfo);
+        }
         pw.println(prefix + "dataDir=" + dataDir);
         if (sharedLibraryFiles != null) {
             pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
@@ -544,6 +556,7 @@
         publicSourceDir = orig.publicSourceDir;
         nativeLibraryDir = orig.nativeLibraryDir;
         resourceDirs = orig.resourceDirs;
+        seinfo = orig.seinfo;
         sharedLibraryFiles = orig.sharedLibraryFiles;
         dataDir = orig.dataDir;
         uid = orig.uid;
@@ -583,6 +596,7 @@
         dest.writeString(publicSourceDir);
         dest.writeString(nativeLibraryDir);
         dest.writeStringArray(resourceDirs);
+        dest.writeString(seinfo);
         dest.writeStringArray(sharedLibraryFiles);
         dest.writeString(dataDir);
         dest.writeInt(uid);
@@ -621,6 +635,7 @@
         publicSourceDir = source.readString();
         nativeLibraryDir = source.readString();
         resourceDirs = source.readStringArray();
+        seinfo = source.readString();
         sharedLibraryFiles = source.readStringArray();
         dataDir = source.readString();
         uid = source.readInt();
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3c3182a..3531926 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -126,11 +126,12 @@
             mRevoked = true;
             mEnabled = false;
         }
-        
+
         /**
          * Take care of dispatching incoming key events to the appropriate
          * callbacks on the service, and tell the client when this is done.
          */
+        @Override
         public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
             boolean handled = event.dispatch(AbstractInputMethodService.this,
                     mDispatcherState, this);
@@ -143,6 +144,7 @@
          * Take care of dispatching incoming trackball events to the appropriate
          * callbacks on the service, and tell the client when this is done.
          */
+        @Override
         public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) {
             boolean handled = onTrackballEvent(event);
             if (callback != null) {
@@ -154,6 +156,7 @@
          * Take care of dispatching incoming generic motion events to the appropriate
          * callbacks on the service, and tell the client when this is done.
          */
+        @Override
         public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) {
             boolean handled = onGenericMotionEvent(event);
             if (callback != null) {
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index d78262b..726dcec 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -18,15 +18,20 @@
 
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodSession;
 
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.SparseArray;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.inputmethod.CompletionInfo;
@@ -36,14 +41,10 @@
 class IInputMethodSessionWrapper extends IInputMethodSession.Stub
         implements HandlerCaller.Callback {
     private static final String TAG = "InputMethodWrapper";
-    private static final boolean DEBUG = false;
     
     private static final int DO_FINISH_INPUT = 60;
     private static final int DO_DISPLAY_COMPLETIONS = 65;
     private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
-    private static final int DO_DISPATCH_KEY_EVENT = 70;
-    private static final int DO_DISPATCH_TRACKBALL_EVENT = 80;
-    private static final int DO_DISPATCH_GENERIC_MOTION_EVENT = 85;
     private static final int DO_UPDATE_SELECTION = 90;
     private static final int DO_UPDATE_CURSOR = 95;
     private static final int DO_APP_PRIVATE_COMMAND = 100;
@@ -53,34 +54,30 @@
 
     HandlerCaller mCaller;
     InputMethodSession mInputMethodSession;
-    
-    // NOTE: we should have a cache of these.
-    static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
-        final IInputMethodCallback mCb;
-        InputMethodEventCallbackWrapper(IInputMethodCallback cb) {
-            mCb = cb;
-        }
-        public void finishedEvent(int seq, boolean handled) {
-            try {
-                mCb.finishedEvent(seq, handled);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-    
+    InputChannel mChannel;
+    ImeInputEventReceiver mReceiver;
+
     public IInputMethodSessionWrapper(Context context,
-            InputMethodSession inputMethodSession) {
+            InputMethodSession inputMethodSession, InputChannel channel) {
         mCaller = new HandlerCaller(context, null,
                 this, true /*asyncHandler*/);
         mInputMethodSession = inputMethodSession;
+        mChannel = channel;
+        if (channel != null) {
+            mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper());
+        }
     }
 
     public InputMethodSession getInternalInputMethodSession() {
         return mInputMethodSession;
     }
 
+    @Override
     public void executeMessage(Message msg) {
-        if (mInputMethodSession == null) return;
+        if (mInputMethodSession == null) {
+            // The session has been finished.
+            return;
+        }
 
         switch (msg.what) {
             case DO_FINISH_INPUT:
@@ -93,33 +90,6 @@
                 mInputMethodSession.updateExtractedText(msg.arg1,
                         (ExtractedText)msg.obj);
                 return;
-            case DO_DISPATCH_KEY_EVENT: {
-                SomeArgs args = (SomeArgs)msg.obj;
-                mInputMethodSession.dispatchKeyEvent(msg.arg1,
-                        (KeyEvent)args.arg1,
-                        new InputMethodEventCallbackWrapper(
-                                (IInputMethodCallback)args.arg2));
-                args.recycle();
-                return;
-            }
-            case DO_DISPATCH_TRACKBALL_EVENT: {
-                SomeArgs args = (SomeArgs)msg.obj;
-                mInputMethodSession.dispatchTrackballEvent(msg.arg1,
-                        (MotionEvent)args.arg1,
-                        new InputMethodEventCallbackWrapper(
-                                (IInputMethodCallback)args.arg2));
-                args.recycle();
-                return;
-            }
-            case DO_DISPATCH_GENERIC_MOTION_EVENT: {
-                SomeArgs args = (SomeArgs)msg.obj;
-                mInputMethodSession.dispatchGenericMotionEvent(msg.arg1,
-                        (MotionEvent)args.arg1,
-                        new InputMethodEventCallbackWrapper(
-                                (IInputMethodCallback)args.arg2));
-                args.recycle();
-                return;
-            }
             case DO_UPDATE_SELECTION: {
                 SomeArgs args = (SomeArgs)msg.obj;
                 mInputMethodSession.updateSelection(args.argi1, args.argi2,
@@ -143,7 +113,7 @@
                 return;
             }
             case DO_FINISH_SESSION: {
-                mInputMethodSession = null;
+                doFinishSession();
                 return;
             }
             case DO_VIEW_CLICKED: {
@@ -153,37 +123,37 @@
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
-    
+
+    private void doFinishSession() {
+        mInputMethodSession = null;
+        if (mReceiver != null) {
+            mReceiver.dispose();
+            mReceiver = null;
+        }
+        if (mChannel != null) {
+            mChannel.dispose();
+            mChannel = null;
+        }
+    }
+
+    @Override
     public void finishInput() {
         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
     }
 
+    @Override
     public void displayCompletions(CompletionInfo[] completions) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(
                 DO_DISPLAY_COMPLETIONS, completions));
     }
-    
+
+    @Override
     public void updateExtractedText(int token, ExtractedText text) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
                 DO_UPDATE_EXTRACTED_TEXT, token, text));
     }
-    
-    public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_KEY_EVENT, seq,
-                event, callback));
-    }
 
-    public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_TRACKBALL_EVENT, seq,
-                event, callback));
-    }
-
-    public void dispatchGenericMotionEvent(int seq, MotionEvent event,
-            IInputMethodCallback callback) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_GENERIC_MOTION_EVENT, seq,
-                event, callback));
-    }
-
+    @Override
     public void updateSelection(int oldSelStart, int oldSelEnd,
             int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
@@ -191,24 +161,74 @@
                 candidatesStart, candidatesEnd));
     }
 
+    @Override
     public void viewClicked(boolean focusChanged) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
     }
 
+    @Override
     public void updateCursor(Rect newCursor) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR,
-                newCursor));
-    }
-    
-    public void appPrivateCommand(String action, Bundle data) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
-    }
-    
-    public void toggleSoftInput(int showFlags, int hideFlags) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor));
     }
 
+    @Override
+    public void appPrivateCommand(String action, Bundle data) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
+    }
+
+    @Override
+    public void toggleSoftInput(int showFlags, int hideFlags) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
+    }
+
+    @Override
     public void finishSession() {
         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
     }
+
+    private final class ImeInputEventReceiver extends InputEventReceiver
+            implements InputMethodSession.EventCallback {
+        private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>();
+
+        public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) {
+            super(inputChannel, looper);
+        }
+
+        @Override
+        public void onInputEvent(InputEvent event) {
+            if (mInputMethodSession == null) {
+                // The session has been finished.
+                finishInputEvent(event, false);
+                return;
+            }
+
+            final int seq = event.getSequenceNumber();
+            mPendingEvents.put(seq, event);
+            if (event instanceof KeyEvent) {
+                KeyEvent keyEvent = (KeyEvent)event;
+                mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this);
+            } else {
+                MotionEvent motionEvent = (MotionEvent)event;
+                if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
+                    mInputMethodSession.dispatchTrackballEvent(seq, motionEvent, this);
+                } else {
+                    mInputMethodSession.dispatchGenericMotionEvent(seq, motionEvent, this);
+                }
+            }
+        }
+
+        @Override
+        public void finishedEvent(int seq, boolean handled) {
+            int index = mPendingEvents.indexOfKey(seq);
+            if (index >= 0) {
+                InputEvent event = mPendingEvents.valueAt(index);
+                mPendingEvents.removeAt(index);
+                finishInputEvent(event, handled);
+            }
+        }
+    }
 }
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 0d981be..9306373 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -32,6 +32,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.util.Log;
+import android.view.InputChannel;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
@@ -53,7 +54,6 @@
 class IInputMethodWrapper extends IInputMethod.Stub
         implements HandlerCaller.Callback {
     private static final String TAG = "InputMethodWrapper";
-    private static final boolean DEBUG = false;
 
     private static final int DO_DUMP = 1;
     private static final int DO_ATTACH_TOKEN = 10;
@@ -78,20 +78,29 @@
     }
     
     // NOTE: we should have a cache of these.
-    static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
+    static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
         final Context mContext;
+        final InputChannel mChannel;
         final IInputSessionCallback mCb;
-        InputMethodSessionCallbackWrapper(Context context, IInputSessionCallback cb) {
+
+        InputMethodSessionCallbackWrapper(Context context, InputChannel channel,
+                IInputSessionCallback cb) {
             mContext = context;
+            mChannel = channel;
             mCb = cb;
         }
+
+        @Override
         public void sessionCreated(InputMethodSession session) {
             try {
                 if (session != null) {
                     IInputMethodSessionWrapper wrap =
-                            new IInputMethodSessionWrapper(mContext, session);
+                            new IInputMethodSessionWrapper(mContext, session, mChannel);
                     mCb.sessionCreated(wrap);
                 } else {
+                    if (mChannel != null) {
+                        mChannel.dispose();
+                    }
                     mCb.sessionCreated(null);
                 }
             } catch (RemoteException e) {
@@ -112,6 +121,7 @@
         return mInputMethod.get();
     }
 
+    @Override
     public void executeMessage(Message msg) {
         InputMethod inputMethod = mInputMethod.get();
         // Need a valid reference to the inputMethod for everything except a dump.
@@ -174,8 +184,11 @@
                 return;
             }
             case DO_CREATE_SESSION: {
+                SomeArgs args = (SomeArgs)msg.obj;
                 inputMethod.createSession(new InputMethodSessionCallbackWrapper(
-                        mCaller.mContext, (IInputSessionCallback)msg.obj));
+                        mCaller.mContext, (InputChannel)args.arg1,
+                        (IInputSessionCallback)args.arg2));
+                args.recycle();
                 return;
             }
             case DO_SET_SESSION_ENABLED:
@@ -197,8 +210,9 @@
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
-    
-    @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         AbstractInputMethodService target = mTarget.get();
         if (target == null) {
             return;
@@ -224,10 +238,12 @@
         }
     }
 
+    @Override
     public void attachToken(IBinder token) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
     }
-    
+
+    @Override
     public void bindInput(InputBinding binding) {
         InputConnection ic = new InputConnectionWrapper(
                 IInputContext.Stub.asInterface(binding.getConnectionToken()));
@@ -235,24 +251,30 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
     }
 
+    @Override
     public void unbindInput() {
         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
     }
 
+    @Override
     public void startInput(IInputContext inputContext, EditorInfo attribute) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
                 inputContext, attribute));
     }
 
+    @Override
     public void restartInput(IInputContext inputContext, EditorInfo attribute) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT,
                 inputContext, attribute));
     }
 
-    public void createSession(IInputSessionCallback callback) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback));
+    @Override
+    public void createSession(InputChannel channel, IInputSessionCallback callback) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
+                channel, callback));
     }
 
+    @Override
     public void setSessionEnabled(IInputMethodSession session, boolean enabled) {
         try {
             InputMethodSession ls = ((IInputMethodSessionWrapper)
@@ -263,7 +285,8 @@
             Log.w(TAG, "Incoming session not of correct type: " + session, e);
         }
     }
-    
+
+    @Override
     public void revokeSession(IInputMethodSession session) {
         try {
             InputMethodSession ls = ((IInputMethodSessionWrapper)
@@ -273,17 +296,20 @@
             Log.w(TAG, "Incoming session not of correct type: " + session, e);
         }
     }
-    
+
+    @Override
     public void showSoftInput(int flags, ResultReceiver resultReceiver) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
                 flags, resultReceiver));
     }
-    
+
+    @Override
     public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
                 flags, resultReceiver));
     }
 
+    @Override
     public void changeInputMethodSubtype(InputMethodSubtype subtype) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
                 subtype));
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index ce1276f..786439e5 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -119,6 +119,8 @@
      * Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
      * used internally by system services like {@link DownloadManager} when
      * performing traffic on behalf of an application.
+     *
+     * @see #clearThreadStatsTag()
      */
     public static void setThreadStatsTag(int tag) {
         NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
@@ -128,11 +130,19 @@
      * Get the active tag used when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * {@link #tagSocket(Socket)}.
+     *
+     * @see #setThreadStatsTag(int)
      */
     public static int getThreadStatsTag() {
         return NetworkManagementSocketTagger.getThreadSocketStatsTag();
     }
 
+    /**
+     * Clear any active tag set to account {@link Socket} traffic originating
+     * from the current thread.
+     *
+     * @see #setThreadStatsTag(int)
+     */
     public static void clearThreadStatsTag() {
         NetworkManagementSocketTagger.setThreadSocketStatsTag(-1);
     }
@@ -148,7 +158,7 @@
      * To take effect, caller must hold
      * {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
      *
-     * {@hide}
+     * @hide
      */
     public static void setThreadStatsUid(int uid) {
         NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
@@ -260,10 +270,13 @@
     }
 
     /**
-     * Get the total number of packets transmitted through the mobile interface.
-     *
-     * @return number of packets.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of packets transmitted across mobile networks since device
+     * boot. Counts packets across all mobile network interfaces, and always
+     * increases monotonically since device boot. Statistics are measured at the
+     * network layer, so they include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getMobileTxPackets() {
         long total = 0;
@@ -274,10 +287,13 @@
     }
 
     /**
-     * Get the total number of packets received through the mobile interface.
-     *
-     * @return number of packets.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of packets received across mobile networks since device
+     * boot. Counts packets across all mobile network interfaces, and always
+     * increases monotonically since device boot. Statistics are measured at the
+     * network layer, so they include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getMobileRxPackets() {
         long total = 0;
@@ -288,10 +304,13 @@
     }
 
     /**
-     * Get the total number of bytes transmitted through the mobile interface.
-     *
-     * @return number of bytes.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of bytes transmitted across mobile networks since device
+     * boot. Counts packets across all mobile network interfaces, and always
+     * increases monotonically since device boot. Statistics are measured at the
+     * network layer, so they include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getMobileTxBytes() {
         long total = 0;
@@ -302,10 +321,13 @@
     }
 
     /**
-     * Get the total number of bytes received through the mobile interface.
-     *
-     * @return number of bytes.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of bytes received across mobile networks since device boot.
+     * Counts packets across all mobile network interfaces, and always increases
+     * monotonically since device boot. Statistics are measured at the network
+     * layer, so they include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getMobileRxBytes() {
         long total = 0;
@@ -339,85 +361,73 @@
         return total;
     }
 
-    /**
-     * Get the total number of packets transmitted through the specified interface.
-     *
-     * @return number of packets.  If the statistics are not supported by this interface,
-     * {@link #UNSUPPORTED} will be returned.
-     * @hide
-     */
+    /** {@hide} */
     public static long getTxPackets(String iface) {
         return nativeGetIfaceStat(iface, TYPE_TX_PACKETS);
     }
 
-    /**
-     * Get the total number of packets received through the specified interface.
-     *
-     * @return number of packets.  If the statistics are not supported by this interface,
-     * {@link #UNSUPPORTED} will be returned.
-     * @hide
-     */
+    /** {@hide} */
     public static long getRxPackets(String iface) {
         return nativeGetIfaceStat(iface, TYPE_RX_PACKETS);
     }
 
-    /**
-     * Get the total number of bytes transmitted through the specified interface.
-     *
-     * @return number of bytes.  If the statistics are not supported by this interface,
-     * {@link #UNSUPPORTED} will be returned.
-     * @hide
-     */
+    /** {@hide} */
     public static long getTxBytes(String iface) {
         return nativeGetIfaceStat(iface, TYPE_TX_BYTES);
     }
 
-    /**
-     * Get the total number of bytes received through the specified interface.
-     *
-     * @return number of bytes.  If the statistics are not supported by this interface,
-     * {@link #UNSUPPORTED} will be returned.
-     * @hide
-     */
+    /** {@hide} */
     public static long getRxBytes(String iface) {
         return nativeGetIfaceStat(iface, TYPE_RX_BYTES);
     }
 
     /**
-     * Get the total number of packets sent through all network interfaces.
-     *
-     * @return the number of packets.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of packets transmitted since device boot. Counts packets
+     * across all network interfaces, and always increases monotonically since
+     * device boot. Statistics are measured at the network layer, so they
+     * include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getTotalTxPackets() {
         return nativeGetTotalStat(TYPE_TX_PACKETS);
     }
 
     /**
-     * Get the total number of packets received through all network interfaces.
-     *
-     * @return number of packets.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of packets received since device boot. Counts packets
+     * across all network interfaces, and always increases monotonically since
+     * device boot. Statistics are measured at the network layer, so they
+     * include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getTotalRxPackets() {
         return nativeGetTotalStat(TYPE_RX_PACKETS);
     }
 
     /**
-     * Get the total number of bytes sent through all network interfaces.
-     *
-     * @return number of bytes.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of bytes transmitted since device boot. Counts packets
+     * across all network interfaces, and always increases monotonically since
+     * device boot. Statistics are measured at the network layer, so they
+     * include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getTotalTxBytes() {
         return nativeGetTotalStat(TYPE_TX_BYTES);
     }
 
     /**
-     * Get the total number of bytes received through all network interfaces.
-     *
-     * @return number of bytes.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of bytes received since device boot. Counts packets across
+     * all network interfaces, and always increases monotonically since device
+     * boot. Statistics are measured at the network layer, so they include both
+     * TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getTotalRxBytes() {
         return nativeGetTotalStat(TYPE_RX_BYTES);
@@ -580,6 +590,7 @@
      * special permission.
      */
     private static NetworkStats getDataLayerSnapshotForUid(Context context) {
+        // TODO: take snapshot locally, since proc file is now visible
         final int uid = android.os.Process.myUid();
         try {
             return getStatsService().getDataLayerSnapshotForUid(uid);
diff --git a/core/java/com/android/internal/view/IInputMethodCallback.aidl b/core/java/android/nfc/BeamShareData.aidl
similarity index 62%
rename from core/java/com/android/internal/view/IInputMethodCallback.aidl
rename to core/java/android/nfc/BeamShareData.aidl
index 717a82d..a47e240 100644
--- a/core/java/com/android/internal/view/IInputMethodCallback.aidl
+++ b/core/java/android/nfc/BeamShareData.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,13 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.internal.view;
+package android.nfc;
 
-/**
- * Helper interface for IInputMethod to allow the input method to call back
- * to its client with results from incoming calls.
- * {@hide}
- */
-oneway interface IInputMethodCallback {
-    void finishedEvent(int seq, boolean handled);
-}
+parcelable BeamShareData;
diff --git a/core/java/android/nfc/BeamShareData.java b/core/java/android/nfc/BeamShareData.java
new file mode 100644
index 0000000..c30ba14
--- /dev/null
+++ b/core/java/android/nfc/BeamShareData.java
@@ -0,0 +1,62 @@
+package android.nfc;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class to IPC data to be shared over Android Beam.
+ * Allows bundling NdefMessage, Uris and flags in a single
+ * IPC call. This is important as we want to reduce the
+ * amount of IPC calls at "touch time".
+ * @hide
+ */
+public final class BeamShareData implements Parcelable {
+    public final NdefMessage ndefMessage;
+    public final Uri[] uris;
+    public final int flags;
+
+    public BeamShareData(NdefMessage msg, Uri[] uris, int flags) {
+        this.ndefMessage = msg;
+        this.uris = uris;
+        this.flags = flags;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        int urisLength = (uris != null) ? uris.length : 0;
+        dest.writeParcelable(ndefMessage, 0);
+        dest.writeInt(urisLength);
+        if (urisLength > 0) {
+            dest.writeTypedArray(uris, 0);
+        }
+        dest.writeInt(this.flags);
+    }
+
+    public static final Parcelable.Creator<BeamShareData> CREATOR =
+            new Parcelable.Creator<BeamShareData>() {
+        @Override
+        public BeamShareData createFromParcel(Parcel source) {
+            Uri[] uris = null;
+            NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader());
+            int numUris = source.readInt();
+            if (numUris > 0) {
+                uris = new Uri[numUris];
+                source.readTypedArray(uris, Uri.CREATOR);
+            }
+            int flags = source.readInt();
+
+            return new BeamShareData(msg, uris, flags);
+        }
+
+        @Override
+        public BeamShareData[] newArray(int size) {
+            return new BeamShareData[size];
+        }
+    };
+}
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
index 1c6d5d0..16771dc 100644
--- a/core/java/android/nfc/INdefPushCallback.aidl
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -16,15 +16,13 @@
 
 package android.nfc;
 
-import android.nfc.NdefMessage;
-import android.net.Uri;
+import android.nfc.BeamShareData;
 
 /**
  * @hide
  */
 interface INdefPushCallback
 {
-    NdefMessage createMessage();
-    Uri[] getUris();
+    BeamShareData createBeamShareData();
     void onNdefPushComplete();
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 7c3123f..10183c0 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -110,6 +110,7 @@
         NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
         NfcAdapter.CreateBeamUrisCallback uriCallback = null;
         Uri[] uris = null;
+        int flags = 0;
         public NfcActivityState(Activity activity) {
             if (activity.getWindow().isDestroyed()) {
                 throw new IllegalStateException("activity is already destroyed");
@@ -215,11 +216,12 @@
         }
     }
 
-    public void setNdefPushMessage(Activity activity, NdefMessage message) {
+    public void setNdefPushMessage(Activity activity, NdefMessage message, int flags) {
         boolean isResumed;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = getActivityState(activity);
             state.ndefMessage = message;
+            state.flags = flags;
             isResumed = state.resumed;
         }
         if (isResumed) {
@@ -228,11 +230,12 @@
     }
 
     public void setNdefPushMessageCallback(Activity activity,
-            NfcAdapter.CreateNdefMessageCallback callback) {
+            NfcAdapter.CreateNdefMessageCallback callback, int flags) {
         boolean isResumed;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = getActivityState(activity);
             state.ndefMessageCallback = callback;
+            state.flags = flags;
             isResumed = state.resumed;
         }
         if (isResumed) {
@@ -267,38 +270,29 @@
 
     /** Callback from NFC service, usually on binder thread */
     @Override
-    public NdefMessage createMessage() {
-        NfcAdapter.CreateNdefMessageCallback callback;
+    public BeamShareData createBeamShareData() {
+        NfcAdapter.CreateNdefMessageCallback ndefCallback;
+        NfcAdapter.CreateBeamUrisCallback urisCallback;
         NdefMessage message;
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = findResumedActivityState();
-            if (state == null) return null;
-
-            callback = state.ndefMessageCallback;
-            message = state.ndefMessage;
-        }
-
-        // Make callback without lock
-        if (callback != null) {
-            return callback.createNdefMessage(mDefaultEvent);
-        } else {
-            return message;
-        }
-    }
-
-    /** Callback from NFC service, usually on binder thread */
-    @Override
-    public Uri[] getUris() {
         Uri[] uris;
-        NfcAdapter.CreateBeamUrisCallback callback;
+        int flags;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findResumedActivityState();
             if (state == null) return null;
+
+            ndefCallback = state.ndefMessageCallback;
+            urisCallback = state.uriCallback;
+            message = state.ndefMessage;
             uris = state.uris;
-            callback = state.uriCallback;
+            flags = state.flags;
         }
-        if (callback != null) {
-            uris = callback.createBeamUris(mDefaultEvent);
+
+        // Make callbacks without lock
+        if (ndefCallback != null) {
+            message  = ndefCallback.createNdefMessage(mDefaultEvent);
+        }
+        if (urisCallback != null) {
+            uris = urisCallback.createBeamUris(mDefaultEvent);
             if (uris != null) {
                 for (Uri uri : uris) {
                     if (uri == null) {
@@ -314,10 +308,9 @@
                     }
                 }
             }
-            return uris;
-        } else {
-            return uris;
         }
+
+        return new BeamShareData(message, uris, flags);
     }
 
     /** Callback from NFC service, usually on binder thread */
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 6ad382b..ca4a7d6 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -196,6 +196,9 @@
     public static final int STATE_TURNING_OFF = 4;
 
     /** @hide */
+    public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
+
+    /** @hide */
     public static final String ACTION_HANDOVER_TRANSFER_STARTED =
             "android.nfc.action.HANDOVER_TRANSFER_STARTED";
 
@@ -796,12 +799,12 @@
             if (activity == null) {
                 throw new NullPointerException("activity cannot be null");
             }
-            mNfcActivityManager.setNdefPushMessage(activity, message);
+            mNfcActivityManager.setNdefPushMessage(activity, message, 0);
             for (Activity a : activities) {
                 if (a == null) {
                     throw new NullPointerException("activities cannot contain null");
                 }
-                mNfcActivityManager.setNdefPushMessage(a, message);
+                mNfcActivityManager.setNdefPushMessage(a, message, 0);
             }
         } catch (IllegalStateException e) {
             if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
@@ -816,6 +819,16 @@
     }
 
     /**
+     * @hide
+     */
+    public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        mNfcActivityManager.setNdefPushMessage(activity, message, flags);
+    }
+
+    /**
      * Set a callback that dynamically generates NDEF messages to send using Android Beam (TM).
      *
      * <p>This method may be called at any time before {@link Activity#onDestroy},
@@ -887,12 +900,12 @@
             if (activity == null) {
                 throw new NullPointerException("activity cannot be null");
             }
-            mNfcActivityManager.setNdefPushMessageCallback(activity, callback);
+            mNfcActivityManager.setNdefPushMessageCallback(activity, callback, 0);
             for (Activity a : activities) {
                 if (a == null) {
                     throw new NullPointerException("activities cannot contain null");
                 }
-                mNfcActivityManager.setNdefPushMessageCallback(a, callback);
+                mNfcActivityManager.setNdefPushMessageCallback(a, callback, 0);
             }
         } catch (IllegalStateException e) {
             if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
@@ -907,6 +920,17 @@
     }
 
     /**
+     * @hide
+     */
+    public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
+            int flags) {
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        mNfcActivityManager.setNdefPushMessageCallback(activity, callback, flags);
+    }
+
+    /**
      * Set a callback on successful Android Beam (TM).
      *
      * <p>This method may be called at any time before {@link Activity#onDestroy},
@@ -1095,7 +1119,7 @@
             throw new NullPointerException();
         }
         enforceResumed(activity);
-        mNfcActivityManager.setNdefPushMessage(activity, message);
+        mNfcActivityManager.setNdefPushMessage(activity, message, 0);
     }
 
     /**
@@ -1123,8 +1147,8 @@
             throw new NullPointerException();
         }
         enforceResumed(activity);
-        mNfcActivityManager.setNdefPushMessage(activity, null);
-        mNfcActivityManager.setNdefPushMessageCallback(activity, null);
+        mNfcActivityManager.setNdefPushMessage(activity, null, 0);
+        mNfcActivityManager.setNdefPushMessageCallback(activity, null, 0);
         mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null);
     }
 
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 4c2d7a6..2e8092a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -34,6 +34,7 @@
     Bitmap getUserIcon(int userHandle);
     List<UserInfo> getUsers(boolean excludeDying);
     UserInfo getUserInfo(int userHandle);
+    boolean isRestricted();
     void setGuestEnabled(boolean enable);
     boolean isGuestEnabled();
     void wipeUser(int userHandle);
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index c9adf45..729c64b 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -138,8 +138,6 @@
 
     /**
      * Returns milliseconds since boot, not counting time spent in deep sleep.
-     * <b>Note:</b> This value may get reset occasionally (before it would
-     * otherwise wrap around).
      *
      * @return milliseconds of non-sleep uptime since boot.
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7c05528..622308f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -37,48 +37,56 @@
     private final Context mContext;
 
     /**
-     * @hide
-     * Key for user restrictions. Specifies if a user is allowed to add or remove accounts.
+     * Key for user restrictions. Specifies if a user is disallowed from adding and removing
+     * accounts.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_MODIFY_ACCOUNTS = "modify_accounts";
+    public static final String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
 
     /**
-     * @hide
-     * Key for user restrictions. Specifies if a user is allowed to change Wi-Fi access points.
+     * Key for user restrictions. Specifies if a user is disallowed from changing Wi-Fi
+     * access points.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_CONFIG_WIFI = "config_wifi";
+    public static final String DISALLOW_CONFIG_WIFI = "no_config_wifi";
 
     /**
-     * @hide
-     * Key for user restrictions. Specifies if a user is allowed to install applications.
+     * Key for user restrictions. Specifies if a user is disallowed from installing applications.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_INSTALL_APPS = "install_apps";
+    public static final String DISALLOW_INSTALL_APPS = "no_install_apps";
 
     /**
-     * @hide
-     * Key for user restrictions. Specifies if a user is allowed to uninstall applications.
+     * Key for user restrictions. Specifies if a user is disallowed from uninstalling applications.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_UNINSTALL_APPS = "uninstall_apps";
+    public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
 
-    /** @hide *
-     * Key for user restrictions. Specifies if a user is allowed to toggle location sharing.
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from toggling location sharing.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_CONFIG_LOCATION_ACCESS = "config_location_access";
+    public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
 
     /** @hide */
     public UserManager(Context context, IUserManager service) {
@@ -129,11 +137,14 @@
     }
 
     /**
-     * @hide
+     * Used to check if the user making this call is a restricted user. Restricted users may have
+     * application restrictions imposed on them. All apps should default to the most restrictive
+     * version, unless they have specific restrictions available through a call to
+     * {@link Context#getApplicationRestrictions()}.
      */
     public boolean isUserRestricted() {
         try {
-            return mService.getUserInfo(getUserHandle()).isRestricted();
+            return mService.isRestricted();
         } catch (RemoteException re) {
             Log.w(TAG, "Could not check if user restricted ", re);
             return false;
@@ -189,12 +200,19 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Returns the user-wide restrictions imposed on this user.
+     * @return a Bundle containing all the restrictions.
+     */
     public Bundle getUserRestrictions() {
         return getUserRestrictions(Process.myUserHandle());
     }
 
-    /** @hide */
+    /**
+     * Returns the user-wide restrictions imposed on the user specified by <code>userHandle</code>.
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @return a Bundle containing all the restrictions.
+     */
     public Bundle getUserRestrictions(UserHandle userHandle) {
         try {
             return mService.getUserRestrictions(userHandle.getIdentifier());
@@ -204,12 +222,21 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Sets all the user-wide restrictions for this user.
+     * Requires the MANAGE_USERS permission.
+     * @param restrictions the Bundle containing all the restrictions.
+     */
     public void setUserRestrictions(Bundle restrictions) {
         setUserRestrictions(restrictions, Process.myUserHandle());
     }
 
-    /** @hide */
+    /**
+     * Sets all the user-wide restrictions for the specified user.
+     * Requires the MANAGE_USERS permission.
+     * @param restrictions the Bundle containing all the restrictions.
+     * @param userHandle the UserHandle of the user for whom to set the restrictions.
+     */
     public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
         try {
             mService.setUserRestrictions(restrictions, userHandle.getIdentifier());
@@ -218,7 +245,26 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Sets the value of a specific restriction.
+     * Requires the MANAGE_USERS permission.
+     * @param key the key of the restriction
+     * @param value the value for the restriction
+     */
+    public void setUserRestriction(String key, boolean value) {
+        Bundle bundle = getUserRestrictions();
+        bundle.putBoolean(key, value);
+        setUserRestrictions(bundle);
+    }
+
+    /**
+     * @hide
+     * Sets the value of a specific restriction on a specific user.
+     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * @param key the key of the restriction
+     * @param value the value for the restriction
+     * @param userHandle the user whose restriction is to be changed.
+     */
     public void setUserRestriction(String key, boolean value, UserHandle userHandle) {
         Bundle bundle = getUserRestrictions(userHandle);
         bundle.putBoolean(key, value);
@@ -467,7 +513,7 @@
      * @hide
      */
     public boolean isLocationSharingToggleAllowed() {
-        return getUserRestrictions().getBoolean(ALLOW_CONFIG_LOCATION_ACCESS);
+        return !getUserRestrictions().getBoolean(DISALLOW_SHARE_LOCATION, false);
     }
 
     /**
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 8f54a38..a4c4411 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4325,6 +4325,13 @@
         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");
 
         /**
+         * A boolean parameter for {@link Data#CONTENT_URI}.
+         * This specifies whether or not the returned data items should be filtered to show
+         * data items belonging to visible contacts only.
+         */
+        public static final String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
+
+        /**
          * The MIME type of the results from {@link #CONTENT_URI}.
          */
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/data";
@@ -6833,6 +6840,38 @@
             public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(CONTENT_URI,
                     "filter");
         }
+
+        /**
+         * A special class of data items, used to refer to types of data that can be used to attempt
+         * to start communicating with a person ({@link Phone} and {@link Email}). Note that this
+         * is NOT a separate data kind.
+         *
+         * This URI allows the ContactsProvider to return a unified result for data items that users
+         * can use to initiate communications with another contact. {@link Phone} and {@link Email}
+         * are the current data types in this category.
+         */
+        public static final class Contactables implements DataColumnsWithJoins, CommonColumns {
+            /**
+             * The content:// style URI for these data items, which requests a directory of data
+             * rows matching the selection criteria.
+             */
+            public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
+                    "contactables");
+
+            /**
+             * The content:// style URI for these data items, which allows for a query parameter to
+             * be appended onto the end to filter for data items matching the query.
+             */
+            public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(
+                    Contactables.CONTENT_URI, "filter");
+
+            /**
+             * A boolean parameter for {@link Data#CONTENT_URI}.
+             * This specifies whether or not the returned data items should be filtered to show
+             * data items belonging to visible contacts only.
+             */
+            public static final String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
+        }
     }
 
     /**
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 7e9d811..5a88cf6 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -39,7 +39,6 @@
 {
     private static final Object sLock = new Object();
     private static Configuration sLastConfig;
-    private static java.text.DateFormat sStatusTimeFormat;
     private static String sElapsedFormatMMSS;
     private static String sElapsedFormatHMMSS;
 
@@ -95,14 +94,14 @@
     // translated.
     /**
      * This is not actually the preferred 24-hour date format in all locales.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final String HOUR_MINUTE_24 = "%H:%M";
     public static final String MONTH_FORMAT = "%B";
     /**
      * This is not actually a useful month name in all locales.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final String ABBREV_MONTH_FORMAT = "%b";
@@ -118,7 +117,7 @@
     // The index is constructed from a bit-wise OR of the boolean values:
     // {showTime, showYear, showWeekDay}.  For example, if showYear and
     // showWeekDay are both true, then the index would be 3.
-    /** @deprecated do not use. */
+    /** @deprecated Do not use. */
     public static final int sameYearTable[] = {
         com.android.internal.R.string.same_year_md1_md2,
         com.android.internal.R.string.same_year_wday1_md1_wday2_md2,
@@ -145,7 +144,7 @@
     // The index is constructed from a bit-wise OR of the boolean values:
     // {showTime, showYear, showWeekDay}.  For example, if showYear and
     // showWeekDay are both true, then the index would be 3.
-    /** @deprecated do not use. */
+    /** @deprecated Do not use. */
     public static final int sameMonthTable[] = {
         com.android.internal.R.string.same_month_md1_md2,
         com.android.internal.R.string.same_month_wday1_md1_wday2_md2,
@@ -172,7 +171,7 @@
      *
      * @more <p>
      *       e.g. "Sunday" or "January"
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_LONG = 10;
@@ -183,7 +182,7 @@
      *
      * @more <p>
      *       e.g. "Sun" or "Jan"
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_MEDIUM = 20;
@@ -195,7 +194,7 @@
      * <p>e.g. "Su" or "Jan"
      * <p>In most languages, the results returned for LENGTH_SHORT will be the same as
      * the results returned for {@link #LENGTH_MEDIUM}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORT = 30;
@@ -204,7 +203,7 @@
      * Request an even shorter abbreviated version of the name.
      * Do not use this.  Currently this will always return the same result
      * as {@link #LENGTH_SHORT}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORTER = 40;
@@ -216,7 +215,7 @@
      * <p>e.g. "S", "T", "T" or "J"
      * <p>In some languages, the results returned for LENGTH_SHORTEST will be the same as
      * the results returned for {@link #LENGTH_SHORT}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORTEST = 50;
@@ -232,7 +231,7 @@
      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
      *               but may return something different in the future.
      * @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
@@ -254,7 +253,7 @@
      * @param ampm Either {@link Calendar#AM Calendar.AM} or {@link Calendar#PM Calendar.PM}.
      * @throws IndexOutOfBoundsException if the ampm is out of bounds.
      * @return Localized version of "AM" or "PM".
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getAMPMString(int ampm) {
@@ -270,7 +269,7 @@
      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
      *               but may return something different in the future.
      * @return Localized month of the year.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getMonthString(int month, int abbrev) {
@@ -514,7 +513,6 @@
         Configuration cfg = r.getConfiguration();
         if (sLastConfig == null || !sLastConfig.equals(cfg)) {
             sLastConfig = cfg;
-            sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
             sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
             sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
         }
@@ -651,99 +649,6 @@
     }
 
     /**
-     * Return a string containing the date and time in RFC2445 format.
-     * Ensures that the time is written in UTC.  The Calendar class doesn't
-     * really help out with this, so this is slower than it ought to be.
-     *
-     * @param cal the date and time to write
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal)
-    {
-        TimeZone tz = TimeZone.getTimeZone("GMT");
-        GregorianCalendar c = new GregorianCalendar(tz);
-        c.setTimeInMillis(cal.getTimeInMillis());
-        return writeDateTime(c, true);
-    }
-
-    /**
-     * Return a string containing the date and time in RFC2445 format.
-     *
-     * @param cal the date and time to write
-     * @param zulu If the calendar is in UTC, pass true, and a Z will
-     * be written at the end as per RFC2445.  Otherwise, the time is
-     * considered in localtime.
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal, boolean zulu)
-    {
-        StringBuilder sb = new StringBuilder();
-        sb.ensureCapacity(16);
-        if (zulu) {
-            sb.setLength(16);
-            sb.setCharAt(15, 'Z');
-        } else {
-            sb.setLength(15);
-        }
-        return writeDateTime(cal, sb);
-    }
-
-    /**
-     * Return a string containing the date and time in RFC2445 format.
-     *
-     * @param cal the date and time to write
-     * @param sb a StringBuilder to use.  It is assumed that setLength
-     *           has already been called on sb to the appropriate length
-     *           which is sb.setLength(zulu ? 16 : 15)
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal, StringBuilder sb)
-    {
-        int n;
-
-        n = cal.get(Calendar.YEAR);
-        sb.setCharAt(3, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(2, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(1, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(0, (char)('0'+n%10));
-
-        n = cal.get(Calendar.MONTH) + 1;
-        sb.setCharAt(5, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(4, (char)('0'+n%10));
-
-        n = cal.get(Calendar.DAY_OF_MONTH);
-        sb.setCharAt(7, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(6, (char)('0'+n%10));
-
-        sb.setCharAt(8, 'T');
-
-        n = cal.get(Calendar.HOUR_OF_DAY);
-        sb.setCharAt(10, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(9, (char)('0'+n%10));
-
-        n = cal.get(Calendar.MINUTE);
-        sb.setCharAt(12, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(11, (char)('0'+n%10));
-
-        n = cal.get(Calendar.SECOND);
-        sb.setCharAt(14, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(13, (char)('0'+n%10));
-
-        return sb.toString();
-    }
-
-    /**
      * Formats a date or a time range according to the local conventions.
      * <p>
      * Note that this is a convenience method. Using it involves creating an
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 121c6f2..9c98b98 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -95,16 +95,12 @@
     }
 
     /**
-     * Returns a string in the canonical IP format ###.###.###.### from a packed integer containing
-     * the IP address.  The IP address is expected to be in little-endian format (LSB first). That
-     * is, 0x01020304 will return "4.3.2.1".
+     * Returns a string in the canonical IPv4 format ###.###.###.### from a packed integer
+     * containing the IP address. The IPv4 address is expected to be in little-endian
+     * format (LSB first). That is, 0x01020304 will return "4.3.2.1".
      *
-     * @param ipv4Address the IP address as a packed integer with LSB first.
-     * @return string with canonical IP address format.
-     *
-     * @deprecated this method doesn't support IPv6 addresses. Prefer {@link
-     *     java.net.InetAddress#getHostAddress()}, which supports both IPv4 and
-     *     IPv6 addresses.
+     * @deprecated Use {@link java.net.InetAddress#getHostAddress()}, which supports both IPv4 and
+     *     IPv6 addresses. This method does not support IPv6 addresses.
      */
     @Deprecated
     public static String formatIpAddress(int ipv4Address) {
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index e086f5a..7918823 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -428,6 +428,8 @@
     interface HardwareDrawCallbacks {
         /**
          * Invoked before a view is drawn by a hardware renderer.
+         * This method can be used to apply transformations to the
+         * canvas but no drawing command should be issued.
          * 
          * @param canvas The Canvas used to render the view.
          */
@@ -435,6 +437,7 @@
 
         /**
          * Invoked after a view is drawn by a hardware renderer.
+         * It is safe to invoke drawing commands from this method.
          * 
          * @param canvas The Canvas used to render the view.
          */
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f0c6241..a85a558 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -60,8 +60,12 @@
             in IInputContext inputContext);
     boolean inputMethodClientHasFocus(IInputMethodClient client);
 
+    void getInitialDisplaySize(int displayId, out Point size);
+    void getBaseDisplaySize(int displayId, out Point size);
     void setForcedDisplaySize(int displayId, int width, int height);
     void clearForcedDisplaySize(int displayId);
+    int getInitialDisplayDensity(int displayId);
+    int getBaseDisplayDensity(int displayId);
     void setForcedDisplayDensity(int displayId, int density);
     void clearForcedDisplayDensity(int displayId);
 
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index 523af04..a797176 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -78,7 +78,9 @@
      * Creates a new input channel pair.  One channel should be provided to the input
      * dispatcher and the other to the application's input queue.
      * @param name The descriptive (non-unique) name of the channel pair.
-     * @return A pair of input channels.  They are symmetric and indistinguishable.
+     * @return A pair of input channels.  The first channel is designated as the
+     * server channel and should be used to publish input events.  The second channel
+     * is designated as the client channel and should be used to consume input events.
      */
     public static InputChannel[] openInputChannelPair(String name) {
         if (name == null) {
@@ -123,10 +125,11 @@
         nativeTransferTo(outParameter);
     }
 
+    @Override
     public int describeContents() {
         return Parcelable.CONTENTS_FILE_DESCRIPTOR;
     }
-    
+
     public void readFromParcel(Parcel in) {
         if (in == null) {
             throw new IllegalArgumentException("in must not be null");
@@ -134,7 +137,8 @@
         
         nativeReadFromParcel(in);
     }
-    
+
+    @Override
     public void writeToParcel(Parcel out, int flags) {
         if (out == null) {
             throw new IllegalArgumentException("out must not be null");
diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java
new file mode 100644
index 0000000..adf63fe
--- /dev/null
+++ b/core/java/android/view/InputEventSender.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import dalvik.system.CloseGuard;
+
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.util.Log;
+
+/**
+ * Provides a low-level mechanism for an application to send input events.
+ * @hide
+ */
+public abstract class InputEventSender {
+    private static final String TAG = "InputEventSender";
+
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    private int mSenderPtr;
+
+    // We keep references to the input channel and message queue objects here so that
+    // they are not GC'd while the native peer of the receiver is using them.
+    private InputChannel mInputChannel;
+    private MessageQueue mMessageQueue;
+
+    private static native int nativeInit(InputEventSender sender,
+            InputChannel inputChannel, MessageQueue messageQueue);
+    private static native void nativeDispose(int senderPtr);
+    private static native boolean nativeSendKeyEvent(int senderPtr, int seq, KeyEvent event);
+    private static native boolean nativeSendMotionEvent(int senderPtr, int seq, MotionEvent event);
+
+    /**
+     * Creates an input event sender bound to the specified input channel.
+     *
+     * @param inputChannel The input channel.
+     * @param looper The looper to use when invoking callbacks.
+     */
+    public InputEventSender(InputChannel inputChannel, Looper looper) {
+        if (inputChannel == null) {
+            throw new IllegalArgumentException("inputChannel must not be null");
+        }
+        if (looper == null) {
+            throw new IllegalArgumentException("looper must not be null");
+        }
+
+        mInputChannel = inputChannel;
+        mMessageQueue = looper.getQueue();
+        mSenderPtr = nativeInit(this, inputChannel, mMessageQueue);
+
+        mCloseGuard.open("dispose");
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            dispose(true);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Disposes the receiver.
+     */
+    public void dispose() {
+        dispose(false);
+    }
+
+    private void dispose(boolean finalized) {
+        if (mCloseGuard != null) {
+            if (finalized) {
+                mCloseGuard.warnIfOpen();
+            }
+            mCloseGuard.close();
+        }
+
+        if (mSenderPtr != 0) {
+            nativeDispose(mSenderPtr);
+            mSenderPtr = 0;
+        }
+        mInputChannel = null;
+        mMessageQueue = null;
+    }
+
+    /**
+     * Called when an input event is finished.
+     *
+     * @param seq The input event sequence number.
+     * @param handled True if the input event was handled.
+     */
+    public void onInputEventFinished(int seq, boolean handled) {
+    }
+
+    /**
+     * Sends an input event.
+     * Must be called on the same Looper thread to which the sender is attached.
+     *
+     * @param seq The input event sequence number.
+     * @param event The input event to send.
+     * @return True if the entire event was sent successfully.  May return false
+     * if the input channel buffer filled before all samples were dispatched.
+     */
+    public final boolean sendInputEvent(int seq, InputEvent event) {
+        if (event == null) {
+            throw new IllegalArgumentException("event must not be null");
+        }
+        if (mSenderPtr == 0) {
+            Log.w(TAG, "Attempted to send an input event but the input event "
+                    + "sender has already been disposed.");
+            return false;
+        }
+
+        if (event instanceof KeyEvent) {
+            return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event);
+        } else {
+            return nativeSendMotionEvent(mSenderPtr, seq, (MotionEvent)event);
+        }
+    }
+
+    // Called from native code.
+    @SuppressWarnings("unused")
+    private void dispatchInputEventFinished(int seq, boolean handled) {
+        onInputEventFinished(seq, handled);
+    }
+}
diff --git a/core/java/android/view/Overlay.java b/core/java/android/view/Overlay.java
index 210bc31..6630752 100644
--- a/core/java/android/view/Overlay.java
+++ b/core/java/android/view/Overlay.java
@@ -18,9 +18,10 @@
 import android.graphics.drawable.Drawable;
 
 /**
- * An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after
- * all other content in that view (including children, if the view is a ViewGroup). Interaction
- * with the overlay layer is done in terms of adding/removing views and drawables.
+ * An overlay is an extra layer that sits on top of a View (the "host view")
+ * which is drawn after all other content in that view (including children,
+ * if the view is a ViewGroup). Interaction with the overlay layer is done in
+ * terms of adding/removing views and drawables.
  *
  * @see android.view.View#getOverlay()
  */
@@ -47,10 +48,16 @@
     void remove(Drawable drawable);
 
     /**
-     * Adds a View to the overlay. The bounds of the added view should be relative to
-     * the host view. Any view added to the overlay should be removed when it is no longer
-     * needed or no longer visible. The view must not be parented elsewhere when it is added
-     * to the overlay.
+     * Adds a View to the overlay. The bounds of the added view should be
+     * relative to the host view. Any view added to the overlay should be
+     * removed when it is no longer needed or no longer visible.
+     *
+     * <p>If the view has a parent, the view will be removed from that parent
+     * before being added to the overlay. Also, the view will be repositioned
+     * such that it is in the same relative location inside the activity. For
+     * example, if the view's current parent lies 100 pixels to the right
+     * and 200 pixels down from the origin of the overlay's
+     * host view, then the view will be offset by (100, 200).</p>
      *
      * @param view The View to be added to the overlay. The added view will be
      * drawn when the overlay is drawn.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 34f5a2b..a5b3c8f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12098,7 +12098,7 @@
         //System.out.println("Attached! " + this);
         mAttachInfo = info;
         if (mOverlay != null) {
-            mOverlay.mAttachInfo = info;
+            mOverlay.dispatchAttachedToWindow(info, visibility);
         }
         mWindowAttachCount++;
         // We will need to evaluate the drawable state at least once.
@@ -12169,7 +12169,7 @@
 
         mAttachInfo = null;
         if (mOverlay != null) {
-            mOverlay.mAttachInfo = null;
+            mOverlay.dispatchDetachedFromWindow();
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d63f7bc..98edeae 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -51,6 +51,8 @@
 import java.util.Collections;
 import java.util.HashSet;
 
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
 /**
  * <p>
  * A <code>ViewGroup</code> is a special view that can contain other views
@@ -1868,13 +1870,37 @@
                     // have become out of sync.
                     removePointersFromTouchTargets(idBitsToAssign);
 
+                    final float x = ev.getX(actionIndex);
+                    final float y = ev.getY(actionIndex);
+
+                    if (mOverlay != null) {
+                        ViewOverlay overlay = (ViewOverlay) mOverlay;
+                        // Check to see whether the overlay can handle the event
+                        final View child = mOverlay;
+                        if (canViewReceivePointerEvents(child) &&
+                                isTransformedTouchPointInView(x, y, child, null)) {
+                            newTouchTarget = getTouchTarget(child);
+                            if (newTouchTarget != null) {
+                                newTouchTarget.pointerIdBits |= idBitsToAssign;
+                            } else {
+                                resetCancelNextUpFlag(child);
+                                if (dispatchTransformedTouchEvent(ev, false, child,
+                                        idBitsToAssign)) {
+                                    mLastTouchDownTime = ev.getDownTime();
+                                    mLastTouchDownX = ev.getX();
+                                    mLastTouchDownY = ev.getY();
+                                    newTouchTarget = addTouchTarget(child, idBitsToAssign);
+                                    alreadyDispatchedToNewTouchTarget = true;
+                                }
+                            }
+                        }
+                    }
+
                     final int childrenCount = mChildrenCount;
-                    if (childrenCount != 0 || mOverlay != null) {
+                    if (newTouchTarget == null && childrenCount != 0) {
                         // Find a child that can receive the event.
                         // Scan children from front to back.
                         final View[] children = mChildren;
-                        final float x = ev.getX(actionIndex);
-                        final float y = ev.getY(actionIndex);
 
                         final boolean customOrder = isChildrenDrawingOrderEnabled();
                         for (int i = childrenCount - 1; i >= 0; i--) {
@@ -1906,27 +1932,6 @@
                                 break;
                             }
                         }
-                        if (mOverlay != null && newTouchTarget == null) {
-                            // Check to see whether the overlay can handle the event
-                            final View child = mOverlay;
-                            if (canViewReceivePointerEvents(child) &&
-                                    isTransformedTouchPointInView(x, y, child, null)) {
-                                newTouchTarget = getTouchTarget(child);
-                                if (newTouchTarget != null) {
-                                    newTouchTarget.pointerIdBits |= idBitsToAssign;
-                                } else {
-                                    resetCancelNextUpFlag(child);
-                                    if (dispatchTransformedTouchEvent(ev, false, child,
-                                            idBitsToAssign)) {
-                                        mLastTouchDownTime = ev.getDownTime();
-                                        mLastTouchDownX = ev.getX();
-                                        mLastTouchDownY = ev.getY();
-                                        newTouchTarget = addTouchTarget(child, idBitsToAssign);
-                                        alreadyDispatchedToNewTouchTarget = true;
-                                    }
-                                }
-                            }
-                        }
                     }
 
                     if (newTouchTarget == null && mFirstTouchTarget != null) {
@@ -1957,7 +1962,7 @@
                         handled = true;
                     } else {
                         final boolean cancelChild = resetCancelNextUpFlag(target.child)
-                        || intercepted;
+                                || intercepted;
                         if (dispatchTransformedTouchEvent(ev, cancelChild,
                                 target.child, target.pointerIdBits)) {
                             handled = true;
@@ -5861,7 +5866,7 @@
          * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
-        private int startMargin = DEFAULT_RELATIVE;
+        private int startMargin = DEFAULT_MARGIN_RELATIVE;
 
         /**
          * The end margin in pixels of the child.
@@ -5869,21 +5874,21 @@
          * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
-        private int endMargin = DEFAULT_RELATIVE;
+        private int endMargin = DEFAULT_MARGIN_RELATIVE;
 
         /**
          * The default start and end margin.
          * @hide
          */
-        public static final int DEFAULT_RELATIVE = Integer.MIN_VALUE;
+        public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
 
-        private int initialLeftMargin;
-        private int initialRightMargin;
+        // Layout direction is LTR by default
+        private int mLayoutDirection = LAYOUT_DIRECTION_LTR;
 
-        private static int LAYOUT_DIRECTION_UNDEFINED = -1;
+        private static int DEFAULT_MARGIN_RESOLVED = 0;
 
-        // Layout direction undefined by default
-        private int layoutDirection = LAYOUT_DIRECTION_UNDEFINED;
+        private boolean mNeedResolution = false;
+        private boolean mIsRtlCompatibilityMode = true;
 
         /**
          * Creates a new set of layout parameters. The values are extracted from
@@ -5910,21 +5915,30 @@
                 bottomMargin = margin;
             } else {
                 leftMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
+                        DEFAULT_MARGIN_RESOLVED);
                 topMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginTop,
+                        DEFAULT_MARGIN_RESOLVED);
                 rightMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginRight,
+                        DEFAULT_MARGIN_RESOLVED);
                 bottomMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
+                        DEFAULT_MARGIN_RESOLVED);
                 startMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_RELATIVE);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginStart,
+                        DEFAULT_MARGIN_RELATIVE);
                 endMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_RELATIVE);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
+                        DEFAULT_MARGIN_RELATIVE);
+
+                mNeedResolution = isMarginRelative();
             }
 
-            initialLeftMargin = leftMargin;
-            initialRightMargin = rightMargin;
+            final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
+            final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
+            mIsRtlCompatibilityMode = targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport;
 
             a.recycle();
         }
@@ -5934,6 +5948,9 @@
          */
         public MarginLayoutParams(int width, int height) {
             super(width, height);
+
+            mNeedResolution = false;
+            mIsRtlCompatibilityMode = false;
         }
 
         /**
@@ -5952,10 +5969,10 @@
             this.startMargin = source.startMargin;
             this.endMargin = source.endMargin;
 
-            this.initialLeftMargin = source.leftMargin;
-            this.initialRightMargin = source.rightMargin;
+            this.mNeedResolution = source.mNeedResolution;
+            this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode;
 
-            setLayoutDirection(source.layoutDirection);
+            setLayoutDirection(source.mLayoutDirection);
         }
 
         /**
@@ -5963,6 +5980,9 @@
          */
         public MarginLayoutParams(LayoutParams source) {
             super(source);
+
+            mNeedResolution = false;
+            mIsRtlCompatibilityMode = false;
         }
 
         /**
@@ -5985,8 +6005,7 @@
             topMargin = top;
             rightMargin = right;
             bottomMargin = bottom;
-            initialLeftMargin = left;
-            initialRightMargin = right;
+            mNeedResolution = isMarginRelative();
         }
 
         /**
@@ -6012,8 +6031,7 @@
             topMargin = top;
             endMargin = end;
             bottomMargin = bottom;
-            initialLeftMargin = 0;
-            initialRightMargin = 0;
+            mNeedResolution = true;
         }
 
         /**
@@ -6025,6 +6043,7 @@
          */
         public void setMarginStart(int start) {
             startMargin = start;
+            mNeedResolution = true;
         }
 
         /**
@@ -6035,8 +6054,11 @@
          * @return the start margin in pixels.
          */
         public int getMarginStart() {
-            if (startMargin != DEFAULT_RELATIVE) return startMargin;
-            switch(layoutDirection) {
+            if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
+            if (mNeedResolution) {
+                doResolveMargins();
+            }
+            switch(mLayoutDirection) {
                 case View.LAYOUT_DIRECTION_RTL:
                     return rightMargin;
                 case View.LAYOUT_DIRECTION_LTR:
@@ -6054,6 +6076,7 @@
          */
         public void setMarginEnd(int end) {
             endMargin = end;
+            mNeedResolution = true;
         }
 
         /**
@@ -6064,8 +6087,11 @@
          * @return the end margin in pixels.
          */
         public int getMarginEnd() {
-            if (endMargin != DEFAULT_RELATIVE) return endMargin;
-            switch(layoutDirection) {
+            if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
+            if (mNeedResolution) {
+                doResolveMargins();
+            }
+            switch(mLayoutDirection) {
                 case View.LAYOUT_DIRECTION_RTL:
                     return leftMargin;
                 case View.LAYOUT_DIRECTION_LTR:
@@ -6083,7 +6109,7 @@
          * @return true if either marginStart or marginEnd has been set.
          */
         public boolean isMarginRelative() {
-            return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE);
+            return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
         }
 
         /**
@@ -6095,7 +6121,10 @@
         public void setLayoutDirection(int layoutDirection) {
             if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
                     layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
-            this.layoutDirection = layoutDirection;
+            if (layoutDirection != this.mLayoutDirection) {
+                this.mLayoutDirection = layoutDirection;
+                this.mNeedResolution = isMarginRelative();
+            }
         }
 
         /**
@@ -6105,7 +6134,7 @@
          * @return the layout direction.
          */
         public int getLayoutDirection() {
-            return layoutDirection;
+            return mLayoutDirection;
         }
 
         /**
@@ -6116,26 +6145,41 @@
         public void resolveLayoutDirection(int layoutDirection) {
             setLayoutDirection(layoutDirection);
 
-            if (!isMarginRelative()) return;
+            // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
+            // Will use the left and right margins if no relative margin is defined.
+            if (!isMarginRelative() || !mNeedResolution || mIsRtlCompatibilityMode) return;
 
-            switch(layoutDirection) {
+            // Proceed with resolution
+            doResolveMargins();
+        }
+
+        private void doResolveMargins() {
+            // We have some relative margins (either the start one or the end one or both). So use
+            // them and override what has been defined for left and right margins. If either start
+            // or end margin is not defined, just set it to default "0".
+            switch(mLayoutDirection) {
                 case View.LAYOUT_DIRECTION_RTL:
-                    leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : initialLeftMargin;
-                    rightMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : initialRightMargin;
+                    leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
+                            endMargin : DEFAULT_MARGIN_RESOLVED;
+                    rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
+                            startMargin : DEFAULT_MARGIN_RESOLVED;
                     break;
                 case View.LAYOUT_DIRECTION_LTR:
                 default:
-                    leftMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : initialLeftMargin;
-                    rightMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : initialRightMargin;
+                    leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
+                            startMargin : DEFAULT_MARGIN_RESOLVED;
+                    rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
+                            endMargin : DEFAULT_MARGIN_RESOLVED;
                     break;
             }
+            mNeedResolution = false;
         }
 
         /**
          * @hide
          */
         public boolean isLayoutRtl() {
-            return (layoutDirection == View.LAYOUT_DIRECTION_RTL);
+            return (mLayoutDirection == View.LAYOUT_DIRECTION_RTL);
         }
 
         /**
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 939377d..8b18d53 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -23,17 +23,21 @@
 import java.util.ArrayList;
 
 /**
- * ViewOverlay is a container that View uses to host all objects (views and drawables) that
- * are added to its "overlay", gotten through {@link View#getOverlay()}. Views and drawables are
- * added to the overlay via the add/remove methods in this class. These views and drawables are
- * drawn whenever the view itself is drawn; first the view draws its own content (and children,
- * if it is a ViewGroup), then it draws its overlay (if it has one).
+ * ViewOverlay is a container that View uses to host all objects (views and
+ * drawables) that are added to its "overlay", gotten through
+ * {@link View#getOverlay()}. Views and drawables are added to the overlay
+ * via the add/remove methods in this class. These views and drawables are
+ * drawn whenever the view itself is drawn; first the view draws its own
+ * content (and children, if it is a ViewGroup), then it draws its overlay
+ * (if it has one).
  *
- * Besides managing and drawing the list of drawables, this class serves two purposes:
+ * Besides managing and drawing the list of drawables, this class serves
+ * two purposes:
  * (1) it noops layout calls because children are absolutely positioned and
- * (2) it forwards all invalidation calls to its host view. The invalidation redirect is
- * necessary because the overlay is not a child of the host view and invalidation cannot
- * therefore follow the normal path up through the parent hierarchy.
+ * (2) it forwards all invalidation calls to its host view. The invalidation
+ * redirect is necessary because the overlay is not a child of the host view
+ * and invalidation cannot therefore follow the normal path up through the
+ * parent hierarchy.
  *
  * @hide
  */
@@ -85,6 +89,22 @@
 
     @Override
     public void add(View child) {
+        int deltaX = 0;
+        int deltaY = 0;
+        if (child.getParent() instanceof ViewGroup) {
+            ViewGroup parent = (ViewGroup) child.getParent();
+            if (parent != mHostView) {
+                // Moving to different container; figure out how to position child such that
+                // it is in the same location on the screen
+                int[] parentLocation = new int[2];
+                int[] hostViewLocation = new int[2];
+                parent.getLocationOnScreen(parentLocation);
+                mHostView.getLocationOnScreen(hostViewLocation);
+                child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]);
+                child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
+            }
+            parent.removeView(child);
+        }
         super.addView(child);
     }
 
@@ -133,7 +153,6 @@
     public void invalidate(Rect dirty) {
         super.invalidate(dirty);
         if (mHostView != null) {
-            dirty.offset(getLeft(), getTop());
             mHostView.invalidate(dirty);
         }
     }
@@ -203,7 +222,15 @@
     @Override
     public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
         if (mHostView != null) {
-            mHostView.invalidate(dirty);
+            dirty.offset(location[0], location[1]);
+            if (mHostView instanceof ViewGroup) {
+                location[0] = 0;
+                location[1] = 0;
+                super.invalidateChildInParent(location, dirty);
+                return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
+            } else {
+                invalidate(dirty);
+            }
         }
         return null;
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b30dc83..7b34ce1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2937,7 +2937,6 @@
     private final static int MSG_DISPATCH_KEY = 7;
     private final static int MSG_DISPATCH_APP_VISIBILITY = 8;
     private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9;
-    private final static int MSG_IME_FINISHED_EVENT = 10;
     private final static int MSG_DISPATCH_KEY_FROM_IME = 11;
     private final static int MSG_FINISH_INPUT_CONNECTION = 12;
     private final static int MSG_CHECK_FOCUS = 13;
@@ -2952,6 +2951,8 @@
     private final static int MSG_DISPATCH_DONE_ANIMATING = 22;
     private final static int MSG_INVALIDATE_WORLD = 23;
     private final static int MSG_WINDOW_MOVED = 24;
+    private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 25;
+    private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 26;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -2975,8 +2976,6 @@
                     return "MSG_DISPATCH_APP_VISIBILITY";
                 case MSG_DISPATCH_GET_NEW_SURFACE:
                     return "MSG_DISPATCH_GET_NEW_SURFACE";
-                case MSG_IME_FINISHED_EVENT:
-                    return "MSG_IME_FINISHED_EVENT";
                 case MSG_DISPATCH_KEY_FROM_IME:
                     return "MSG_DISPATCH_KEY_FROM_IME";
                 case MSG_FINISH_INPUT_CONNECTION:
@@ -3003,6 +3002,10 @@
                     return "MSG_DISPATCH_DONE_ANIMATING";
                 case MSG_WINDOW_MOVED:
                     return "MSG_WINDOW_MOVED";
+                case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
+                    return "MSG_ENQUEUE_X_AXIS_KEY_REPEAT";
+                case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT:
+                    return "MSG_ENQUEUE_Y_AXIS_KEY_REPEAT";
             }
             return super.getMessageName(message);
         }
@@ -3018,9 +3021,6 @@
                 info.target.invalidate(info.left, info.top, info.right, info.bottom);
                 info.recycle();
                 break;
-            case MSG_IME_FINISHED_EVENT:
-                handleImeFinishedEvent(msg.arg1, msg.arg2 != 0);
-                break;
             case MSG_PROCESS_INPUT_EVENTS:
                 mProcessInputEventsScheduled = false;
                 doProcessInputEvents();
@@ -3229,6 +3229,18 @@
                     invalidateWorld(mView);
                 }
             } break;
+            case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
+            case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
+                KeyEvent oldEvent = (KeyEvent)msg.obj;
+                KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent, SystemClock.uptimeMillis(),
+                        oldEvent.getRepeatCount() + 1);
+                if (mAttachInfo.mHasWindowFocus) {
+                    enqueueInputEvent(e);
+                    Message m = obtainMessage(msg.what, e);
+                    m.setAsynchronous(true);
+                    sendMessageDelayed(m, mViewConfiguration.getKeyRepeatDelay());
+                }
+            } break;
             }
         }
     }
@@ -3444,26 +3456,15 @@
             mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
         }
 
+        int result = EVENT_POST_IME;
         if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
             if (LOCAL_LOGV)
                 Log.v(TAG, "Dispatching trackball " + event + " to " + mView);
 
             // Dispatch to the IME before propagating down the view hierarchy.
-            // The IME will eventually call back into handleImeFinishedEvent.
-            if (mLastWasImTarget) {
-                InputMethodManager imm = InputMethodManager.peekInstance();
-                if (imm != null) {
-                    final int seq = event.getSequenceNumber();
-                    if (DEBUG_IMF)
-                        Log.v(TAG, "Sending trackball event to IME: seq="
-                                + seq + " event=" + event);
-                    return imm.dispatchTrackballEvent(mView.getContext(), seq, event,
-                            mInputMethodCallback);
-                }
-            }
+            result = dispatchImeInputEvent(q);
         }
-
-        return EVENT_POST_IME;
+        return result;
     }
 
     private int deliverTrackballEventPostIme(QueuedInputEvent q) {
@@ -3598,26 +3599,16 @@
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
         }
+
+        int result = EVENT_POST_IME;
         if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
             if (LOCAL_LOGV)
                 Log.v(TAG, "Dispatching generic motion " + event + " to " + mView);
 
             // Dispatch to the IME before propagating down the view hierarchy.
-            // The IME will eventually call back into handleImeFinishedEvent.
-            if (mLastWasImTarget) {
-                InputMethodManager imm = InputMethodManager.peekInstance();
-                if (imm != null) {
-                    final int seq = event.getSequenceNumber();
-                    if (DEBUG_IMF)
-                        Log.v(TAG, "Sending generic motion event to IME: seq="
-                                + seq + " event=" + event);
-                    return imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
-                            mInputMethodCallback);
-                }
-            }
+            result = dispatchImeInputEvent(q);
         }
-
-        return EVENT_POST_IME;
+        return result;
     }
 
     private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
@@ -3677,6 +3668,7 @@
 
         if (xDirection != mLastJoystickXDirection) {
             if (mLastJoystickXKeyCode != 0) {
+                mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
                 enqueueInputEvent(new KeyEvent(time, time,
                         KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
@@ -3688,14 +3680,19 @@
             if (xDirection != 0 && synthesizeNewKeys) {
                 mLastJoystickXKeyCode = xDirection > 0
                         ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
-                enqueueInputEvent(new KeyEvent(time, time,
+                final KeyEvent e = new KeyEvent(time, time,
                         KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
-                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+                enqueueInputEvent(e);
+                Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+                m.setAsynchronous(true);
+                mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
             }
         }
 
         if (yDirection != mLastJoystickYDirection) {
             if (mLastJoystickYKeyCode != 0) {
+                mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
                 enqueueInputEvent(new KeyEvent(time, time,
                         KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
@@ -3707,9 +3704,13 @@
             if (yDirection != 0 && synthesizeNewKeys) {
                 mLastJoystickYKeyCode = yDirection > 0
                         ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
-                enqueueInputEvent(new KeyEvent(time, time,
+                final KeyEvent e = new KeyEvent(time, time,
                         KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
-                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+                enqueueInputEvent(e);
+                Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+                m.setAsynchronous(true);
+                mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
             }
         }
     }
@@ -3806,6 +3807,7 @@
             mInputEventConsistencyVerifier.onKeyEvent(event, 0);
         }
 
+        int result = EVENT_POST_IME;
         if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
             if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
 
@@ -3815,20 +3817,9 @@
             }
 
             // Dispatch to the IME before propagating down the view hierarchy.
-            // The IME will eventually call back into handleImeFinishedEvent.
-            if (mLastWasImTarget) {
-                InputMethodManager imm = InputMethodManager.peekInstance();
-                if (imm != null) {
-                    final int seq = event.getSequenceNumber();
-                    if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
-                            + seq + " event=" + event);
-                    return imm.dispatchKeyEvent(mView.getContext(), seq, event,
-                            mInputMethodCallback);
-                }
-            }
+            result = dispatchImeInputEvent(q);
         }
-
-        return EVENT_POST_IME;
+        return result;
     }
 
     private int deliverKeyEventPostIme(QueuedInputEvent q) {
@@ -4317,14 +4308,6 @@
         }
     }
 
-    void dispatchImeFinishedEvent(int seq, boolean handled) {
-        Message msg = mHandler.obtainMessage(MSG_IME_FINISHED_EVENT);
-        msg.arg1 = seq;
-        msg.arg2 = handled ? 1 : 0;
-        msg.setAsynchronous(true);
-        mHandler.sendMessage(msg);
-    }
-
     public void dispatchFinishInputConnection(InputConnection connection) {
         Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection);
         mHandler.sendMessage(msg);
@@ -4533,6 +4516,21 @@
         return q;
     }
 
+    int dispatchImeInputEvent(QueuedInputEvent q) {
+        if (mLastWasImTarget) {
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                final InputEvent event = q.mEvent;
+                final int seq = event.getSequenceNumber();
+                if (DEBUG_IMF)
+                    Log.v(TAG, "Sending input event to IME: seq=" + seq + " event=" + event);
+                return imm.dispatchInputEvent(mView.getContext(), seq, event,
+                        mInputMethodCallback);
+            }
+        }
+        return EVENT_POST_IME;
+    }
+
     void handleImeFinishedEvent(int seq, boolean handled) {
         QueuedInputEvent q = mCurrentInputEventHead;
         if (q != null && q.mEvent.getSequenceNumber() == seq) {
@@ -5132,7 +5130,7 @@
         public void finishedEvent(int seq, boolean handled) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
-                viewAncestor.dispatchImeFinishedEvent(seq, handled);
+                viewAncestor.handleImeFinishedEvent(seq, handled);
             }
         }
     }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e602eb7..4207832 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -19,7 +19,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.view.IInputConnectionWrapper;
 import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.IInputMethodSession;
@@ -40,8 +39,10 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventSender;
 import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewRootImpl;
 
@@ -319,6 +320,8 @@
      * The actual instance of the method to make calls on it.
      */
     IInputMethodSession mCurMethod;
+    InputChannel mCurChannel;
+    ImeInputEventSender mCurSender;
 
     PendingEvent mPendingEventPool;
     int mPendingEventPoolSize;
@@ -363,10 +366,17 @@
                         if (mBindSequence < 0 || mBindSequence != res.sequence) {
                             Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
                                     + ", given seq=" + res.sequence);
+                            if (res.channel != null) {
+                                res.channel.dispose();
+                            }
                             return;
                         }
                         
                         mCurMethod = res.method;
+                        if (mCurChannel != null) {
+                            mCurChannel.dispose();
+                        }
+                        mCurChannel = res.channel;
                         mCurId = res.id;
                         mBindSequence = res.sequence;
                     }
@@ -482,10 +492,10 @@
     }
     
     final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
-        @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
             // No need to check for dump permission, since we only give this
             // interface to the system.
-            
             CountDownLatch latch = new CountDownLatch(1);
             SomeArgs sargs = SomeArgs.obtain();
             sargs.arg1 = fd;
@@ -501,32 +511,29 @@
                 fout.println("Interrupted waiting for dump");
             }
         }
-        
+
+        @Override
         public void setUsingInputMethod(boolean state) {
         }
-        
+
+        @Override
         public void onBindMethod(InputBindResult res) {
             mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
         }
-        
+
+        @Override
         public void onUnbindMethod(int sequence) {
             mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
         }
-        
+
+        @Override
         public void setActive(boolean active) {
             mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
         }
-    };    
-    
+    };
+
     final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
 
-    final IInputMethodCallback mInputMethodCallback = new IInputMethodCallback.Stub() {
-        @Override
-        public void finishedEvent(int seq, boolean handled) {
-            InputMethodManager.this.finishedEvent(seq, handled);
-        }
-    };
-    
     InputMethodManager(IInputMethodManager service, Looper looper) {
         mService = service;
         mMainLooper = looper;
@@ -714,6 +721,14 @@
         mBindSequence = -1;
         mCurId = null;
         mCurMethod = null;
+        if (mCurSender != null) {
+            mCurSender.dispose();
+            mCurSender = null;
+        }
+        if (mCurChannel != null) {
+            mCurChannel.dispose();
+            mCurChannel = null;
+        }
     }
     
     /**
@@ -1085,6 +1100,7 @@
             // we need to reschedule our work for over there.
             if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
             vh.post(new Runnable() {
+                @Override
                 public void run() {
                     startInputInner(null, 0, 0, 0);
                 }
@@ -1158,11 +1174,20 @@
                     if (res.id != null) {
                         mBindSequence = res.sequence;
                         mCurMethod = res.method;
+                        if (mCurChannel != null) {
+                            mCurChannel.dispose();
+                        }
+                        mCurChannel = res.channel;
                         mCurId = res.id;
-                    } else if (mCurMethod == null) {
-                        // This means there is no input method available.
-                        if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
-                        return true;
+                    } else {
+                        if (res.channel != null) {
+                            res.channel.dispose();
+                        }
+                        if (mCurMethod == null) {
+                            // This means there is no input method available.
+                            if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
+                            return true;
+                        }
                     }
                 }
                 if (mCurMethod != null && mCompletions != null) {
@@ -1556,76 +1581,39 @@
             throw new RuntimeException(e);
         }
     }
-    
+
     /**
      * @hide
      */
-    public int dispatchKeyEvent(Context context, int seq, KeyEvent key,
+    public int dispatchInputEvent(Context context, int seq, InputEvent event,
             FinishedEventCallback callback) {
         synchronized (mH) {
-            if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
+            if (DEBUG) Log.d(TAG, "dispatchInputEvent");
 
             if (mCurMethod != null) {
-                if (key.getAction() == KeyEvent.ACTION_DOWN
-                        && key.getKeyCode() == KeyEvent.KEYCODE_SYM
-                        && key.getRepeatCount() == 0) {
-                    showInputMethodPickerLocked();
-                    return ViewRootImpl.EVENT_HANDLED;
+                if (event instanceof KeyEvent) {
+                    KeyEvent keyEvent = (KeyEvent)event;
+                    if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
+                            && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
+                            && keyEvent.getRepeatCount() == 0) {
+                        showInputMethodPickerLocked();
+                        return ViewRootImpl.EVENT_HANDLED;
+                    }
                 }
-                try {
-                    if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
-                    final long startTime = SystemClock.uptimeMillis();
-                    enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                    mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_PENDING_IME;
-                } catch (RemoteException e) {
-                    Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
-                }
-            }
-        }
-        return ViewRootImpl.EVENT_POST_IME;
-    }
 
-    /**
-     * @hide
-     */
-    public int dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
-            FinishedEventCallback callback) {
-        synchronized (mH) {
-            if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
-
-            if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
-                try {
-                    if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
-                    final long startTime = SystemClock.uptimeMillis();
-                    enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                    mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_PENDING_IME;
-                } catch (RemoteException e) {
-                    Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
-                }
-            }
-        }
-        return ViewRootImpl.EVENT_POST_IME;
-    }
-
-    /**
-     * @hide
-     */
-    public int dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
-            FinishedEventCallback callback) {
-        synchronized (mH) {
-            if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent");
-
-            if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
-                try {
-                    if (DEBUG) Log.v(TAG, "DISPATCH GENERIC MOTION: " + mCurMethod);
-                    final long startTime = SystemClock.uptimeMillis();
-                    enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                    mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_PENDING_IME;
-                } catch (RemoteException e) {
-                    Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e);
+                if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
+                final long startTime = SystemClock.uptimeMillis();
+                if (mCurChannel != null) {
+                    if (mCurSender == null) {
+                        mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
+                    }
+                    if (mCurSender.sendInputEvent(seq, event)) {
+                        enqueuePendingEventLocked(startTime, seq, mCurId, callback);
+                        return ViewRootImpl.EVENT_PENDING_IME;
+                    } else {
+                        Log.w(TAG, "Unable to send input event to IME: "
+                                + mCurId + " dropping: " + event);
+                    }
                 }
             }
         }
@@ -1937,6 +1925,17 @@
         public void finishedEvent(int seq, boolean handled);
     }
 
+    private final class ImeInputEventSender extends InputEventSender {
+        public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
+            super(inputChannel, looper);
+        }
+
+        @Override
+        public void onInputEventFinished(int seq, boolean handled) {
+            finishedEvent(seq, handled);
+        }
+    }
+
     private static final class PendingEvent {
         public PendingEvent mNext;
 
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index c737148..b2073b1 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -228,14 +228,6 @@
         mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
     }
 
-    public AppSecurityPermissions(Context context, List<PermissionInfo> permList) {
-        this(context);
-        for (PermissionInfo pi : permList) {
-            mPermsList.add(new MyPermissionInfo(pi));
-        }
-        setPermissions(mPermsList);
-    }
-    
     public AppSecurityPermissions(Context context, String packageName) {
         this(context);
         Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
@@ -515,16 +507,27 @@
     private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
             int existingReqFlags) {
         final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
-        // Dangerous and normal permissions are always shown to the user.
-        if (base == PermissionInfo.PROTECTION_DANGEROUS ||
-                base == PermissionInfo.PROTECTION_NORMAL) {
+        final boolean isNormal = (base == PermissionInfo.PROTECTION_NORMAL);
+        final boolean isDangerous = (base == PermissionInfo.PROTECTION_DANGEROUS);
+        final boolean isRequired =
+                ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
+        final boolean isDevelopment =
+                ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
+        final boolean wasGranted =
+                ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
+        final boolean isGranted =
+                ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
+
+        // Dangerous and normal permissions are always shown to the user if the permission
+        // is required, or it was previously granted
+        if ((isNormal || isDangerous) && (isRequired || wasGranted || isGranted)) {
             return true;
         }
+
         // Development permissions are only shown to the user if they are already
         // granted to the app -- if we are installing an app and they are not
         // already granted, they will not be granted as part of the install.
-        if ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0
-                && (pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+        if (isDevelopment && wasGranted) {
             if (localLOGV) Log.i(TAG, "Special perm " + pInfo.name
                     + ": protlevel=0x" + Integer.toHexString(pInfo.protectionLevel));
             return true;
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 0dd567b..7b81aa8 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -36,6 +36,8 @@
 
 import java.util.ArrayList;
 
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
 /**
  * A view that shows items in a vertically scrolling two-level list. This
  * differs from the {@link ListView} by allowing two levels: groups which can
@@ -76,6 +78,10 @@
  * @attr ref android.R.styleable#ExpandableListView_childIndicatorLeft
  * @attr ref android.R.styleable#ExpandableListView_childIndicatorRight
  * @attr ref android.R.styleable#ExpandableListView_childDivider
+ * @attr ref android.R.styleable#ExpandableListView_indicatorStart
+ * @attr ref android.R.styleable#ExpandableListView_indicatorEnd
+ * @attr ref android.R.styleable#ExpandableListView_childIndicatorStart
+ * @attr ref android.R.styleable#ExpandableListView_childIndicatorEnd
  */
 public class ExpandableListView extends ListView {
 
@@ -134,6 +140,12 @@
     /** Right bound for drawing the indicator. */
     private int mIndicatorRight;
 
+    /** Start bound for drawing the indicator. */
+    private int mIndicatorStart;
+
+    /** End bound for drawing the indicator. */
+    private int mIndicatorEnd;
+
     /**
      * Left bound for drawing the indicator of a child. Value of
      * {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorLeft.
@@ -147,11 +159,28 @@
     private int mChildIndicatorRight;
 
     /**
+     * Start bound for drawing the indicator of a child. Value of
+     * {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorStart.
+     */
+    private int mChildIndicatorStart;
+
+    /**
+     * End bound for drawing the indicator of a child. Value of
+     * {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorEnd.
+     */
+    private int mChildIndicatorEnd;
+
+    /**
      * Denotes when a child indicator should inherit this bound from the generic
      * indicator bounds
      */
     public static final int CHILD_INDICATOR_INHERIT = -1;
-    
+
+    /**
+     * Denotes an undefined value for an indicator
+     */
+    private static final int INDICATOR_UNDEFINED = -2;
+
     /** The indicator drawn next to a group. */
     private Drawable mGroupIndicator;
 
@@ -202,30 +231,118 @@
         super(context, attrs, defStyle);
 
         TypedArray a =
-            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ExpandableListView, defStyle,
-                    0);
+            context.obtainStyledAttributes(attrs,
+                    com.android.internal.R.styleable.ExpandableListView, defStyle, 0);
 
-        mGroupIndicator = a
-                .getDrawable(com.android.internal.R.styleable.ExpandableListView_groupIndicator);
-        mChildIndicator = a
-                .getDrawable(com.android.internal.R.styleable.ExpandableListView_childIndicator);
-        mIndicatorLeft = a
-                .getDimensionPixelSize(com.android.internal.R.styleable.ExpandableListView_indicatorLeft, 0);
-        mIndicatorRight = a
-                .getDimensionPixelSize(com.android.internal.R.styleable.ExpandableListView_indicatorRight, 0);
+        mGroupIndicator = a.getDrawable(
+                com.android.internal.R.styleable.ExpandableListView_groupIndicator);
+        mChildIndicator = a.getDrawable(
+                com.android.internal.R.styleable.ExpandableListView_childIndicator);
+        mIndicatorLeft = a.getDimensionPixelSize(
+                com.android.internal.R.styleable.ExpandableListView_indicatorLeft, 0);
+        mIndicatorRight = a.getDimensionPixelSize(
+                com.android.internal.R.styleable.ExpandableListView_indicatorRight, 0);
         if (mIndicatorRight == 0 && mGroupIndicator != null) {
             mIndicatorRight = mIndicatorLeft + mGroupIndicator.getIntrinsicWidth();
         }
         mChildIndicatorLeft = a.getDimensionPixelSize(
-                com.android.internal.R.styleable.ExpandableListView_childIndicatorLeft, CHILD_INDICATOR_INHERIT);
+                com.android.internal.R.styleable.ExpandableListView_childIndicatorLeft,
+                CHILD_INDICATOR_INHERIT);
         mChildIndicatorRight = a.getDimensionPixelSize(
-                com.android.internal.R.styleable.ExpandableListView_childIndicatorRight, CHILD_INDICATOR_INHERIT);
-        mChildDivider = a.getDrawable(com.android.internal.R.styleable.ExpandableListView_childDivider);
-        
+                com.android.internal.R.styleable.ExpandableListView_childIndicatorRight,
+                CHILD_INDICATOR_INHERIT);
+        mChildDivider = a.getDrawable(
+                com.android.internal.R.styleable.ExpandableListView_childDivider);
+
+        if (!isRtlCompatibilityMode()) {
+            mIndicatorStart = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ExpandableListView_indicatorStart,
+                    INDICATOR_UNDEFINED);
+            mIndicatorEnd = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ExpandableListView_indicatorEnd,
+                    INDICATOR_UNDEFINED);
+
+            mChildIndicatorStart = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ExpandableListView_childIndicatorStart,
+                    CHILD_INDICATOR_INHERIT);
+            mChildIndicatorEnd = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ExpandableListView_childIndicatorEnd,
+                    CHILD_INDICATOR_INHERIT);
+        }
+
         a.recycle();
     }
-    
-    
+
+    /**
+     * Return true if we are in RTL compatibility mode (either before Jelly Bean MR1 or
+     * RTL not supported)
+     */
+    private boolean isRtlCompatibilityMode() {
+        final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+        return targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport();
+    }
+
+    /**
+     * Return true if the application tag in the AndroidManifest has set "supportRtl" to true
+     */
+    private boolean hasRtlSupport() {
+        return mContext.getApplicationInfo().hasRtlSupport();
+    }
+
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        resolveIndicator();
+        resolveChildIndicator();
+    }
+
+    /**
+     * Resolve start/end indicator. start/end indicator always takes precedence over left/right
+     * indicator when defined.
+     */
+    private void resolveIndicator() {
+        final boolean isLayoutRtl = isLayoutRtl();
+        if (isLayoutRtl) {
+            if (mIndicatorStart >= 0) {
+                mIndicatorRight = mIndicatorStart;
+            }
+            if (mIndicatorEnd >= 0) {
+                mIndicatorLeft = mIndicatorEnd;
+            }
+        } else {
+            if (mIndicatorStart >= 0) {
+                mIndicatorLeft = mIndicatorStart;
+            }
+            if (mIndicatorEnd >= 0) {
+                mIndicatorRight = mIndicatorEnd;
+            }
+        }
+        if (mIndicatorRight == 0 && mGroupIndicator != null) {
+            mIndicatorRight = mIndicatorLeft + mGroupIndicator.getIntrinsicWidth();
+        }
+    }
+
+    /**
+     * Resolve start/end child indicator. start/end child indicator always takes precedence over
+     * left/right child indicator when defined.
+     */
+    private void resolveChildIndicator() {
+        final boolean isLayoutRtl = isLayoutRtl();
+        if (isLayoutRtl) {
+            if (mChildIndicatorStart >= CHILD_INDICATOR_INHERIT) {
+                mChildIndicatorRight = mChildIndicatorStart;
+            }
+            if (mChildIndicatorEnd >= CHILD_INDICATOR_INHERIT) {
+                mChildIndicatorLeft = mChildIndicatorEnd;
+            }
+        } else {
+            if (mChildIndicatorStart >= CHILD_INDICATOR_INHERIT) {
+                mChildIndicatorLeft = mChildIndicatorStart;
+            }
+            if (mChildIndicatorEnd >= CHILD_INDICATOR_INHERIT) {
+                mChildIndicatorRight = mChildIndicatorEnd;
+            }
+        }
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw children, etc.
@@ -1053,8 +1170,26 @@
     public void setChildIndicatorBounds(int left, int right) {
         mChildIndicatorLeft = left;
         mChildIndicatorRight = right;
+        resolveChildIndicator();
     }
-    
+
+    /**
+     * Sets the relative drawing bounds for the child indicator. For either, you can
+     * specify {@link #CHILD_INDICATOR_INHERIT} to use inherit from the general
+     * indicator's bounds.
+     *
+     * @see #setIndicatorBounds(int, int)
+     * @param start The start position (relative to the start bounds of this View)
+     *            to start drawing the indicator.
+     * @param end The end position (relative to the end bounds of this
+     *            View) to end the drawing of the indicator.
+     */
+    public void setChildIndicatorBoundsRelative(int start, int end) {
+        mChildIndicatorStart = start;
+        mChildIndicatorEnd = end;
+        resolveChildIndicator();
+    }
+
     /**
      * Sets the indicator to be drawn next to a group.
      * 
@@ -1084,8 +1219,26 @@
     public void setIndicatorBounds(int left, int right) {
         mIndicatorLeft = left;
         mIndicatorRight = right;
+        resolveIndicator();
     }
-    
+
+    /**
+     * Sets the relative drawing bounds for the indicators (at minimum, the group indicator
+     * is affected by this; the child indicator is affected by this if the
+     * child indicator bounds are set to inherit).
+     *
+     * @see #setChildIndicatorBounds(int, int)
+     * @param start The start position (relative to the start bounds of this View)
+     *            to start drawing the indicator.
+     * @param end The end position (relative to the end bounds of this
+     *            View) to end the drawing of the indicator.
+     */
+    public void setIndicatorBoundsRelative(int start, int end) {
+        mIndicatorStart = start;
+        mIndicatorEnd = end;
+        resolveIndicator();
+    }
+
     /**
      * Extra menu information specific to an {@link ExpandableListView} provided
      * to the
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index c9d2d95..529de2e 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1212,8 +1212,8 @@
 
         private int mLeft, mTop, mRight, mBottom;
 
-        private int mStart = DEFAULT_RELATIVE;
-        private int mEnd = DEFAULT_RELATIVE;
+        private int mStart = DEFAULT_MARGIN_RELATIVE;
+        private int mEnd = DEFAULT_MARGIN_RELATIVE;
 
         private boolean mRulesChanged = false;
         private boolean mIsRtlCompatibilityMode = false;
@@ -1314,7 +1314,7 @@
                         break;
                 }
             }
-
+            mRulesChanged = true;
             System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
 
             a.recycle();
@@ -1574,11 +1574,11 @@
         public void resolveLayoutDirection(int layoutDirection) {
             final boolean isLayoutRtl = isLayoutRtl();
             if (isLayoutRtl) {
-                if (mStart != DEFAULT_RELATIVE) mRight = mStart;
-                if (mEnd != DEFAULT_RELATIVE) mLeft = mEnd;
+                if (mStart != DEFAULT_MARGIN_RELATIVE) mRight = mStart;
+                if (mEnd != DEFAULT_MARGIN_RELATIVE) mLeft = mEnd;
             } else {
-                if (mStart != DEFAULT_RELATIVE) mLeft = mStart;
-                if (mEnd != DEFAULT_RELATIVE) mRight = mEnd;
+                if (mStart != DEFAULT_MARGIN_RELATIVE) mLeft = mStart;
+                if (mEnd != DEFAULT_MARGIN_RELATIVE) mRight = mEnd;
             }
 
             if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index eed3e67..eb2d1fe 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -27,6 +27,7 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.util.Log;
 
 import com.android.org.bouncycastle.util.encoders.Base64;
@@ -64,6 +65,10 @@
 
     public LocalTransport(Context context) {
         mContext = context;
+        mDataDir.mkdirs();
+        if (!SELinux.restorecon(mDataDir)) {
+            Log.e(TAG, "SELinux restorecon failed for " + mDataDir);
+        }
     }
 
     public Intent configurationIntent() {
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index bd947e9..77456da 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -18,6 +18,7 @@
 
 import android.os.IBinder;
 import android.os.ResultReceiver;
+import android.view.InputChannel;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethodSubtype;
@@ -41,7 +42,7 @@
 
     void restartInput(in IInputContext inputContext, in EditorInfo attribute);
 
-    void createSession(IInputSessionCallback  callback);
+    void createSession(in InputChannel channel, IInputSessionCallback callback);
 
     void setSessionEnabled(IInputMethodSession session, boolean enabled);
 
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index cdec254..90210ce 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -22,7 +22,6 @@
 import android.view.MotionEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.ExtractedText;
-import com.android.internal.view.IInputMethodCallback;
 
 /**
  * Sub-interface of IInputMethod which is safe to give to client applications.
@@ -40,14 +39,8 @@
     void viewClicked(boolean focusChanged);
 
     void updateCursor(in Rect newCursor);
-    
+
     void displayCompletions(in CompletionInfo[] completions);
-    
-    void dispatchKeyEvent(int seq, in KeyEvent event, IInputMethodCallback callback);
-
-    void dispatchTrackballEvent(int seq, in MotionEvent event, IInputMethodCallback callback);
-
-    void dispatchGenericMotionEvent(int seq, in MotionEvent event, IInputMethodCallback callback);
 
     void appPrivateCommand(String action, in Bundle data);
 
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 658f098..9143c61 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.InputChannel;
 
 /**
  * Bundle of information returned by input method manager about a successful
@@ -30,7 +31,12 @@
      * The input method service.
      */
     public final IInputMethodSession method;
-    
+
+    /**
+     * The input channel used to send input events to this IME.
+     */
+    public final InputChannel channel;
+
     /**
      * The ID for this input method, as found in InputMethodInfo; null if
      * no input method will be bound.
@@ -42,18 +48,25 @@
      */
     public final int sequence;
     
-    public InputBindResult(IInputMethodSession _method, String _id, int _sequence) {
+    public InputBindResult(IInputMethodSession _method, InputChannel _channel,
+            String _id, int _sequence) {
         method = _method;
+        channel = _channel;
         id = _id;
         sequence = _sequence;
     }
     
     InputBindResult(Parcel source) {
         method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
+        if (source.readInt() != 0) {
+            channel = InputChannel.CREATOR.createFromParcel(source);
+        } else {
+            channel = null;
+        }
         id = source.readString();
         sequence = source.readInt();
     }
-    
+
     @Override
     public String toString() {
         return "InputBindResult{" + method + " " + id
@@ -62,12 +75,19 @@
 
     /**
      * Used to package this object into a {@link Parcel}.
-     * 
+     *
      * @param dest The {@link Parcel} to be written.
      * @param flags The flags used for parceling.
      */
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeStrongInterface(method);
+        if (channel != null) {
+            dest.writeInt(1);
+            channel.writeToParcel(dest, 0);
+        } else {
+            dest.writeInt(0);
+        }
         dest.writeString(id);
         dest.writeInt(sequence);
     }
@@ -75,17 +95,21 @@
     /**
      * Used to make this class parcelable.
      */
-    public static final Parcelable.Creator<InputBindResult> CREATOR = new Parcelable.Creator<InputBindResult>() {
+    public static final Parcelable.Creator<InputBindResult> CREATOR =
+            new Parcelable.Creator<InputBindResult>() {
+        @Override
         public InputBindResult createFromParcel(Parcel source) {
             return new InputBindResult(source);
         }
 
+        @Override
         public InputBindResult[] newArray(int size) {
             return new InputBindResult[size];
         }
     };
 
+    @Override
     public int describeContents() {
-        return 0;
+        return channel != null ? channel.describeContents() : 0;
     }
 }
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 6bb7ac7..d1db230 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -835,6 +835,8 @@
                 (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) {
             // Don't show while in expanded mode or with empty text
             mTitleLayout.setVisibility(GONE);
+        } else {
+            mTitleLayout.setVisibility(VISIBLE);
         }
     }
 
@@ -1587,15 +1589,10 @@
                     mTitleLayout.setVisibility(VISIBLE);
                 }
             }
-            if (mTabScrollView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
-                mTabScrollView.setVisibility(VISIBLE);
-            }
-            if (mSpinner != null && mNavigationMode == ActionBar.NAVIGATION_MODE_LIST) {
-                mSpinner.setVisibility(VISIBLE);
-            }
-            if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
-                mCustomNavView.setVisibility(VISIBLE);
-            }
+            if (mTabScrollView != null) mTabScrollView.setVisibility(VISIBLE);
+            if (mSpinner != null) mSpinner.setVisibility(VISIBLE);
+            if (mCustomNavView != null) mCustomNavView.setVisibility(VISIBLE);
+
             mExpandedHomeLayout.setIcon(null);
             mCurrentExpandedItem = null;
             setHomeButtonEnabled(mWasHomeEnabled); // Set by expandItemActionView above
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 1e27be8..66cea9d7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -44,6 +44,7 @@
 	android_view_InputChannel.cpp \
 	android_view_InputDevice.cpp \
 	android_view_InputEventReceiver.cpp \
+	android_view_InputEventSender.cpp \
 	android_view_KeyEvent.cpp \
 	android_view_KeyCharacterMap.cpp \
 	android_view_HardwareRenderer.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 86d3cb6..1300d01 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -163,6 +163,7 @@
 extern int register_android_view_InputChannel(JNIEnv* env);
 extern int register_android_view_InputDevice(JNIEnv* env);
 extern int register_android_view_InputEventReceiver(JNIEnv* env);
+extern int register_android_view_InputEventSender(JNIEnv* env);
 extern int register_android_view_KeyCharacterMap(JNIEnv *env);
 extern int register_android_view_KeyEvent(JNIEnv* env);
 extern int register_android_view_MotionEvent(JNIEnv* env);
@@ -1195,6 +1196,7 @@
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_view_InputChannel),
     REG_JNI(register_android_view_InputEventReceiver),
+    REG_JNI(register_android_view_InputEventSender),
     REG_JNI(register_android_view_KeyEvent),
     REG_JNI(register_android_view_MotionEvent),
     REG_JNI(register_android_view_PointerIcon),
diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
index 42d408d..f5eaf94 100644
--- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp
+++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
@@ -2,16 +2,16 @@
 **
 ** Copyright 2006, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
@@ -23,20 +23,17 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <utils/Log.h>
+#include <utils/String8.h>
 
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#if defined(__arm__)
-extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 
-        size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
-        
-extern "C" void free_malloc_leak_info(uint8_t* info);
-#endif
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
+                                     size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
 
-#define MAPS_FILE_SIZE 65 * 1024
+extern "C" void free_malloc_leak_info(uint8_t* info);
 
 struct Header {
     size_t mapSize;
@@ -48,96 +45,57 @@
 
 namespace android {
 
+static void ReadFile(const char* path, String8& s) {
+    int fd = open(path, O_RDONLY);
+    if (fd != -1) {
+        char bytes[1024];
+        ssize_t byteCount;
+        while ((byteCount = TEMP_FAILURE_RETRY(read(fd, bytes, sizeof(bytes)))) > 0) {
+            s.append(bytes, byteCount);
+        }
+        close(fd);
+    }
+}
+
 /*
- * Retrieve the native heap information and the info from /proc/<self>/maps,
+ * Retrieve the native heap information and the info from /proc/self/maps,
  * copy them into a byte[] with a "struct Header" that holds data offsets,
  * and return the array.
  */
-static jbyteArray getLeakInfo(JNIEnv *env, jobject clazz)
-{
-#if defined(__arm__)
-    // get the info in /proc/[pid]/map
+static jbyteArray DdmHandleNativeHeap_getLeakInfo(JNIEnv* env, jobject) {
     Header header;
     memset(&header, 0, sizeof(header));
 
-    pid_t pid = getpid();
-
-    char path[FILENAME_MAX];
-    sprintf(path, "/proc/%d/maps", pid);
-
-    struct stat sb;
-    int ret = stat(path, &sb);
-
-    uint8_t* mapsFile = NULL;
-    if (ret == 0) {
-        mapsFile = (uint8_t*)malloc(MAPS_FILE_SIZE);
-        int fd = open(path, O_RDONLY);
-    
-        if (mapsFile != NULL && fd != -1) {
-            int amount = 0;
-            do {
-                uint8_t* ptr = mapsFile + header.mapSize;
-                amount = read(fd, ptr, MAPS_FILE_SIZE);
-                if (amount <= 0) {
-                    if (errno != EINTR)
-                        break; 
-                    else
-                        continue;
-                }
-                header.mapSize += amount;
-            } while (header.mapSize < MAPS_FILE_SIZE);
-            
-            ALOGD("**** read %d bytes from '%s'", (int) header.mapSize, path);
-        }
-    }
+    String8 maps;
+    ReadFile("/proc/self/maps", maps);
+    header.mapSize = maps.size();
 
     uint8_t* allocBytes;
-    get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize, 
-            &header.totalMemory, &header.backtraceSize);
+    get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize,
+                         &header.totalMemory, &header.backtraceSize);
 
-    jbyte* bytes = NULL;
-    jbyte* ptr = NULL;
+    ALOGD("*** mapSize: %d allocSize: %d allocInfoSize: %d totalMemory: %d",
+          header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory);
+
     jbyteArray array = env->NewByteArray(sizeof(Header) + header.mapSize + header.allocSize);
-    if (array == NULL) {
-        goto done;
+    if (array != NULL) {
+        env->SetByteArrayRegion(array, 0,
+                                sizeof(header), reinterpret_cast<jbyte*>(&header));
+        env->SetByteArrayRegion(array, sizeof(header),
+                                maps.size(), reinterpret_cast<const jbyte*>(maps.string()));
+        env->SetByteArrayRegion(array, sizeof(header) + maps.size(),
+                                header.allocSize, reinterpret_cast<jbyte*>(allocBytes));
     }
 
-    bytes = env->GetByteArrayElements(array, NULL);
-    ptr = bytes;
-
-//    ALOGD("*** mapSize: %d allocSize: %d allocInfoSize: %d totalMemory: %d", 
-//            header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory);
-
-    memcpy(ptr, &header, sizeof(header));
-    ptr += sizeof(header);
-    
-    if (header.mapSize > 0 && mapsFile != NULL) {
-        memcpy(ptr, mapsFile, header.mapSize);
-        ptr += header.mapSize;
-    }
-    
-    memcpy(ptr, allocBytes, header.allocSize);
-    env->ReleaseByteArrayElements(array, bytes, 0);
-
-done:
-    if (mapsFile != NULL) {
-        free(mapsFile);
-    }
-    // free the info up!
     free_malloc_leak_info(allocBytes);
-
     return array;
-#else
-    return NULL;
-#endif
 }
 
 static JNINativeMethod method_table[] = {
-    { "getLeakInfo", "()[B", (void*)getLeakInfo },
+    { "getLeakInfo", "()[B", (void*) DdmHandleNativeHeap_getLeakInfo },
 };
 
-int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env)
-{
+int register_android_ddm_DdmHandleNativeHeap(JNIEnv* env) {
     return AndroidRuntime::registerNativeMethods(env, "android/ddm/DdmHandleNativeHeap", method_table, NELEM(method_table));
 }
 
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
new file mode 100644
index 0000000..bd1d103
--- /dev/null
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputEventSender"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about the dispatch cycle.
+#define DEBUG_DISPATCH_CYCLE 0
+
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/threads.h>
+#include <utils/KeyedVector.h>
+#include <androidfw/InputTransport.h>
+#include "android_os_MessageQueue.h"
+#include "android_view_InputChannel.h"
+#include "android_view_KeyEvent.h"
+#include "android_view_MotionEvent.h"
+
+namespace android {
+
+static struct {
+    jclass clazz;
+
+    jmethodID dispatchInputEventFinished;
+} gInputEventSenderClassInfo;
+
+
+class NativeInputEventSender : public LooperCallback {
+public:
+    NativeInputEventSender(JNIEnv* env,
+            jobject senderObj, const sp<InputChannel>& inputChannel,
+            const sp<MessageQueue>& messageQueue);
+
+    status_t initialize();
+    void dispose();
+    status_t sendKeyEvent(uint32_t seq, const KeyEvent* event);
+    status_t sendMotionEvent(uint32_t seq, const MotionEvent* event);
+
+protected:
+    virtual ~NativeInputEventSender();
+
+private:
+    jobject mSenderObjGlobal;
+    InputPublisher mInputPublisher;
+    sp<MessageQueue> mMessageQueue;
+    KeyedVector<uint32_t, uint32_t> mPublishedSeqMap;
+    uint32_t mNextPublishedSeq;
+
+    const char* getInputChannelName() {
+        return mInputPublisher.getChannel()->getName().string();
+    }
+
+    virtual int handleEvent(int receiveFd, int events, void* data);
+    status_t receiveFinishedSignals(JNIEnv* env);
+};
+
+
+NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
+        jobject senderObj, const sp<InputChannel>& inputChannel,
+        const sp<MessageQueue>& messageQueue) :
+        mSenderObjGlobal(env->NewGlobalRef(senderObj)),
+        mInputPublisher(inputChannel), mMessageQueue(messageQueue),
+        mNextPublishedSeq(0) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName());
+#endif
+}
+
+NativeInputEventSender::~NativeInputEventSender() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->DeleteGlobalRef(mSenderObjGlobal);
+}
+
+status_t NativeInputEventSender::initialize() {
+    int receiveFd = mInputPublisher.getChannel()->getFd();
+    mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
+    return OK;
+}
+
+void NativeInputEventSender::dispose() {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName());
+#endif
+
+    mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
+}
+
+status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq);
+#endif
+
+    uint32_t publishedSeq = mNextPublishedSeq++;
+    status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
+            event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
+            event->getKeyCode(), event->getScanCode(), event->getMetaState(),
+            event->getRepeatCount(), event->getDownTime(), event->getEventTime());
+    if (status) {
+        ALOGW("Failed to send key event on channel '%s'.  status=%d",
+                getInputChannelName(), status);
+        return status;
+    }
+    mPublishedSeqMap.add(publishedSeq, seq);
+    return OK;
+}
+
+status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName(), seq);
+#endif
+
+    uint32_t publishedSeq;
+    for (size_t i = 0; i <= event->getHistorySize(); i++) {
+        publishedSeq = mNextPublishedSeq++;
+        status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
+                event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
+                event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
+                event->getXOffset(), event->getYOffset(),
+                event->getXPrecision(), event->getYPrecision(),
+                event->getDownTime(), event->getHistoricalEventTime(i),
+                event->getPointerCount(), event->getPointerProperties(),
+                event->getHistoricalRawPointerCoords(0, i));
+        if (status) {
+            ALOGW("Failed to send motion event sample on channel '%s'.  status=%d",
+                    getInputChannelName(), status);
+            return status;
+        }
+    }
+    mPublishedSeqMap.add(publishedSeq, seq);
+    return OK;
+}
+
+int NativeInputEventSender::handleEvent(int receiveFd, int events, void* data) {
+    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
+        ALOGE("channel '%s' ~ Consumer closed input channel or an error occurred.  "
+                "events=0x%x", getInputChannelName(), events);
+        return 0; // remove the callback
+    }
+
+    if (!(events & ALOOPER_EVENT_INPUT)) {
+        ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
+                "events=0x%x", getInputChannelName(), events);
+        return 1;
+    }
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    status_t status = receiveFinishedSignals(env);
+    mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
+    return status == OK || status == NO_MEMORY ? 1 : 0;
+}
+
+status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName());
+#endif
+
+    bool skipCallbacks = false;
+    for (;;) {
+        uint32_t publishedSeq;
+        bool handled;
+        status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &handled);
+        if (status) {
+            if (status == WOULD_BLOCK) {
+                return OK;
+            }
+            ALOGE("channel '%s' ~ Failed to consume finished signals.  status=%d",
+                    getInputChannelName(), status);
+            return status;
+        }
+
+        ssize_t index = mPublishedSeqMap.indexOfKey(publishedSeq);
+        if (index >= 0) {
+            uint32_t seq = mPublishedSeqMap.valueAt(index);
+            mPublishedSeqMap.removeItemsAt(index);
+
+#if DEBUG_DISPATCH_CYCLE
+            ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
+                    "pendingEvents=%u.",
+                    getInputChannelName(), seq, handled ? "true" : "false",
+                    mPublishedSeqMap.size());
+#endif
+
+            if (!skipCallbacks) {
+                env->CallVoidMethod(mSenderObjGlobal,
+                        gInputEventSenderClassInfo.dispatchInputEventFinished,
+                        jint(seq), jboolean(handled));
+                if (env->ExceptionCheck()) {
+                    ALOGE("Exception dispatching finished signal.");
+                    skipCallbacks = true;
+                }
+            }
+        }
+    }
+}
+
+
+static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderObj,
+        jobject inputChannelObj, jobject messageQueueObj) {
+    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
+            inputChannelObj);
+    if (inputChannel == NULL) {
+        jniThrowRuntimeException(env, "InputChannel is not initialized.");
+        return 0;
+    }
+
+    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
+    if (messageQueue == NULL) {
+        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
+        return 0;
+    }
+
+    sp<NativeInputEventSender> sender = new NativeInputEventSender(env,
+            senderObj, inputChannel, messageQueue);
+    status_t status = sender->initialize();
+    if (status) {
+        String8 message;
+        message.appendFormat("Failed to initialize input event sender.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
+        return 0;
+    }
+
+    sender->incStrong(gInputEventSenderClassInfo.clazz); // retain a reference for the object
+    return reinterpret_cast<jint>(sender.get());
+}
+
+static void nativeDispose(JNIEnv* env, jclass clazz, jint senderPtr) {
+    sp<NativeInputEventSender> sender =
+            reinterpret_cast<NativeInputEventSender*>(senderPtr);
+    sender->dispose();
+    sender->decStrong(gInputEventSenderClassInfo.clazz); // drop reference held by the object
+}
+
+static jboolean nativeSendKeyEvent(JNIEnv* env, jclass clazz, jint senderPtr,
+        jint seq, jobject eventObj) {
+    sp<NativeInputEventSender> sender =
+            reinterpret_cast<NativeInputEventSender*>(senderPtr);
+    KeyEvent event;
+    android_view_KeyEvent_toNative(env, eventObj, &event);
+    status_t status = sender->sendKeyEvent(seq, &event);
+    return !status;
+}
+
+static jboolean nativeSendMotionEvent(JNIEnv* env, jclass clazz, jint senderPtr,
+        jint seq, jobject eventObj) {
+    sp<NativeInputEventSender> sender =
+            reinterpret_cast<NativeInputEventSender*>(senderPtr);
+    MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
+    status_t status = sender->sendMotionEvent(seq, event);
+    return !status;
+}
+
+
+static JNINativeMethod gMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeInit",
+            "(Landroid/view/InputEventSender;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
+            (void*)nativeInit },
+    { "nativeDispose", "(I)V",
+            (void*)nativeDispose },
+    { "nativeSendKeyEvent", "(IILandroid/view/KeyEvent;)Z",
+            (void*)nativeSendKeyEvent },
+    { "nativeSendMotionEvent", "(IILandroid/view/MotionEvent;)Z",
+            (void*)nativeSendMotionEvent },
+};
+
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className); \
+        var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+int register_android_view_InputEventSender(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "android/view/InputEventSender",
+            gMethods, NELEM(gMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    FIND_CLASS(gInputEventSenderClassInfo.clazz, "android/view/InputEventSender");
+
+    GET_METHOD_ID(gInputEventSenderClassInfo.dispatchInputEventFinished,
+            gInputEventSenderClassInfo.clazz,
+            "dispatchInputEventFinished", "(IZ)V");
+    return 0;
+}
+
+} // namespace android
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index be8f5f4..9eca0ce 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -74,6 +74,11 @@
     <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
     <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
 
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_UPDATE_OPTIONS" />
+    <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.backup.intent.RUN" />
     <protected-broadcast android:name="android.backup.intent.CLEAR" />
     <protected-broadcast android:name="android.backup.intent.INIT" />
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000..45450e4
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000..b568989
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000..e0434584
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000..f208c99
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000..94eb994
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000..1fee149
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000..abffc49
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000..a369081
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000..e33e964
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000..0a845dd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000..74b0352
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000..bfb4972
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000..d253dd4
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000..65f9ec1
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000..105da60
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000..de53be7
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000..3be0b0c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000..878c702
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 090c928..57e1797 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Aan:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer die vereiste PIN in:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Die foon sal tydelik van Wi-Fi ontkoppel terwyl dit aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gekoppel is"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Die foon sal tydelik van Wi-Fi ontkoppel terwyl dit aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gekoppel is"</string>
     <string name="select_character" msgid="3365550120617701745">"Voeg karakter in"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Stuur SMS-boodskappe"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; stuur \'n groot aantal SMS-boodskappe. Wil jy hierdie program toelaat om voort te gaan om boodskappe te stuur?"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 0ee9f29..103f0b7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"ለ፦"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"የሚፈለገውን ፒን ተይብ፦"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ፒን፦"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"ስልኩ ከ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር ተገናኝቶ ባለበት ጊዜ በጊዜያዊነት ከWi-Fi ጋር ያለው ግንኙነት ይቋረጣል"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"ስልኩ ከ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር ተገናኝቶ ባለበት ጊዜ በጊዜያዊነት ከWi-Fi ጋር ያለው ግንኙነት ይቋረጣል"</string>
     <string name="select_character" msgid="3365550120617701745">"ቁምፊ አስገባ"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"የSMS መልዕክቶች መበላክ ላይ"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ቁጥራቸው ብዙ የሆኑ የኤስ.ኤም.ኤስ. መልዕክቶችን እየላከ ነው። ይሄ መተግበሪያ መልዕክቶችን መላኩን እንዲቀጥል መፍቀድ ትፈልጋለህ?"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0c5039f..b8fc281 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"إلى:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"اكتب رقم التعريف الشخصي المطلوب:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"رقم التعريف الشخصي:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"سيتم قطع اتصال الهاتف مؤقتًا بشبكة Wi-Fi في الوقت الذي يكون فيه متصلاً بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"سيتم قطع اتصال الهاتف مؤقتًا بشبكة Wi-Fi في الوقت الذي يكون فيه متصلاً بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"إدراج حرف"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"إرسال رسائل قصيرة SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; يرسل عددًا كبيرًا من الرسائل القصيرة SMS. هل تريد السماح لهذا التطبيق بالاستمرار في إرسال الرسائل؟"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 3fbd1f8..3746e91 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Каму:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Увядзіце патрэбны PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Тэлефон будзе часова адключаны ад Wi-Fi, пакуль ён падлучаны да прылады <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Тэлефон будзе часова адключаны ад Wi-Fi, пакуль ён падлучаны да прылады <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Уставіць сімвал"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Адпраўка SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Прыкладанне &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; дасылае вялікую колькасць SMS-паведамленняў. Дазволіць гэтаму прыкладанню працягваць адпраўляць паведамленні?"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 66f56b8..4831909 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"До:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Въведете задължителния ПИН:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ПИН:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Телефонът временно ще прекрати връзката с Wi-Fi, докато е свързан с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Телефонът временно ще прекрати връзката с Wi-Fi, докато е свързан с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Вмъкване на знак"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Изпращане на SMS съобщения"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; изпраща голям брой SMS съобщения. Искате ли да разрешите на това приложение да продължи да го прави?"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 2faf80d..59323c7 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Per a:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introdueix el PIN sol·licitat:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"El telèfon es desconnectarà temporalment de la Wi-Fi mentre estigui connectat a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El telèfon es desconnectarà temporalment de la Wi-Fi mentre estigui connectat a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Insereix un caràcter"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"S\'estan enviant missatges SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; està enviant molts missatges SMS. Vols permetre que aquesta aplicació continuï enviant missatges?"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 714813c..6e067b5 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Komu:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadejte požadovaný kód PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefon se při připojení k zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> dočasně odpojí od sítě Wi-Fi"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon se při připojení k zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> dočasně odpojí od sítě Wi-Fi"</string>
     <string name="select_character" msgid="3365550120617701745">"Vkládání znaků"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Odesílání zpráv SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;odesílá velký počet SMS zpráv. Chcete aplikaci povolit, aby zprávy odesílala i nadále?"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ed3aa40..ea9c3ad 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Til:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv den påkrævede pinkode:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pinkode:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefonens Wi-Fi-forbindelse vil midlertidigt blive afbrudt, når den er tilsluttet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonens Wi-Fi-forbindelse vil midlertidigt blive afbrudt, når den er tilsluttet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Indsæt tegn"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sender sms-beskeder"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sender et stort antal sms-beskeder. Vil du tillade, at denne app fortsat sender beskeder?"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 7ebea96..f3f897a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"An:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Geben Sie die erforderliche PIN ein:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Das Telefon wird vorübergehend vom WLAN getrennt, während eine Verbindung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hergestellt wird."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Das Telefon wird vorübergehend vom WLAN getrennt, während eine Verbindung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hergestellt wird."</string>
     <string name="select_character" msgid="3365550120617701745">"Zeichen einfügen"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS werden gesendet"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sendet eine große Anzahl SMS. Möchten Sie zulassen, dass die App weiterhin Nachrichten sendet?"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 4ac9aab..00fbd8f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Προς:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Πληκτρολογήστε τον απαιτούμενο κωδικό PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Το τηλέφωνο θα αποσυνδεθεί προσωρινά από το δίκτυο Wi-Fi ενώ συνδέεται στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Το τηλέφωνο θα αποσυνδεθεί προσωρινά από το δίκτυο Wi-Fi ενώ συνδέεται στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Εισαγωγή χαρακτήρα"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Αποστολή μηνυμάτων SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Η εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; στέλνει έναν μεγάλο αριθμό μηνυμάτων SMS. Θέλετε να επιτρέψετε σε αυτήν την εφαρμογή να συνεχίσει να στέλνει μηνύματα;"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index f0390be..0314fc9 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"To:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Type the required PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"The phone will temporarily disconnect from Wi-FI while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"The phone will temporarily disconnect from Wi-FI while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Insert character"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sending SMS messages"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; is sending a large number of SMS messages. Do you want to allow this app to continue sending messages?"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3a39920..c26998a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"El dispositivo se desconectará temporalmente de la red Wi-Fi mientras esté conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El dispositivo se desconectará temporalmente de la red Wi-Fi mientras esté conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Insertar caracteres"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; está enviando una gran cantidad de mensajes SMS. ¿Quieres permitir que está aplicación siga enviando mensajes?"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 2217e30..b709983 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"El teléfono se desconectará temporalmente de la red Wi-Fi mientras está conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El teléfono se desconectará temporalmente de la red Wi-Fi mientras está conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Insertar carácter"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS..."</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; está enviando un gran número de mensajes SMS. ¿Quieres permitir que está aplicación siga enviando mensajes?"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index ce8bd81..3abaf62e 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Saaja:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Sisestage nõutav PIN-kood:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kood:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefoni ühendus WiFi-ga katkestatakse ajutiselt, kui see on ühendatud seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefoni ühendus WiFi-ga katkestatakse ajutiselt, kui see on ühendatud seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Sisesta tähemärk"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-sõnumite saatmine"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; saadab suurel hulgal SMS-sõnumeid. Kas tahate lubada sellel rakendusel ka edaspidi sõnumeid saata?"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 49325e1..f8c1629 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"به:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"پین لازم را تایپ کنید:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"پین:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"این گوشی به‌طور موقت از Wi-Fi قطع خواهد شد، در حالی که به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> وصل است"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"این گوشی به‌طور موقت از Wi-Fi قطع خواهد شد، در حالی که به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> وصل است"</string>
     <string name="select_character" msgid="3365550120617701745">"درج نویسه"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"ارسال پیامک ها"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; در حال ارسال تعداد زیادی پیامک است. آیا اجازه می‌دهید این برنامه همچنان پیامک ارسال کند؟"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3711f19..da5c2db 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kohde:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Kirjoita pyydetty PIN-koodi:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-koodi:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Puhelimen yhteys wifi-verkkoon katkaistaan väliaikaisesti puhelimen ollessa yhdistettynä laitteeseen <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Puhelimen yhteys wifi-verkkoon katkaistaan väliaikaisesti puhelimen ollessa yhdistettynä laitteeseen <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Lisää merkki"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Tekstiviestien lähettäminen"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lähettää suuria määriä tekstiviestejä. Annetaanko tämän sovelluksen jatkaa tekstiviestien lähettämistä?"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index dcd6ffd..c9a0eb5 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -906,7 +906,7 @@
     <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permettre à l\'application titulaire d\'accéder à des fournisseurs de contenu depuis la commande shell. Les applications standards ne devraient jamais avoir recours à cette autorisation."</string>
     <string name="permlab_updateLock" msgid="3527558366616680889">"déconseiller mises à jour auto appareil"</string>
     <string name="permdesc_updateLock" msgid="1655625832166778492">"Permet à l\'application autorisée d\'indiquer au système le moment opportun pour un redémarrage non interactif en vue de la mise à jour de l\'appareil."</string>
-    <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
+    <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Pas maintenant"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Mémoriser"</string>
     <string name="save_password_never" msgid="8274330296785855105">"Jamais"</string>
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"À :"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Saisissez le code PIN requis :"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Code PIN :"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Le téléphone sera déconnecté du réseau Wi-Fi tant qu\'il sera connecté à l\'appareil <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Le téléphone sera déconnecté du réseau Wi-Fi tant qu\'il sera connecté à l\'appareil <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Insérer un caractère"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Envoi de messages SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envoie un grand nombre de SMS. Autorisez-vous cette application à poursuivre l\'envoi des messages ?"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c0bd858..cbec3fe 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"प्रति:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"आवश्‍यक पिन लिखें:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"पिन:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"फ़ोन <xliff:g id="DEVICE_NAME">%1$s</xliff:g> से कनेक्ट रहते समय Wi-Fi से अस्थायी रूप से डिस्कनेक्ट हो जाएगा"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"फ़ोन <xliff:g id="DEVICE_NAME">%1$s</xliff:g> से कनेक्ट रहते समय Wi-Fi से अस्थायी रूप से डिस्कनेक्ट हो जाएगा"</string>
     <string name="select_character" msgid="3365550120617701745">"वर्ण सम्‍मिलित करें"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS संदेश भेज रहा है"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; बड़ी संख्या में SMS संदेश भेज रहा है. क्या आप इस एप्लिकेशन को संदेश भेजना जारी रखने देना चाहते हैं?"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 1564866..6eb3863 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Prima:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Upišite potreban PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefon će se privremeno isključiti s Wi-Fija dok je povezan s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon će se privremeno isključiti s Wi-Fija dok je povezan s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Umetni znak"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Slanje SMS poruka"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje veliki broj SMS poruka. Želite li dopustiti ovoj aplikaciji da nastavi slati poruke?"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index e7dd28e..b621899 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Címzett:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Adja meg a szükséges PIN kódot:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kód:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"A telefon ideiglenesen kilép a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"A telefon ideiglenesen kilép a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik."</string>
     <string name="select_character" msgid="3365550120617701745">"Karakter beszúrása"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-ek küldése"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/ b&gt; nagyszámú SMS üzenetet küld. Engedélyezi, hogy ez az alkalmazás továbbra is üzeneteket küldjön?"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 88b5a2d..2d8d75d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kepada:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ketik PIN yang diminta:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Ponsel akan terputus sementara dari Wi-Fi saat tersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Ponsel akan terputus sementara dari Wi-Fi saat tersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Sisipkan huruf"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Mengirim pesan SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sedang mengirim pesan SMS dalam jumlah besar. Izinkan aplikasi ini untuk melanjutkan pengiriman pesan?"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index fab4740..4486e69 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"A:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Inserisci il PIN richiesto:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Il telefono verrà momentaneamente scollegato dalla rete Wi-Fi durante il collegamento a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Il telefono verrà momentaneamente scollegato dalla rete Wi-Fi durante il collegamento a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Inserisci carattere"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Invio SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sta inviando molti SMS. Vuoi consentire all\'applicazione di continuare a inviare messaggi?"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 763a105..17e8a37 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"אל:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"הקלד את קוד ה-PIN הנדרש."</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"הטלפון יתנתק מרשת ה-Wi-Fi באופן זמני בשעה שהוא מחובר אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"הטלפון יתנתק מרשת ה-Wi-Fi באופן זמני בשעה שהוא מחובר אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"הוסף תו"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"שולח הודעות SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt; <xliff:g id="APP_NAME">%1$s</xliff:g> &lt;/ b&gt; שולח מספר רב של הודעות SMS. האם ברצונך לאפשר ליישום זה להמשיך לשלוח הודעות?"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 83925af..2b6cb00 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"To:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"必要なPINを入力してください:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"携帯端末が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に接続されている間は一時的にWi-Fi接続が解除されます。"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"携帯端末が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に接続されている間は一時的にWi-Fi接続が解除されます。"</string>
     <string name="select_character" msgid="3365550120617701745">"文字を挿入"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMSメッセージの送信中"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;が大量のSMSメッセージを送信しています。このアプリにこのままメッセージの送信を許可しますか?"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index ce657f8..a810269 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"받는사람:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"필수 PIN 입력:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 연결되어 있는 동안 일시적으로 휴대전화의 Wi-Fi 연결이 해제됩니다."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 연결되어 있는 동안 일시적으로 휴대전화의 Wi-Fi 연결이 해제됩니다."</string>
     <string name="select_character" msgid="3365550120617701745">"문자 삽입"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS 메시지를 보내는 중"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;이(가) SMS 메시지를 대량으로 보내고 있습니다. 해당 앱이 메시지를 계속 보내도록 하시겠습니까?"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 9922744..1a48fac 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Skirta:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Įveskite reikiamą PIN kodą:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kodas:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefonas bus laikinai atjungtas nuo „Wi-Fi“, kol bus prijungtas prie „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonas bus laikinai atjungtas nuo „Wi-Fi“, kol bus prijungtas prie „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
     <string name="select_character" msgid="3365550120617701745">"Įterpti simbolį"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS pranešimų siuntimas"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Naudojant &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; siunčiama daug SMS pranešimų. Ar norite leisti šiai programai toliau siųsti pranešimus?"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 861f37e..dcaf8e3 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kam:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ierakstiet pieprasīto PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Tālrunis tiks īslaicīgi atvienots no Wi-Fi tīkla, kamēr būs izveidots savienojums ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Tālrunis tiks īslaicīgi atvienots no Wi-Fi tīkla, kamēr būs izveidots savienojums ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Ievietojiet rakstzīmi"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Īsziņu sūtīšana"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Lietotne &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sūta daudz īsziņu. Vai vēlaties, lai šī lietotne turpinātu sūtīt ziņojumus?"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index c42697e..be04f9d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kepada:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Taipkan PIN yang diperlukan:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Sambungan telefon ke Wi-Fi akan diputuskan buat sementara waktu semasa telefon bersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Sambungan telefon ke Wi-Fi akan diputuskan buat sementara waktu semasa telefon bersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Masukkan aksara"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Menghantar mesej SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sedang menghantar banyak mesej SMS. Adakah anda mahu membenarkan apl ini terus menghantar mesej?"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 2d4f9cd..387676f 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Til:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv inn påkrevd PIN-kode:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefonen frakobles Wi-Fi midlertidig mens den er tilkoblet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonen frakobles Wi-Fi midlertidig mens den er tilkoblet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Sett inn tegn"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sender SMS-meldinger"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sender et stort antall SMS. Vil du la appen fortsette å sende ut meldinger?"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 85590ce..38619e5 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Naar:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer de gewenste pincode in:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pincode"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"De verbinding met het wifi-netwerk wordt tijdelijk uitgeschakeld terwijl de telefoon verbonden is met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"De verbinding met het wifi-netwerk wordt tijdelijk uitgeschakeld terwijl de telefoon verbonden is met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Teken invoegen"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-berichten verzenden"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; verzendt moment een groot aantal sms-berichten. Wilt u toestaan ​​dat deze app berichten blijft verzenden?"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 9bcd359..a491104 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Do:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Wpisz wymagany kod PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Kod PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Na czas połączenia z <xliff:g id="DEVICE_NAME">%1$s</xliff:g> telefon zostanie tymczasowo odłączony od Wi-Fi"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Na czas połączenia z <xliff:g id="DEVICE_NAME">%1$s</xliff:g> telefon zostanie tymczasowo odłączony od Wi-Fi"</string>
     <string name="select_character" msgid="3365550120617701745">"Wstaw znak"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Wysyłanie wiadomości SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; wysyła wiele SMS-ów. Chcesz pozwolić tej aplikacji dalej wysyłać SMS-y?"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 56a98ed..646bc3a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduza o PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"O telemóvel irá desligar-se temporariamente da rede Wi-Fi enquanto está ligado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"O telemóvel irá desligar-se temporariamente da rede Wi-Fi enquanto está ligado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Introduzir carácter"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"A enviar mensagens SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; está a enviar um grande número de mensagens SMS. Pretende autorizar que a aplicação continue a enviar mensagens?"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 64e02f0..873c9db 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Digite o PIN obrigatório:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"O telefone desconectará temporariamente da rede Wi-Fi enquanto estiver conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"O telefone desconectará temporariamente da rede Wi-Fi enquanto estiver conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Inserir caractere"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensagens SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envia uma grande quantidade de mensagens SMS. Deseja permitir que este aplicativo continue enviando mensagens?"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index c081d7f..e934a08 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1785,6 +1785,8 @@
     <skip />
     <!-- no translation found for wifi_p2p_show_pin_message (8530563323880921094) -->
     <skip />
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
     <!-- no translation found for wifi_p2p_frequency_conflict_message (7363907213787469151) -->
     <skip />
     <string name="select_character" msgid="3365550120617701745">"Inserir in caracter"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index f711741..c44b362 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Către:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduceţi codul PIN necesar:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Cod PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefonul se va deconecta temporar de la reţeaua Wi-Fi cât timp este conectat la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonul se va deconecta temporar de la reţeaua Wi-Fi cât timp este conectat la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Introduceţi caracterul"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Se trimit mesaje SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; trimite un număr mare de mesaje SMS. Permiteţi acestei aplicaţii să trimită în continuare mesaje?"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index d230f8a..7152bb8 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Кому:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введите PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"При подключении к устройству <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон будет временно отключаться от сети Wi-Fi"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"При подключении к устройству <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон будет временно отключаться от сети Wi-Fi"</string>
     <string name="select_character" msgid="3365550120617701745">"Введите символ"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Отправка SMS-сообщений"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; отправляет большое количество SMS. Разрешить приложению и дальше отправлять сообщения?"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f37ebf0..6f95ff6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Komu:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadajte požadovaný kód PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefón bude počas pripojenia k zariadeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> od siete Wi-Fi dočasne odpojený."</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefón bude počas pripojenia k zariadeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> od siete Wi-Fi dočasne odpojený."</string>
     <string name="select_character" msgid="3365550120617701745">"Vkladanie znakov"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Odosielanie správ SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Aplikácia &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; posiela veľký počet správ SMS. Chcete tejto aplikácií povoliť, aby aj naďalej posielala správy?"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index d865c7c..1486341 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Za:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Vnesite zahtevano kodo PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefon bo začasno prekinil povezavo z Wi-Fi-jem, ko je povezan z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon bo začasno prekinil povezavo z Wi-Fi-jem, ko je povezan z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Vstavljanje znaka"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Pošiljanje sporočil SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pošilja veliko SMS-ov. Ali želite dovoliti, da jih še naprej pošilja?"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 8bc5879..a1b869f 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Коме:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Унесите потребни PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Телефон ће привремено прекинути везу са Wi-Fi-јем док је повезан са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Телефон ће привремено прекинути везу са Wi-Fi-јем док је повезан са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Уметање знака"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Слање SMS порука"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; шаље велики број SMS порука. Желите ли да дозволите овој апликацији да настави са слањем порука?"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 46f2d6e..4c98712 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Till:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; skickar ett stort antal SMS. Vill du tillåta att appen fortsätter att skicka meddelanden?"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 04ee91e..d1e4270 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kwa:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Charaza PIN inayohitajika:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Simu itaukata muunganisho kwa muda kutoka kwenye Wi-Fi inapokuwa imeunganishwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Simu itaukata muunganisho kwa muda kutoka kwenye Wi-Fi inapokuwa imeunganishwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Ingiza kibambo"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Inatuma ujumbe wa SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; inatuma idadi kubwa ya SMS. Je, unataka kuruhusu programu hii kuendelea kutuma SMS?"</string>
@@ -1437,7 +1439,7 @@
     <string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"Tenganisha"</string>
     <string name="kg_emergency_call_label" msgid="684946192523830531">"Simu ya dharura"</string>
     <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Umesahau Ruwaza"</string>
-    <string name="kg_wrong_pattern" msgid="1850806070801358830">"Ruwaza Isiyo sahihi"</string>
+    <string name="kg_wrong_pattern" msgid="1850806070801358830">"Mchoro Usio sahihi"</string>
     <string name="kg_wrong_password" msgid="2333281762128113157">"Nenosiri Lisilo sahihi"</string>
     <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN isiyo sahihi"</string>
     <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Jaribu tena baada ya sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 9055408..4bca1b1 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"ถึง:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"พิมพ์ PIN ที่ต้องการ:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"โทรศัพท์จะยกเลิกการเชื่อมต่อกับ Wi-Fi ชั่วคราวในขณะที่เชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"โทรศัพท์จะยกเลิกการเชื่อมต่อกับ Wi-Fi ชั่วคราวในขณะที่เชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"ใส่อักขระ"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"กำลังส่งข้อความ SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; กำลังส่งข้อความ SMS จำนวนมาก คุณต้องการอนุญาตให้แอปพลิเคชันนี้ส่งข้อความต่อหรือไม่"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 59a91c2..00c9ef8 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kay:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"I-type ang kinakailangang PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Pansamantalang madidiskoneta ang telepono sa Wi-Fi habang nakakonekta ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Pansamantalang madidiskoneta ang telepono sa Wi-Fi habang nakakonekta ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Magpasok ng character"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Nagpapadala ng mga SMS na mensahe"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ay nagpapadala ng maraming mensaheng SMS. Gusto mo bang payagan ang app na ito na magpatuloy sa pagpapadala ng mga mensahe?"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b95998f..5595a7a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Alıcı:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Gerekli PIN\'i yazın:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefon <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken Kablosuz ağ bağlantısı geçici olarak kesilecektir"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken Kablosuz ağ bağlantısı geçici olarak kesilecektir"</string>
     <string name="select_character" msgid="3365550120617701745">"Karakter ekle"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS mesajları gönderiliyor"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; çok sayıda SMS mesajı gönderiyor. Bu uygulamanın mesaj göndermeye devam etmesine izin veriyor musunuz?"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 994e8a1..49bc0c3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Кому:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введіть потрібний PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Під час з’єднання з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон тимчасово від’єднається від мережі Wi-Fi"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Під час з’єднання з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон тимчасово від’єднається від мережі Wi-Fi"</string>
     <string name="select_character" msgid="3365550120617701745">"Вставл-ня символу"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Надсил. SMS повідомлень"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Програма &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; надсилає велику кількість SMS-повідомлень. Дозволити цій програмі й надалі надсилати повідомлення?"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 0e61026..48e3fca 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Người nhận:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Nhập PIN bắt buộc:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Mã PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Điện thoại sẽ tạm thời ngắt kết nối khỏi Wi-Fi trong khi điện thoại được kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Điện thoại sẽ tạm thời ngắt kết nối khỏi Wi-Fi trong khi điện thoại được kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Chèn ký tự"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Đang gửi tin nhắn SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; đang gửi rất nhiều tin nhắn SMS. Bạn có muốn cho phép ứng dụng này tiếp tục gửi tin nhắn không?"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ec33b27..ccc66fb 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"收件人:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"键入所需的 PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"手机连接到<xliff:g id="DEVICE_NAME">%1$s</xliff:g>时会暂时断开与 Wi-Fi 的连接。"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"手机连接到<xliff:g id="DEVICE_NAME">%1$s</xliff:g>时会暂时断开与 Wi-Fi 的连接。"</string>
     <string name="select_character" msgid="3365550120617701745">"插入字符"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"正在发送短信"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在发送大量短信。是否允许该应用继续发送短信?"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 905cdfb..53f578a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"收件者:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"請輸入必要的 PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"手機與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 連線期間將暫時中斷 WiFi 連線"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"手機與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 連線期間將暫時中斷 WiFi 連線"</string>
     <string name="select_character" msgid="3365550120617701745">"插入字元"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"傳送 SMS 簡訊"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;&lt;/b&gt;「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在傳送大量簡訊。您要允許這個應用程式繼續傳送簡訊嗎?"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 52cb1b9..18f603c 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Ku:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Faka i-PIN edingekayo:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Ifoni izonqamuka okwesikhashana ku-Wi-Fi ngenkathi ixhumeke ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Ifoni izonqamuka okwesikhashana ku-Wi-Fi ngenkathi ixhumeke ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Faka uhlamvu"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Ithumela imiyalezo ye-SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"I-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ithumela inombolo enkulu yemilayezo ye-SMS. Ufuna ukuvumela lolu hlelo lokusebenza ukuqhubeka ukuthumela imilayezo?"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index badd3d7..5282df3 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2709,6 +2709,16 @@
              below and above child items.) The height of this will be the same as
              the height of the normal list item divider. -->
         <attr name="childDivider" format="reference|color" />
+        <!-- The start bound for an item's indicator. To specify a start bound specific to children,
+             use childIndicatorStart. -->
+        <attr name="indicatorStart" format="dimension" />
+        <!-- The end bound for an item's indicator. To specify a right bound specific to children,
+             use childIndicatorEnd. -->
+        <attr name="indicatorEnd" format="dimension" />
+        <!-- The start bound for a child's indicator. -->
+        <attr name="childIndicatorStart" format="dimension" />
+        <!-- The end bound for a child's indicator. -->
+        <attr name="childIndicatorEnd" format="dimension" />
     </declare-styleable>
     <declare-styleable name="Gallery">
         <attr name="gravity" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9f810af..489a947 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2037,6 +2037,11 @@
   <public type="attr" name="mirrorForRtl" />
   <public type="attr" name="windowOverscan" />
   <public type="attr" name="requiredForAllUsers" />
+  <public type="attr" name="indicatorStart"  />
+  <public type="attr" name="indicatorEnd" />
+  <public type="attr" name="childIndicatorStart" />
+  <public type="attr" name="childIndicatorEnd" />
+
   <public type="style" name="Theme.NoTitleBar.Overscan" />
   <public type="style" name="Theme.Light.NoTitleBar.Overscan" />
   <public type="style" name="Theme.Black.NoTitleBar.Overscan" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a2d4570..9e10661 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3202,7 +3202,8 @@
     <string name="wifi_p2p_enter_pin_message">Type the required PIN: </string>
     <string name="wifi_p2p_show_pin_message">PIN: </string>
 
-    <string name="wifi_p2p_frequency_conflict_message">The phone will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="device_name">%1$s</xliff:g></string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet">The tablet will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="device_name">%1$s</xliff:g></string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default">The phone will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="device_name">%1$s</xliff:g></string>
 
     <!-- Name of the dialog that lets the user choose an accented character to insert -->
     <string name="select_character">Insert character</string>
diff --git a/core/tests/benchmarks/src/com/android/internal/util/IndentingPrintWriterBenchmark.java b/core/tests/benchmarks/src/com/android/internal/util/IndentingPrintWriterBenchmark.java
new file mode 100644
index 0000000..34c73e8
--- /dev/null
+++ b/core/tests/benchmarks/src/com/android/internal/util/IndentingPrintWriterBenchmark.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import com.google.android.collect.Lists;
+import com.google.caliper.SimpleBenchmark;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class IndentingPrintWriterBenchmark extends SimpleBenchmark {
+
+    private PrintWriter mDirect;
+    private IndentingPrintWriter mIndenting;
+
+    private Node mSimple;
+    private Node mComplex;
+
+    @Override
+    protected void setUp() throws IOException {
+        final FileOutputStream os = new FileOutputStream(new File("/dev/null"));
+        mDirect = new PrintWriter(os);
+        mIndenting = new IndentingPrintWriter(mDirect, "  ");
+
+        final Node manyChildren = Node.build("ManyChildren", Node.build("1"), Node.build("2"),
+                Node.build("3"), Node.build("4"), Node.build("5"), Node.build("6"), Node.build("7"),
+                Node.build("8"), Node.build("9"), Node.build("10"));
+
+        mSimple = Node.build("RED");
+        mComplex = Node.build("PARENT", Node.build("RED"), Node.build("GREEN",
+                Node.build("BLUE", manyChildren, manyChildren), manyChildren, manyChildren),
+                manyChildren);
+    }
+
+    @Override
+    protected void tearDown() {
+        mIndenting.close();
+        mIndenting = null;
+        mDirect = null;
+    }
+
+    public void timeSimpleDirect(int reps) {
+        for (int i = 0; i < reps; i++) {
+            mSimple.dumpDirect(mDirect, 0);
+        }
+    }
+
+    public void timeSimpleIndenting(int reps) {
+        for (int i = 0; i < reps; i++) {
+            mSimple.dumpIndenting(mIndenting);
+        }
+    }
+
+    public void timeComplexDirect(int reps) {
+        for (int i = 0; i < reps; i++) {
+            mComplex.dumpDirect(mDirect, 0);
+        }
+    }
+
+    public void timeComplexIndenting(int reps) {
+        for (int i = 0; i < reps; i++) {
+            mComplex.dumpIndenting(mIndenting);
+        }
+    }
+
+    public void timePairRaw(int reps) {
+        final int value = 1024;
+        for (int i = 0; i < reps; i++) {
+            mDirect.print("key=");
+            mDirect.print(value);
+            mDirect.print(" ");
+        }
+    }
+
+    public void timePairIndenting(int reps) {
+        final int value = 1024;
+        for (int i = 0; i < reps; i++) {
+            mIndenting.printPair("key", value);
+        }
+    }
+
+    private static class Node {
+        public String name;
+        public ArrayList<Node> children;
+
+        private static String[] sIndents = new String[] { "", "  ", "    ", "      ", "        " };
+
+        public static Node build(String name, Node... children) {
+            Node node = new Node();
+            node.name = name;
+            if (children != null && children.length > 0) {
+                node.children = Lists.newArrayList(children);
+            }
+            return node;
+        }
+
+        private void dumpSelf(PrintWriter pw) {
+            pw.print("Node ");
+            pw.print(name);
+            pw.print(" first ");
+            pw.print(512);
+            pw.print(" second ");
+            pw.print(1024);
+            pw.print(" third ");
+            pw.println(2048);
+        }
+
+        public void dumpDirect(PrintWriter pw, int depth) {
+            pw.print(sIndents[depth]);
+            dumpSelf(pw);
+
+            if (children != null) {
+                for (Node child : children) {
+                    child.dumpDirect(pw, depth + 1);
+                }
+            }
+
+            pw.println();
+        }
+
+        public void dumpIndenting(IndentingPrintWriter pw) {
+            dumpSelf(pw);
+
+            if (children != null) {
+                pw.increaseIndent();
+                for (Node child : children) {
+                    child.dumpIndenting(pw);
+                }
+                pw.decreaseIndent();
+            }
+
+            pw.println();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/net/UriMatcherTest.java b/core/tests/coretests/src/android/net/UriMatcherTest.java
index 2872144..a728d4f 100644
--- a/core/tests/coretests/src/android/net/UriMatcherTest.java
+++ b/core/tests/coretests/src/android/net/UriMatcherTest.java
@@ -19,10 +19,11 @@
 import android.content.UriMatcher;
 import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
+
 import junit.framework.TestCase;
 
-public class UriMatcherTest extends TestCase
-{
+public class UriMatcherTest extends TestCase {
+
     static final int ROOT = 0;
     static final int PEOPLE = 1;
     static final int PEOPLE_ID = 2;
@@ -37,54 +38,76 @@
     static final int CALLERID = 11;
     static final int CALLERID_TEXT = 12;
     static final int FILTERRECENT = 13;
-    
+    static final int ANOTHER_PATH_SEGMENT = 13;
+
     @SmallTest
     public void testContentUris() {
-        check("content://asdf", UriMatcher.NO_MATCH);
-        check("content://people", PEOPLE);
-        check("content://people/1", PEOPLE_ID);
-        check("content://people/asdf", UriMatcher.NO_MATCH);
-        check("content://people/2/phones", PEOPLE_PHONES); 
-        check("content://people/2/phones/3", PEOPLE_PHONES_ID); 
-        check("content://people/2/phones/asdf", UriMatcher.NO_MATCH);
-        check("content://people/2/addresses", PEOPLE_ADDRESSES); 
-        check("content://people/2/addresses/3", PEOPLE_ADDRESSES_ID); 
-        check("content://people/2/addresses/asdf", UriMatcher.NO_MATCH);
-        check("content://people/2/contact-methods", PEOPLE_CONTACTMETH); 
-        check("content://people/2/contact-methods/3", PEOPLE_CONTACTMETH_ID); 
-        check("content://people/2/contact-methods/asdf", UriMatcher.NO_MATCH);
-        check("content://calls", CALLS);
-        check("content://calls/1", CALLS_ID);
-        check("content://calls/asdf", UriMatcher.NO_MATCH);
-        check("content://caller-id", CALLERID);
-        check("content://caller-id/asdf", CALLERID_TEXT);
-        check("content://caller-id/1", CALLERID_TEXT);
-        check("content://filter-recent", FILTERRECENT);
+        UriMatcher matcher = new UriMatcher(ROOT);
+        matcher.addURI("people", null, PEOPLE);
+        matcher.addURI("people", "#", PEOPLE_ID);
+        matcher.addURI("people", "#/phones", PEOPLE_PHONES);
+        matcher.addURI("people", "#/phones/blah", PEOPLE_PHONES_ID);
+        matcher.addURI("people", "#/phones/#", PEOPLE_PHONES_ID);
+        matcher.addURI("people", "#/addresses", PEOPLE_ADDRESSES);
+        matcher.addURI("people", "#/addresses/#", PEOPLE_ADDRESSES_ID);
+        matcher.addURI("people", "#/contact-methods", PEOPLE_CONTACTMETH);
+        matcher.addURI("people", "#/contact-methods/#", PEOPLE_CONTACTMETH_ID);
+        matcher.addURI("calls", null, CALLS);
+        matcher.addURI("calls", "#", CALLS_ID);
+        matcher.addURI("caller-id", null, CALLERID);
+        matcher.addURI("caller-id", "*", CALLERID_TEXT);
+        matcher.addURI("filter-recent", null, FILTERRECENT);
+        matcher.addURI("auth", "another/path/segment", ANOTHER_PATH_SEGMENT);
+        checkAll(matcher);
     }
 
-    private static final UriMatcher mURLMatcher = new UriMatcher(ROOT);
-
-    static
-    {
-        mURLMatcher.addURI("people", null, PEOPLE);
-        mURLMatcher.addURI("people", "#", PEOPLE_ID);
-        mURLMatcher.addURI("people", "#/phones", PEOPLE_PHONES);
-        mURLMatcher.addURI("people", "#/phones/blah", PEOPLE_PHONES_ID);
-        mURLMatcher.addURI("people", "#/phones/#", PEOPLE_PHONES_ID);
-        mURLMatcher.addURI("people", "#/addresses", PEOPLE_ADDRESSES);
-        mURLMatcher.addURI("people", "#/addresses/#", PEOPLE_ADDRESSES_ID);
-        mURLMatcher.addURI("people", "#/contact-methods", PEOPLE_CONTACTMETH);
-        mURLMatcher.addURI("people", "#/contact-methods/#", PEOPLE_CONTACTMETH_ID);
-        mURLMatcher.addURI("calls", null, CALLS);
-        mURLMatcher.addURI("calls", "#", CALLS_ID);
-        mURLMatcher.addURI("caller-id", null, CALLERID);
-        mURLMatcher.addURI("caller-id", "*", CALLERID_TEXT);
-        mURLMatcher.addURI("filter-recent", null, FILTERRECENT);
+    @SmallTest
+    public void testContentUrisWithLeadingSlash() {
+        UriMatcher matcher = new UriMatcher(ROOT);
+        matcher.addURI("people", null, PEOPLE);
+        matcher.addURI("people", "/#", PEOPLE_ID);
+        matcher.addURI("people", "/#/phones", PEOPLE_PHONES);
+        matcher.addURI("people", "/#/phones/blah", PEOPLE_PHONES_ID);
+        matcher.addURI("people", "/#/phones/#", PEOPLE_PHONES_ID);
+        matcher.addURI("people", "/#/addresses", PEOPLE_ADDRESSES);
+        matcher.addURI("people", "/#/addresses/#", PEOPLE_ADDRESSES_ID);
+        matcher.addURI("people", "/#/contact-methods", PEOPLE_CONTACTMETH);
+        matcher.addURI("people", "/#/contact-methods/#", PEOPLE_CONTACTMETH_ID);
+        matcher.addURI("calls", null, CALLS);
+        matcher.addURI("calls", "/#", CALLS_ID);
+        matcher.addURI("caller-id", null, CALLERID);
+        matcher.addURI("caller-id", "/*", CALLERID_TEXT);
+        matcher.addURI("filter-recent", null, FILTERRECENT);
+        matcher.addURI("auth", "/another/path/segment", ANOTHER_PATH_SEGMENT);
+        checkAll(matcher);
     }
 
-    void check(String uri, int expected)
-    {
-        int result = mURLMatcher.match(Uri.parse(uri));
+    private void checkAll(UriMatcher matcher) {
+        check("content://asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://people", PEOPLE, matcher);
+        check("content://people/1", PEOPLE_ID, matcher);
+        check("content://people/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://people/2/phones", PEOPLE_PHONES, matcher);
+        check("content://people/2/phones/3", PEOPLE_PHONES_ID, matcher);
+        check("content://people/2/phones/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://people/2/addresses", PEOPLE_ADDRESSES, matcher);
+        check("content://people/2/addresses/3", PEOPLE_ADDRESSES_ID, matcher);
+        check("content://people/2/addresses/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://people/2/contact-methods", PEOPLE_CONTACTMETH, matcher);
+        check("content://people/2/contact-methods/3", PEOPLE_CONTACTMETH_ID, matcher);
+        check("content://people/2/contact-methods/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://calls", CALLS, matcher);
+        check("content://calls/1", CALLS_ID, matcher);
+        check("content://calls/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://caller-id", CALLERID, matcher);
+        check("content://caller-id/asdf", CALLERID_TEXT, matcher);
+        check("content://caller-id/1", CALLERID_TEXT, matcher);
+        check("content://filter-recent", FILTERRECENT, matcher);
+        check("content://auth/another/path/segment", ANOTHER_PATH_SEGMENT, matcher);
+    }
+
+    private void check(String uri, int expected, UriMatcher matcher) {
+        int result = matcher.match(Uri.parse(uri));
         if (result != expected) {
             String msg = "failed on " + uri;
             msg += " expected " + expected + " got " + result;
diff --git a/docs/html/guide/topics/providers/calendar-provider.jd b/docs/html/guide/topics/providers/calendar-provider.jd
index f53b062..5adc68c 100644
--- a/docs/html/guide/topics/providers/calendar-provider.jd
+++ b/docs/html/guide/topics/providers/calendar-provider.jd
@@ -605,7 +605,7 @@
 Uri updateUri = null;
 // The new title for the event
 values.put(Events.TITLE, &quot;Kickboxing&quot;); 
-myUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
 int rows = getContentResolver().update(updateUri, values, null, null);
 Log.i(DEBUG_TAG, &quot;Rows updated: &quot; + rows);  </pre>
 
diff --git a/docs/html/training/contacts-provider/ContactsList.zip b/docs/html/training/contacts-provider/ContactsList.zip
new file mode 100644
index 0000000..d2a5cfb
--- /dev/null
+++ b/docs/html/training/contacts-provider/ContactsList.zip
Binary files differ
diff --git a/docs/html/training/contacts-provider/display-contact-badge.jd b/docs/html/training/contacts-provider/display-contact-badge.jd
new file mode 100644
index 0000000..f08935d
--- /dev/null
+++ b/docs/html/training/contacts-provider/display-contact-badge.jd
@@ -0,0 +1,635 @@
+page.title=Displaying the Quick Contact Badge
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+    <li>
+        <a href="#AddView">Add a QuickContactBadge View</a>
+    </li>
+    <li>
+        <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
+    </li>
+    <li>
+        <a href="#ListView">
+            Add a QuickContactBadge to a ListView
+        </a>
+    </li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics
+        </a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider
+        </a>
+    </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+    This lesson shows you how to add a {@link android.widget.QuickContactBadge} to your UI
+    and how to bind data to it. A {@link android.widget.QuickContactBadge} is a widget that
+    initially appears as a thumbnail image. Although you can use any {@link android.graphics.Bitmap}
+    for the thumbnail image, you usually use a {@link android.graphics.Bitmap} decoded from the
+    contact's photo thumbnail image.
+</p>
+<p>
+    The small image acts as a control; when users click on the image, the
+    {@link android.widget.QuickContactBadge} expands into a dialog containing the following:
+</p>
+<dl>
+    <dt>A large image</dt>
+    <dd>
+        The large image associated with the contact, or no image is available, a placeholder
+        graphic.
+    </dd>
+    <dt>
+        App icons
+    </dt>
+    <dd>
+        An app icon for each piece of detail data that can be handled by a built-in app. For
+        example, if the contact's details include one or more email addresses, an email icon
+        appears. When users click the icon, all of the contact's email addresses appear. When users
+        click one of the addresses, the email app displays a screen for composing a message to the
+        selected email address.
+    </dd>
+</dl>
+<p>
+    The {@link android.widget.QuickContactBadge} view provides instant access to a contact's
+    details, as well as a fast way of communicating with the contact. Users don't have to look up
+    a contact, find and copy information, and then paste it into the appropriate app. Instead, they
+    can click on the {@link android.widget.QuickContactBadge}, choose the communication method they
+    want to use, and send the information for that method directly to the appropriate app.
+</p>
+<h2 id="AddView">Add a QuickContactBadge View</h2>
+<p>
+    To add a {@link android.widget.QuickContactBadge}, insert a
+    <code>&lt;QuickContactBadge&gt;</code> element in your layout. For example:
+</p>
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"&gt;
+...
+    &lt;QuickContactBadge
+               android:id=&#64;+id/quickbadge
+               android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:scaleType="centerCrop"/&gt;
+    ...
+&lt;/RelativeLayout&gt;
+</pre>
+<h2 id="">Retrieve provider data</h2>
+<p>
+    To display a contact in the {@link android.widget.QuickContactBadge}, you need a content URI
+    for the contact and a {@link android.graphics.Bitmap} for the small image. You generate
+    both the content URI and the {@link android.graphics.Bitmap} from columns retrieved from the
+    Contacts Provider. Specify these columns as part of the projection you use to load data into
+    your {@link android.database.Cursor}.
+</p>
+<p>
+    For Android 3.0 (API level 11) and later, include the following columns in your projection:</p>
+<ul>
+    <li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
+    <li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
+    <li>
+        {@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI
+        Contacts.PHOTO_THUMBNAIL_URI}
+    </li>
+</ul>
+<p>
+    For Android 2.3.3 (API level 10) and earlier, use the following columns:
+</p>
+<ul>
+    <li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
+    <li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
+</ul>
+<p>
+    The remainder of this lesson assumes that you've already loaded a
+    {@link android.database.Cursor} that contains these columns as well as others you may have
+    chosen. To learn how to retrieve this columns in a {@link android.database.Cursor}, read the
+    lesson <a href="retrieve-names.html">Retrieving a List of Contacts</a>.
+</p>
+<h2 id="SetURIThumbnail">Set the Contact URI and Thumbnail</h2>
+<p>
+    Once you have the necessary columns, you can bind data to the
+    {@link android.widget.QuickContactBadge}.
+</p>
+<h3>Set the Contact URI</h3>
+<p>
+    To set the content URI for the contact, call
+    {@link android.provider.ContactsContract.Contacts#getLookupUri getLookupUri(id,lookupKey)} to
+    get a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, then
+    call {@link android.widget.QuickContactBadge#assignContactUri assignContactUri()} to set the
+    contact. For example:
+</p>
+<pre>
+    // The Cursor that contains contact rows
+    Cursor mCursor;
+    // The index of the _ID column in the Cursor
+    int mIdColumn;
+    // The index of the LOOKUP_KEY column in the Cursor
+    int mLookupKeyColumn;
+    // A content URI for the desired contact
+    Uri mContactUri;
+    // A handle to the QuickContactBadge view
+    QuickContactBadge mBadge;
+    ...
+    mBadge = (QuickContactBadge) findViewById(R.id.quickbadge);
+    /*
+     * Insert code here to move to the desired cursor row
+     */
+    // Gets the _ID column index
+    mIdColumn = mCursor.getColumnIndex(Contacts._ID);
+    // Gets the LOOKUP_KEY index
+    mLookupKeyColumn = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
+    // Gets a content URI for the contact
+    mContactUri =
+            Contacts.getLookupUri(
+                Cursor.getLong(mIdColumn),
+                Cursor.getString(mLookupKeyColumn)
+            );
+    mBadge.assignContactUri(mContactUri);
+</pre>
+<p>
+    When users click the {@link android.widget.QuickContactBadge} icon, the contact's
+    details automatically appear in the dialog.
+</p>
+<h3>Set the photo thumbnail</h3>
+<p>
+    Setting the contact URI for the {@link android.widget.QuickContactBadge} does not automatically
+    load the contact's thumbnail photo. To load the photo, get a URI for the photo from the
+    contact's {@link android.database.Cursor} row, use it to open the file containing the compressed
+    thumbnail photo, and read the file into a {@link android.graphics.Bitmap}.
+</p>
+<p class="note">
+    <strong>Note:</strong> The
+    {@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI} column isn't available
+    in platform versions prior to 3.0. For those versions, you must retrieve the URI
+    from the {@link android.provider.ContactsContract.Contacts.Photo Contacts.Photo} subtable.
+</p>
+<p>
+    First, set up variables for accessing the {@link android.database.Cursor} containing the
+    {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
+    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} columns, as
+    described previously:
+</p>
+<pre>
+    // The column in which to find the thumbnail ID
+    int mThumbnailColumn;
+    /*
+     * The thumbnail URI, expressed as a String.
+     * Contacts Provider stores URIs as String values.
+     */
+    String mThumbnailUri;
+    ...
+    /*
+     * Gets the photo thumbnail column index if
+     * platform version &gt;= Honeycomb
+     */
+    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB) {
+        mThumbnailColumn =
+                mCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
+    // Otherwise, sets the thumbnail column to the _ID column
+    } else {
+        mThumbnailColumn = mIdColumn;
+    }
+    /*
+     * Assuming the current Cursor position is the contact you want,
+     * gets the thumbnail ID
+     */
+    mThumbnailUri = Cursor.getString(mThumbnailColumn);
+    ...
+</pre>
+<p>
+    Define a method that takes photo-related data for the contact and dimensions for the
+    destination view,  and returns the properly-sized thumbnail in a
+    {@link android.graphics.Bitmap}. Start by constructing a URI that points to the
+    thumbnail:
+<p>
+<pre>
+    /**
+     * Load a contact photo thumbnail and return it as a Bitmap,
+     * resizing the image to the provided image dimensions as needed.
+     * @param photoData photo ID Prior to Honeycomb, the contact's _ID value.
+     * For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI.
+     * @return A thumbnail Bitmap, sized to the provided width and height.
+     * Returns null if the thumbnail is not found.
+     */
+    private Bitmap loadContactPhotoThumbnail(String photoData) {
+        // Creates an asset file descriptor for the thumbnail file.
+        AssetFileDescriptor afd = null;
+        // try-catch block for file not found
+        try {
+            // Creates a holder for the URI.
+            Uri thumbUri;
+            // If Android 3.0 or later
+            if (Build.VERSION.SDK_INT
+                    &gt;=
+                Build.VERSION_CODES.HONEYCOMB) {
+                // Sets the URI from the incoming PHOTO_THUMBNAIL_URI
+                thumbUri = Uri.parse(photoData);
+            } else {
+            // Prior to Android 3.0, constructs a photo Uri using _ID
+                /*
+                 * Creates a contact URI from the Contacts content URI
+                 * incoming photoData (_ID)
+                 */
+                final Uri contactUri = Uri.withAppendedPath(
+                        Contacts.CONTENT_URI, photoData);
+                /*
+                 * Creates a photo URI by appending the content URI of
+                 * Contacts.Photo.
+                 */
+                thumbUri =
+                        Uri.withAppendedPath(
+                                contactUri, Photo.CONTENT_DIRECTORY);
+            }
+    
+        /*
+         * Retrieves an AssetFileDescriptor object for the thumbnail
+         * URI
+         * using ContentResolver.openAssetFileDescriptor
+         */
+        afd = getActivity().getContentResolver().
+                openAssetFileDescriptor(thumbUri, "r");
+        /*
+         * Gets a file descriptor from the asset file descriptor.
+         * This object can be used across processes.
+         */
+        FileDescriptor fileDescriptor = afd.getFileDescriptor();
+        // Decode the photo file and return the result as a Bitmap
+        // If the file descriptor is valid
+        if (fileDescriptor != null) {
+            // Decodes the bitmap
+            return BitmapFactory.decodeFileDescriptor(
+                    fileDescriptor, null, null);
+            }
+        // If the file isn't found
+        } catch (FileNotFoundException e) {
+            /*
+             * Handle file not found errors
+             */
+        }
+        // In all cases, close the asset file descriptor
+        } finally {
+            if (afd != null) {
+                try {
+                    afd.close();
+                } catch (IOException e) {}
+            }
+        }
+        return null;
+    }
+</pre>
+<p>
+    Call the <code>loadContactPhotoThumbnail()</code> method in your code to get the
+    thumbnail {@link android.graphics.Bitmap}, and use the result to set the photo thumbnail in
+    your {@link android.widget.QuickContactBadge}:
+</p>
+<pre>
+    ...
+    /*
+     * Decodes the thumbnail file to a Bitmap.
+     */
+    Bitmap mThumbnail =
+            loadContactPhotoThumbnail(mThumbnailUri);
+    /*
+     * Sets the image in the QuickContactBadge
+     * QuickContactBadge inherits from ImageView, so
+     */
+    mBadge.setImageBitmap(mThumbnail);
+</pre>
+<h2 id="ListView">Add a QuickContactBadge to a ListView</h2>
+<p>
+    A {@link android.widget.QuickContactBadge} is a useful addition to a
+    {@link android.widget.ListView} that displays a list of contacts. Use the
+    {@link android.widget.QuickContactBadge} to display a thumbnail photo for each contact; when
+    users click the thumbnail, the {@link android.widget.QuickContactBadge} dialog appears.
+</p>
+<h3>Add the QuickContactBadge element</h3>
+<p>
+    To start, add a {@link android.widget.QuickContactBadge} view element to your item layout
+    For example, if you want to display a {@link android.widget.QuickContactBadge} and a name for
+    each contact you retrieve, put the following XML into a layout file:
+</p>
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"&gt;
+    &lt;QuickContactBadge
+        android:id="&#64;+id/quickcontact"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:scaleType="centerCrop"/&gt;
+    &lt;TextView android:id="&#64;+id/displayname"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:layout_toRightOf="&#64;+id/quickcontact"
+              android:gravity="center_vertical"
+              android:layout_alignParentRight="true"
+              android:layout_alignParentTop="true"/&gt;
+&lt;/RelativeLayout&gt;
+</pre>
+<p>
+    In the following sections, this file is referred to as <code>contact_item_layout.xml</code>.
+</p>
+<h3>Set up a custom CursorAdapter</h3>
+<p>
+    To bind a {@link android.support.v4.widget.CursorAdapter} to a {@link android.widget.ListView}
+    containing a {@link android.widget.QuickContactBadge}, define a custom adapter that
+    extends {@link android.support.v4.widget.CursorAdapter}. This approach allows you to process the
+    data in the {@link android.database.Cursor} before you bind it to the
+    {@link android.widget.QuickContactBadge}. This approach also allows you to bind multiple
+    {@link android.database.Cursor} columns to the {@link android.widget.QuickContactBadge}. Neither
+    of these operations is possible in a regular {@link android.support.v4.widget.CursorAdapter}.
+</p>
+<p>
+    The subclass of {@link android.support.v4.widget.CursorAdapter} that you define must
+    override the following methods:
+</p>
+<dl>
+    <dt>{@link android.support.v4.widget.CursorAdapter#newView CursorAdapter.newView()}</dt>
+    <dd>
+        Inflates a new {@link android.view.View} object to hold the item layout. In the override
+        of this method, store handles to the child {@link android.view.View} objects of the layout,
+        including the child {@link android.widget.QuickContactBadge}. By taking this approach, you
+        avoid having to get handles to the child {@link android.view.View} objects each time you
+        inflate a new layout.
+        <p>
+            You must override this method so you can get handles to the individual child
+            {@link android.view.View} objects. This technique allows you to control their binding in
+            {@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}.
+        </p>
+    </dd>
+    <dt>{@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}</dt>
+    <dd>
+        Moves data from the current {@link android.database.Cursor} row to the child
+        {@link android.view.View} objects of the item layout. You must override this method so
+        you can bind both the contact's URI and thumbnail to the
+        {@link android.widget.QuickContactBadge}. The default implementation only allows a 1-to-1
+        mapping between a column and a {@link android.view.View}
+    </dd>
+</dl>
+<p>
+    The following code snippet contains an example of a custom subclass of
+    {@link android.support.v4.widget.CursorAdapter}:
+</p>
+<h3>Define the custom list adapter</h3>
+<p>
+    Define the subclass of {@link android.support.v4.widget.CursorAdapter} including its
+    constructor, and override
+    {@link android.support.v4.widget.CursorAdapter#newView newView()} and
+    {@link android.support.v4.widget.CursorAdapter#bindView bindView()}:
+</p>
+<pre>
+    /**
+     *
+     *
+     */
+    private class ContactsAdapter extends CursorAdapter {
+        private LayoutInflater mInflater;
+        ...
+        public ContactsAdapter(Context context) {
+            super(context, null, 0);
+
+            /*
+             * Gets an inflater that can instantiate
+             * the ListView layout from the file.
+             */
+            mInflater = LayoutInflater.from(context);
+            ...
+        }
+        ...
+        /**
+         * Defines a class that hold resource IDs of each item layout
+         * row to prevent having to look them up each time data is
+         * bound to a row.
+         */
+        private class ViewHolder {
+            TextView displayname;
+            QuickContactBadge quickcontact;
+        }
+        ..
+        &#64;Override
+        public View newView(
+                Context context,
+                Cursor cursor,
+                ViewGroup viewGroup) {
+            /* Inflates the item layout. Stores resource IDs in a
+             * in a ViewHolder class to prevent having to look
+             * them up each time bindView() is called.
+             */
+            final View itemView =
+                    mInflater.inflate(
+                            R.layout.contact_list_layout,
+                            viewGroup,
+                            false
+                    );
+            final ViewHolder holder = new ViewHolder();
+            holder.displayname =
+                    (TextView) view.findViewById(R.id.displayname);
+            holder.quickcontact =
+                    (QuickContactBadge)
+                            view.findViewById(R.id.quickcontact);
+            view.setTag(holder);
+            return view;
+        }
+        ...
+        &#64;Override
+        public void bindView(
+                View view,
+                Context context,
+                Cursor cursor) {
+            final ViewHolder holder = (ViewHolder) view.getTag();
+            final String photoData =
+                    cursor.getString(mPhotoDataIndex);
+            final String displayName =
+                    cursor.getString(mDisplayNameIndex);
+            ...
+            // Sets the display name in the layout
+            holder.displayname = cursor.getString(mDisplayNameIndex);
+            ...
+            /*
+             * Generates a contact URI for the QuickContactBadge.
+             */
+            final Uri contactUri = Contacts.getLookupUri(
+                    cursor.getLong(mIdIndex),
+                    cursor.getString(mLookupKeyIndex));
+            holder.quickcontact.assignContactUri(contactUri);
+            String photoData = cursor.getString(mPhotoDataIndex);
+            /*
+             * Decodes the thumbnail file to a Bitmap.
+             * The method loadContactPhotoThumbnail() is defined
+             * in the section "Set the Contact URI and Thumbnail"
+             */
+            Bitmap thumbnailBitmap =
+                    loadContactPhotoThumbnail(photoData);
+            /*
+             * Sets the image in the QuickContactBadge
+             * QuickContactBadge inherits from ImageView
+             */
+            holder.quickcontact.setImageBitmap(thumbnailBitmap);
+    }
+</pre>
+
+<h3>Set up variables</h3>
+<p>
+    In your code, set up variables, including a {@link android.database.Cursor} projection that
+    includes the necessary columns.
+</p>
+<p class="note">
+    <strong>Note:</strong> The following code snippets use the method
+    <code>loadContactPhotoThumbnail()</code>, which is defined in the section
+    <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
+</p>
+<p>
+    For example:
+</p>
+<pre>
+public class ContactsFragment extends Fragment implements
+        LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
+...
+    // Defines a ListView
+    private ListView mListView;
+    // Defines a ContactsAdapter
+    private ContactsAdapter mAdapter;
+    ...
+    // Defines a Cursor to contain the retrieved data
+    private Cursor mCursor;
+    /*
+     * Defines a projection based on platform version. This ensures
+     * that you retrieve the correct columns.
+     */
+    private static final String[] PROJECTION =
+            {
+                Contacts._ID,
+                Contacts.LOOKUP_KEY,
+                (Build.VERSION.SDK_INT &gt;=
+                 Build.VERSION_CODES.HONEYCOMB) ?
+                        Contacts.DISPLAY_NAME_PRIMARY :
+                        Contacts.DISPLAY_NAME
+                (Build.VERSION.SDK_INT &gt;=
+                 Build.VERSION_CODES.HONEYCOMB) ?
+                        Contacts.PHOTO_THUMBNAIL_ID :
+                        /*
+                         * Although it's not necessary to include the
+                         * column twice, this keeps the number of
+                         * columns the same regardless of version
+                         */
+                        Contacts_ID
+                ...
+            };
+    /*
+     * As a shortcut, defines constants for the
+     * column indexes in the Cursor. The index is
+     * 0-based and always matches the column order
+     * in the projection.
+     */
+    // Column index of the _ID column
+    private int mIdIndex = 0;
+    // Column index of the LOOKUP_KEY column
+    private int mLookupKeyIndex = 1;
+    // Column index of the display name column
+    private int mDisplayNameIndex = 3;
+    /*
+     * Column index of the photo data column.
+     * It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
+     * and _ID for previous versions.
+     */
+    private int mPhotoDataIndex =
+            Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB ?
+            3 :
+            0;
+    ...
+</pre>
+<h3>Set up the ListView</h3>
+<p>
+    In {@link android.support.v4.app.Fragment#onCreate Fragment.onCreate()}, instantiate the custom
+    cursor adapter and get a handle to the {@link android.widget.ListView}:
+</p>
+<pre>
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        ...
+        /*
+         * Instantiates the subclass of
+         * CursorAdapter
+         */
+        ContactsAdapter mContactsAdapter =
+                new ContactsAdapter(getActivity());
+        /*
+         * Gets a handle to the ListView in the file
+         * contact_list_layout.xml
+         */
+        mListView = (ListView) findViewById(R.layout.contact_list_layout);
+        ...
+    }
+    ...
+</pre>
+<p>
+    In {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, bind the
+    <code>ContactsAdapter</code> to the {@link android.widget.ListView}:
+</p>
+<pre>
+    &#64;Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        ...
+        // Sets up the adapter for the ListView
+        mListView.setAdapter(mAdapter);
+        ...
+    }
+    ...
+</pre>
+<p>
+    When you get back a {@link android.database.Cursor} containing the contacts data, usually in
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()},
+    call {@link android.support.v4.widget.CursorAdapter#swapCursor swapCursor()} to move the
+    {@link android.database.Cursor} data to the {@link android.widget.ListView}. This displays the
+    {@link android.widget.QuickContactBadge} for each entry in the list of contacts:
+</p>
+<pre>
+    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
+        // When the loader has completed, swap the cursor into the adapter.
+        mContactsAdapter.swapCursor(cursor);
+    }
+</pre>
+<p>
+    When you bind a {@link android.database.Cursor} to a
+    {@link android.widget.ListView} with a {@link android.support.v4.widget.CursorAdapter}
+    (or subclass), and you use a {@link android.support.v4.content.CursorLoader} to load the
+    {@link android.database.Cursor}, always clear references to the {@link android.database.Cursor}
+    in your implementation of
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}.
+    For example:
+</p>
+<pre>
+    &#64;Override
+    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
+        // Removes remaining reference to the previous Cursor
+        mContactsAdapter.swapCursor(null);
+    }
+</pre>
diff --git a/docs/html/training/contacts-provider/index.jd b/docs/html/training/contacts-provider/index.jd
new file mode 100644
index 0000000..f380d95
--- /dev/null
+++ b/docs/html/training/contacts-provider/index.jd
@@ -0,0 +1,97 @@
+page.title=Accessing Contacts Data
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+<ul>
+  <li>Android 2.0 (API Level 5) or higher</li>
+  <li>Experience in using {@link android.content.Intent} objects</li>
+  <li>Experience in using content providers</li>
+</ul>
+
+<!-- related docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider</a>
+    </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>
+    The <a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a> is
+    the central repository of the user's contacts information, including data from contacts apps and
+    social networking apps. In your apps, you can access Contacts Provider information directly by
+    calling {@link android.content.ContentResolver} methods or by sending intents to a contacts app.
+</p>
+<p>
+    This class focuses on retrieving lists of contacts, displaying the details for a particular
+    contact, and modifying contacts using intents. The basic techniques described
+    here can be extended to perform more complex tasks. In addition, this class helps you
+    understand the overall structure and operation of the
+    <a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a>.
+</p>
+<h2>Lessons</h2>
+ 
+<dl>
+    <dt>
+        <b><a href="retrieve-names.html">Retrieving a List of Contacts</a></b>
+    </dt>
+    <dd>
+        Learn how to retrieve a list of contacts for which the data matches all or part of a search
+        string, using the following techniques:
+        <ul>
+            <li>Match by contact name</li>
+            <li>Match any type of contact data</li>
+            <li>Match a specific type of contact data, such as a phone number</li>
+        </ul>
+    </dd>
+    <dt>
+        <b><a href="retrieve-details.html">Retrieving Details for a Contact</a></b>
+    </dt>
+    <dd>
+        Learn how to retrieve the details for a single contact. A contact's details are data
+        such as phone numbers and email addresses. You can retrieve all details, or you can
+        retrieve details of a specific type, such as all email addresses.
+    </dd>
+    <dt>
+        <b><a href="modify-data.html">Modifying Contacts Using Intents</a></b>
+    </dt>
+    <dd>
+        Learn how to modify a contact by sending an intent to the People app.
+    </dd>
+    <dt>
+        <b>
+            <a href="display-contact-badge.html">Displaying the Quick Contact Badge</a>
+        </b>
+    </dt>
+    <dd>
+        Learn how to display the {@link android.widget.QuickContactBadge} widget. When the user
+        clicks the contact badge widget, a dialog opens that displays the contact's details and
+        action buttons for apps that can handle the details. For example, if the contact has an
+        email address, the dialog displays an action button for the default email app.
+    </dd>
+</dl>
diff --git a/docs/html/training/contacts-provider/modify-data.jd b/docs/html/training/contacts-provider/modify-data.jd
new file mode 100644
index 0000000..64853ef
--- /dev/null
+++ b/docs/html/training/contacts-provider/modify-data.jd
@@ -0,0 +1,305 @@
+page.title=Modifying Contacts Using Intents
+trainingnavtop=true
+@jd:body
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#InsertContact">Insert a New Contact Using an Intent</a></li>
+  <li><a href="#EditContact">Edit an Existing Contact Using an Intent</a></li>
+  <li><a href="#InsertEdit">Let Users Choose to Insert or Edit Using an Intent</a>
+</ol>
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics
+        </a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider
+        </a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a>
+    </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+    This lesson shows you how to use an {@link android.content.Intent} to insert a new contact or
+    modify a contact's data. Instead of accessing the Contacts Provider directly, an
+    {@link android.content.Intent} starts the contacts app, which runs the appropriate
+    {@link android.app.Activity}. For the modification actions described in this lesson,
+    if you send extended data in the {@link android.content.Intent} it's entered into the UI of the
+    {@link android.app.Activity} that is started.
+</p>
+<p>
+    Using an {@link android.content.Intent} to insert or update a single contact is the preferred
+    way of modifying the Contacts Provider, for the following reasons:
+</p>
+<ul>
+    <li>It saves you the time and and effort of developing your own UI and code.</li>
+    <li>
+        It avoids introducing errors caused by modifications that don't follow the
+        Contacts Provider's rules.
+    </li>
+    <li>
+        It reduces the number of permissions you need to request. Your app doesn't need permission
+        to write to the Contacts Provider, because it delegates modifications to the contacts app,
+        which already has that permission.
+    </li>
+</ul>
+<h2 id="InsertContact">Insert a New Contact Using an Intent</h2>
+<p>
+    You often want to allow the user to insert a new contact when your app receives new data. For
+    example, a restaurant review app can allow users to add the restaurant as a contact as they're
+    reviewing it. To do this using an intent, create the intent using as much data as you have
+    available, and then send the intent to the contacts app.
+</p>
+<p>
+    Inserting a contact using the contacts app inserts a new <em>raw</em> contact into the Contacts
+    Provider's {@link android.provider.ContactsContract.RawContacts} table.  If necessary,
+    the contacts app prompts users for the account type and account to use when creating the raw
+    contact. The contacts app also notifies users if the raw contact already exists. Users then have
+    option of canceling the insertion, in which case no contact is created. To learn
+    more about raw contacts, see the
+    <a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a>
+    API guide.
+</p>
+
+<h3>Create an Intent</h3>
+<p>
+    To start, create a new {@link android.content.Intent} object with the action
+    {@link android.provider.ContactsContract.Intents.Insert#ACTION Intents.Insert.ACTION}.
+    Set the MIME type to {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE
+    RawContacts.CONTENT_TYPE}. For example:
+</p>
+<pre>
+...
+// Creates a new Intent to insert a contact
+Intent intent = new Intent(Intents.Insert.ACTION);
+// Sets the MIME type to match the Contacts Provider
+intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
+</pre>
+<p>
+    If you already have details for the contact, such as a phone number or email address, you can
+    insert them into the intent as extended data. For a key value, use the appropriate constant from
+    {@link android.provider.ContactsContract.Intents.Insert Intents.Insert}. The contacts app
+    displays the data in its insert screen, allowing users to make further edits and additions.
+</p>
+<pre>
+/* Assumes EditText fields in your UI contain an email address
+ * and a phone number.
+ *
+ */
+private EditText mEmailAddress = (EditText) findViewById(R.id.email);
+private EditText mPhoneNumber = (EditText) findViewById(R.id.phone);
+...
+/*
+ * Inserts new data into the Intent. This data is passed to the
+ * contacts app's Insert screen
+ */
+// Inserts an email address
+intent.putExtra(Intents.Insert.EMAIL, mEmailAddress.getText())
+/*
+ * In this example, sets the email type to be a work email.
+ * You can set other email types as necessary.
+ */
+      .putExtra(Intents.Insert.EMAIL_TYPE, CommonDataKinds.Email.TYPE_WORK)
+// Inserts a phone number
+      .putExtra(Intents.Insert.PHONE, mPhoneNumber.getText())
+/*
+ * In this example, sets the phone type to be a work phone.
+ * You can set other phone types as necessary.
+ */
+      .putExtra(Intents.Insert.PHONE_TYPE, Phone.TYPE_WORK);
+
+</pre>
+<p>
+    Once you've created the {@link android.content.Intent}, send it by calling
+    {@link android.support.v4.app.Fragment#startActivity startActivity()}.
+</p>
+<pre>
+    /* Sends the Intent
+     */
+    startActivity(intent);
+</pre>
+<p>
+    This call opens a screen in the contacts app that allows users to enter a new contact. The
+    account type and account name for the contact is listed at the top of the screen. Once users
+    enter the data and click <i>Done</i>, the contacts app's contact list appears. Users return to
+    your app by clicking <i>Back</i>.
+</p>
+<h2 id="EditContact">Edit an Existing Contact Using an Intent</h2>
+<p>
+    Editing an existing contact using an {@link android.content.Intent} is useful if the user
+    has already chosen a contact of interest. For example, an app that finds contacts that have
+    postal addresses but lack a postal code could give users the option of looking up the code and
+    then adding it to the contact.
+</p>
+<p>
+    To edit an existing contact using an intent, use a procedure similar to
+    inserting a contact. Create an intent as described in the section
+    <a href="#InsertContact">Insert a New Contact Using an Intent</a>, but add the contact's
+    {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
+    Contacts.CONTENT_LOOKUP_URI} and the MIME type
+    {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE
+    Contacts.CONTENT_ITEM_TYPE} to the intent. If you want to edit the contact with details you
+    already have, you can put them in the intent's extended data. Notice that some
+    name columns can't be edited using an intent; these columns are listed in the summary
+    section of the API reference for the class {@link android.provider.ContactsContract.Contacts}
+    under the heading "Update".
+</p>
+<p>
+    Finally, send the intent. In response, the contacts app displays an edit screen. When the user
+    finishes editing and saves the edits, the contacts app displays a contact list. When the user
+    clicks <i>Back</i>, your app is displayed.
+</p>
+<div class="sidebox-wrapper">
+<div class="sidebox">
+    <h2>Contacts Lookup Key</h2>
+    <p>
+        A contact's {@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} value is
+        the identifier that you should use to retrieve a contact. It remains constant,
+        even if the provider changes the contact's row ID to handle internal operations.
+    </p>
+</div>
+</div>
+<h3>Create the Intent</h3>
+<p>
+    To edit a contact, call {@link android.content.Intent#Intent Intent(action)} to
+    create an intent with the action {@link android.content.Intent#ACTION_EDIT}. Call
+    {@link android.content.Intent#setDataAndType setDataAndType()} to set the data value for the
+    intent to the contact's {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
+    Contacts.CONTENT_LOOKUP_URI} and the MIME type to
+    {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE
+    Contacts.CONTENT_ITEM_TYPE} MIME type; because a call to
+    {@link android.content.Intent#setType setType()} overwrites the current data value for the
+    {@link android.content.Intent}, you must set the data and the MIME type at the same time.
+</p>
+<p>
+    To get a contact's {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
+    Contacts.CONTENT_LOOKUP_URI}, call
+    {@link android.provider.ContactsContract.Contacts#getLookupUri
+    Contacts.getLookupUri(id, lookupkey)} with the contact's
+    {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
+    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} values as
+    arguments.
+</p>
+<p>
+    The following snippet shows you how to create an intent:
+</p>
+<pre>
+    // The Cursor that contains the Contact row
+    public Cursor mCursor;
+    // The index of the lookup key column in the cursor
+    public int mLookupKeyIndex;
+    // The index of the contact's _ID value
+    public int mIdIndex;
+    // The lookup key from the Cursor
+    public String mCurrentLookupKey;
+    // The _ID value from the Cursor
+    public long mCurrentId;
+    // A content URI pointing to the contact
+    Uri mSelectedContactUri;
+    ...
+    /*
+     * Once the user has selected a contact to edit,
+     * this gets the contact's lookup key and _ID values from the
+     * cursor and creates the necessary URI.
+     */
+    // Gets the lookup key column index
+    mLookupKeyIndex = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
+    // Gets the lookup key value
+    mCurrentLookupKey = mCursor.getString(mLookupKeyIndex);
+    // Gets the _ID column index
+    mIdIndex = mCursor.getColumnIndex(Contacts._ID);
+    mCurrentId = mCursor.getLong(mIdIndex);
+    mSelectedContactUri =
+            Contacts.getLookupUri(mCurrentId, mCurrentLookupKey);
+    ...
+    // Creates a new Intent to edit a contact
+    Intent editIntent = new Intent(Intent.ACTION_EDIT);
+    /*
+     * Sets the contact URI to edit, and the data type that the
+     * Intent must match
+     */
+    editIntent.setDataAndType(mSelectedContactUri,Contacts.CONTENT_ITEM_TYPE);
+</pre>
+<h3>Add the navigation flag</h3>
+<p>
+    In Android 4.0 (API version 14) and later, a problem in the contacts app causes incorrect
+    navigation. When your app sends an edit intent to the contacts app, and users edit and save a
+    contact, when they click <i>Back</i> they see the contacts list screen. To navigate back to
+    your app, they have to click <i>Recents</i> and choose your app.
+</p>
+<p>
+    To work around this problem in Android 4.0.3 (API version 15) and later, add the extended
+    data key {@code finishActivityOnSaveCompleted} to the intent, with a value of {@code true}.
+    Android versions prior to Android 4.0 accept this key, but it has no effect. To set the
+    extended data, do the following:
+</p>
+<pre>
+    // Sets the special extended data for navigation
+    editIntent.putExtra("finishActivityOnSaveCompleted", true);
+</pre>
+<h3>Add other extended data</h3>
+<p>
+    To add additional extended data to the {@link android.content.Intent}, call
+    {@link android.content.Intent#putExtra putExtra()} as desired.
+    You can add extended data for common contact fields by using the key values specified in
+    {@link android.provider.ContactsContract.Intents.Insert Intents.Insert}. Remember that some
+    columns in the {@link android.provider.ContactsContract.Contacts} table can't be modified.
+    These columns are listed in the summary section of the API reference for the class
+    {@link android.provider.ContactsContract.Contacts} under the heading "Update".
+</p>
+
+<h3>Send the Intent</h3>
+<p>
+    Finally, send the intent you've constructed. For example:
+</p>
+<pre>
+    // Sends the Intent
+    startActivity(editIntent);
+</pre>
+<h2 id="InsertEdit">Let Users Choose to Insert or Edit Using an Intent</h2>
+<p>
+    You can allow users to choose whether to insert a contact or edit an existing one by sending
+    an {@link android.content.Intent} with the action
+    {@link android.content.Intent#ACTION_INSERT_OR_EDIT}. For example, an email client app could
+    allow users to add an incoming email address to a new contact, or add it as an additional
+    address for an existing contact. Set the MIME type for this intent to
+    {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE Contacts.CONTENT_ITEM_TYPE},
+    but don't set the data URI.
+</p>
+<p>
+    When you send this intent, the contacts app displays a list of contacts.
+    Users can either insert a new contact or pick an existing contact and edit it.
+    Any extended data fields you add to the intent populates the screen that appears. You can use
+    any of the key values specified in {@link android.provider.ContactsContract.Intents.Insert
+    Intents.Insert}. The following code snippet shows how to construct and send the intent:
+</p>
+<pre>
+    // Creates a new Intent to insert or edit a contact
+    Intent intentInsertEdit = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+    // Sets the MIME type
+    intentInsertEdit.setType(Contacts.CONTENT_ITEM_TYPE);
+    // Add code here to insert extended data, if desired
+    ...
+    // Sends the Intent with an request ID
+    startActivity(intentInsertEdit);
+</pre>
diff --git a/docs/html/training/contacts-provider/retrieve-details.jd b/docs/html/training/contacts-provider/retrieve-details.jd
new file mode 100644
index 0000000..0de3b67
--- /dev/null
+++ b/docs/html/training/contacts-provider/retrieve-details.jd
@@ -0,0 +1,378 @@
+page.title=Retrieving Details for a Contact
+
+trainingnavtop=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#RetrieveAll">Retrieve All Details for a Contact</a></li>
+  <li><a href="#RetrieveSpecific">Retrieve Specific Details for a Contact</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/components/loaders.html">Loaders</a>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+    This lesson shows how to retrieve detail data for a contact, such as email addresses, phone
+    numbers, and so forth. It's the details that users are looking for when they retrieve a contact.
+    You can give them all the details for a contact, or only display details of a particular type,
+    such as email addresses.
+</p>
+<p>
+    The steps in this lesson assume that you already have a
+    {@link android.provider.ContactsContract.Contacts} row for a contact the user has picked.
+    The <a href="retrieve-names.html">Retrieving Contact Names</a> lesson shows how to
+    retrieve a list of contacts.
+</p>
+<h2 id="RetrieveAll">Retrieve All Details for a Contact</h2>
+<p>
+    To retrieve all the details for a contact, search the
+    {@link android.provider.ContactsContract.Data} table for any rows that contain the contact's
+    {@link android.provider.ContactsContract.Data#LOOKUP_KEY}. This column is available in
+    the {@link android.provider.ContactsContract.Data} table, because the Contacts
+    Provider makes an implicit join between the {@link android.provider.ContactsContract.Contacts}
+    table and the {@link android.provider.ContactsContract.Data} table. The
+    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} column is described
+    in more detail in the <a href="retrieve-names.html">Retrieving Contact Names</a> lesson.
+</p>
+<p class="note">
+    <strong>Note:</strong> Retrieving all the details for a contact reduces the performance of a
+    device, because it needs to retrieve all of the columns in the
+    {@link android.provider.ContactsContract.Data} table. Consider the performance impact before
+    you use this technique.
+</p>
+<h3>Request permissions</h3>
+<p>
+    To read from the Contacts Provider, your app must have
+    {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission.
+    To request this permission, add the following child element of
+    <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">
+    &lt;manifest&gt;</a></code> to your manifest file:
+</p>
+<pre>
+    &lt;uses-permission android:name="android.permission.READ_CONTACTS" /&gt;
+</pre>
+<h3>Set up a projection</h3>
+<p>
+    Depending on the data type a row contains, it may use only a few columns or many. In addition,
+    the data is in different columns depending on the data type.
+    To ensure you get all the possible columns for all possible data types, you need to add all the
+    column names to your projection. Always retrieve
+    {@link android.provider.ContactsContract.Data#_ID Data._ID} if you're binding the result
+    {@link android.database.Cursor} to a {@link android.widget.ListView}; otherwise, the binding
+    won't work. Also retrieve {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}
+    so you can identify the data type of each row you retrieve. For example:
+</p>
+<pre>
+    private static final String PROJECTION =
+            {
+                Data._ID,
+                Data.MIMETYPE,
+                Data.DATA1,
+                Data.DATA2,
+                Data.DATA3,
+                Data.DATA4,
+                Data.DATA5,
+                Data.DATA6,
+                Data.DATA7,
+                Data.DATA8,
+                Data.DATA9,
+                Data.DATA10,
+                Data.DATA11,
+                Data.DATA12,
+                Data.DATA13,
+                Data.DATA14,
+                Data.DATA15
+            };
+</pre>
+<p>
+    This projection retrieves all the columns for a row in the
+    {@link android.provider.ContactsContract.Data} table, using the column names defined in
+    the {@link android.provider.ContactsContract.Data} class.
+</p>
+<p>
+    Optionally, you can also use any other column constants defined in or inherited by the
+    {@link android.provider.ContactsContract.Data} class. Notice, however, that the columns
+    {@link android.provider.ContactsContract.DataColumns#SYNC1} through
+    {@link android.provider.ContactsContract.DataColumns#SYNC4} are meant to be used by sync
+    adapters, so their data is not useful.
+</p>
+<h3>Define the selection criteria</h3>
+<p>
+    Define a constant for your selection clause, an array to hold selection arguments, and a
+    variable to hold the selection value. Use
+    the {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} column to
+    find the contact. For example:
+</p>
+<pre>
+    // Defines the selection clause
+    private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
+    // Defines the array to hold the search criteria
+    private String[] mSelectionArgs = { "" };
+    /*
+     * Defines a variable to contain the selection value. Once you
+     * have the Cursor from the Contacts table, and you've selected
+     * the desired row, move the row's LOOKUP_KEY value into this
+     * variable.
+     */
+    private String mLookupKey;
+</pre>
+<p>
+    Using "?" as a placeholder in your selection text expression ensures that the resulting search
+    is generated by binding rather than SQL compilation. This approach eliminates the
+    possibility of malicious SQL injection.
+</p>
+<h3>Define the sort order</h3>
+<p>
+    Define the sort order you want in the resulting {@link android.database.Cursor}. To
+    keep all rows for a particular data type together, sort by
+    {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}. This query argument
+    groups all email rows together, all phone rows together, and so forth. For example:
+</p>
+<pre>
+    /*
+     * Defines a string that specifies a sort order of MIME type
+     */
+    private static final String SORT_ORDER = Data.MIMETYPE;
+</pre>
+<p class="note">
+    <strong>Note:</strong> Some data types don't use a subtype, so you can't sort on subtype.
+    Instead, you have to iterate through the returned {@link android.database.Cursor},
+    determine the data type of the current row, and store data for rows that use a subtype. When
+    you finish reading the cursor, you can then sort each data type by subtype and display the
+    results.
+</p>
+<h3>Initialize the Loader</h3>
+<p>
+   Always do retrievals from the Contacts Provider (and all other content providers) in a
+   background thread. Use the Loader framework defined by the
+   {@link android.support.v4.app.LoaderManager} class and the
+   {@link android.support.v4.app.LoaderManager.LoaderCallbacks} interface to do background
+   retrievals.
+</p>
+<p>
+    When you're ready to retrieve the rows, initialize the loader framework by
+    calling {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Pass an
+    integer identifier to the method; this identifier is passed to
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks} methods. The identifier helps you
+    use multiple loaders in an app by allowing you to differentiate between them.
+</p>
+<p>
+    The following snippet shows how to initialize the loader framework:
+</p>
+<pre>
+public class DetailsFragment extends Fragment implements
+        LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
+    ...
+    // Defines a constant that identifies the loader
+    DETAILS_QUERY_ID = 0;
+    ...
+    /*
+     * Invoked when the parent Activity is instantiated
+     * and the Fragment's UI is ready. Put final initialization
+     * steps here.
+     */
+    &#64;Override
+    onActivityCreated(Bundle savedInstanceState) {
+        ...
+        // Initializes the loader framework
+        getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
+</pre>
+<h3>Implement onCreateLoader()</h3>
+<p>
+    Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
+    onCreateLoader()} method, which is called by the loader framework immediately after you call
+    {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Return a
+    {@link android.support.v4.content.CursorLoader} from this method. Since you're searching
+    the {@link android.provider.ContactsContract.Data} table, use the constant
+    {@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI} as the content URI.
+    For example:
+</p>
+<pre>
+    &#64;Override
+    public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
+        // Choose the proper action
+        switch (loaderId) {
+            case DETAILS_QUERY_ID:
+            // Assigns the selection parameter
+            mSelectionArgs[0] = mLookupKey;
+            // Starts the query
+            CursorLoader mLoader =
+                    new CursorLoader(
+                            getActivity(),
+                            Data.CONTENT_URI,
+                            PROJECTION,
+                            SELECTION,
+                            mSelectionArgs,
+                            SORT_ORDER
+                    );
+            ...
+    }
+</pre>
+<h3>Implement onLoadFinished() and onLoaderReset()</h3>
+<p>
+    Implement the
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+    method. The loader framework calls
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+    when the Contacts Provider returns the results of the query. For example:
+</p>
+<pre>
+    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
+        switch (loader.getId()) {
+            case DETAILS_QUERY_ID:
+                    /*
+                     * Process the resulting Cursor here.
+                     */
+                }
+                break;
+            ...
+        }
+    }
+</pre>
+<p>
+<p>
+    The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset
+    onLoaderReset()} is invoked when the loader framework detects that the data backing the result
+    {@link android.database.Cursor} has changed. At this point, remove any existing references
+    to the {@link android.database.Cursor} by setting them to null. If you don't, the loader
+    framework won't destroy the old {@link android.database.Cursor}, and you'll get a memory
+    leak. For example:
+<pre>
+    &#64;Override
+    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
+        switch (loader.getId()) {
+            case DETAILS_QUERY_ID:
+                /*
+                 * If you have current references to the Cursor,
+                 * remove them here.
+                 */
+                }
+                break;
+    }
+</pre>
+<h2 id="RetrieveSpecific">Retrieve Specific Details for a Contact</h2>
+<p>
+    Retrieving a specific data type for a contact, such as all the emails, follows the same pattern
+    as retrieving all details. These are the only changes you need to make to the code
+    listed in <a href="#RetrieveAll">Retrieve All Details for a Contact</a>:
+</p>
+<dl>
+    <dt>
+        Projection
+    </dt>
+    <dd>
+        Modify your projection to retrieve the columns that are specific to the
+        data type. Also modify the projection to use the column name constants defined in the
+        {@link android.provider.ContactsContract.CommonDataKinds} subclass corresponding to the
+        data type.
+    </dd>
+    <dt>
+        Selection
+    </dt>
+    <dd>
+        Modify the selection text to search for the
+        {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value that's specific to
+        your data type.
+    </dd>
+    <dt>
+        Sort order
+    </dt>
+    <dd>
+        Since you're only selecting a single detail type, don't group the returned
+        {@link android.database.Cursor} by {@link android.provider.ContactsContract.Data#MIMETYPE
+        Data.MIMETYPE}.
+    </dd>
+</dl>
+<p>
+    These modifications are described in the following sections.
+</p>
+<h3>Define a projection</h3>
+<p>
+    Define the columns you want to retrieve, using the column name constants in the subclass
+    of {@link android.provider.ContactsContract.CommonDataKinds} for the data type.
+    If you plan to bind your {@link android.database.Cursor} to a {@link android.widget.ListView},
+    be sure to retrieve the <code>_ID</code> column. For example, to retrieve email data, define the
+    following projection:
+</p>
+<pre>
+    private static final String[] PROJECTION =
+            {
+                Email._ID,
+                Email.ADDRESS,
+                Email.TYPE,
+                Email.LABEL
+            };
+</pre>
+<p>
+    Notice that this projection uses the column names defined in the class
+    {@link android.provider.ContactsContract.CommonDataKinds.Email}, instead of the column names
+    defined in the class {@link android.provider.ContactsContract.Data}. Using the email-specific
+    column names makes the code more readable.
+</p>
+<p>
+    In the projection, you can also use any of the other columns defined in the
+    {@link android.provider.ContactsContract.CommonDataKinds} subclass.
+</p>
+<h3>Define selection criteria</h3>
+<p>
+    Define a search text expression that retrieves rows for a specific contact's
+    {@link android.provider.ContactsContract.Data#LOOKUP_KEY} and the
+    {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE} of the details you
+    want. Enclose the {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value in
+    single quotes by concatenating a "<code>'</code>" (single-quote) character to the start and end
+    of the constant; otherwise, the provider interprets the constant as a variable name rather
+    than as a string value. You don't need to use a placeholder for this value, because you're
+    using a constant rather than a user-supplied value. For example:
+</p>
+<pre>
+    /*
+     * Defines the selection clause. Search for a lookup key
+     * and the Email MIME type
+     */
+    private static final String SELECTION =
+            Data.LOOKUP_KEY + " = ?" +
+            " AND " +
+            Data.MIMETYPE + " = " +
+            "'" + Email.CONTENT_ITEM_TYPE + "'";
+    // Defines the array to hold the search criteria
+    private String[] mSelectionArgs = { "" };
+</pre>
+<h3>Define a sort order</h3>
+<p>
+    Define a sort order for the returned {@link android.database.Cursor}. Since you're retrieving a
+    specific data type, omit the sort on {@link android.provider.ContactsContract.Data#MIMETYPE}.
+    Instead, if the type of detail data you're searching includes a subtype, sort on it.
+    For example, for email data you can sort on
+    {@link android.provider.ContactsContract.CommonDataKinds.Email#TYPE Email.TYPE}:
+</p>
+<pre>
+    private static final String SORT_ORDER = Email.TYPE + " ASC ";
+</pre>
diff --git a/docs/html/training/contacts-provider/retrieve-names.jd b/docs/html/training/contacts-provider/retrieve-names.jd
new file mode 100644
index 0000000..b034a6a
--- /dev/null
+++ b/docs/html/training/contacts-provider/retrieve-names.jd
@@ -0,0 +1,815 @@
+page.title=Retrieving a List of Contacts
+
+trainingnavtop=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#Permissions">Request Permission to Read the Provider</a>
+  <li><a href="#NameMatch">Match a Contact by Name and List the Results</a></li>
+  <li><a href="#TypeMatch">Match a Contact By a Specific Type of Data</a></li>
+  <li><a href="#GeneralMatch">Match a Contact By Any Type of Data</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/components/loaders.html">Loaders</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>
+    </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+    This lesson shows you how to retrieve a list of contacts whose data matches all or part of a
+    search string, using the following techniques:
+</p>
+<dl>
+    <dt>Match contact names</dt>
+    <dd>
+        Retrieve a list of contacts by matching the search string to all or part of the contact
+        name data. The Contacts Provider allows multiple instances of the same name, so this
+        technique can return a list of matches.
+    </dd>
+    <dt>Match a specific type of data, such as a phone number</dt>
+    <dd>
+        Retrieve a list of contacts by matching the search string to a particular type of detail
+        data such as an email address. For example, this technique allows you to list all of the
+        contacts whose email address matches the search string.
+    </dd>
+    <dt>Match any type of data</dt>
+    <dd>
+        Retrieve a list of contacts by matching the search string to any type of detail data,
+        including name, phone number, street address, email address, and so forth. For example,
+        this technique allows you to accept any type of data for a search string and then list the
+        contacts for which the data matches the string.
+    </dd>
+</dl>
+<p class="note">
+    <strong>Note:</strong> All the examples in this lesson use a
+    {@link android.support.v4.content.CursorLoader} to retrieve data from the Contacts
+    Provider. A {@link android.support.v4.content.CursorLoader} runs its query on a
+    thread that's separate from the UI thread. This ensures that the query doesn't slow down UI
+    response times and cause a poor user experience. For more information, see the Android
+    training class <a href="{@docRoot}training/load-data-background/index.html">
+    Loading Data in the Background</a>.
+</p>
+<h2 id="Permissions">Request Permission to Read the Provider</h2>
+<p>
+    To do any type of search of the Contacts Provider, your app must have
+    {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission.
+    To request this, add this
+<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
+    element to your manifest file as a child element of
+<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code>:
+</p>
+<pre>
+    &lt;uses-permission android:name="android.permission.READ_CONTACTS" /&gt;
+</pre>
+<h2 id="NameMatch">Match a Contact by Name and List the Results</h2>
+<p>
+    This technique tries to match a search string to the name of a contact or contacts in the
+    Contact Provider's {@link android.provider.ContactsContract.Contacts} table. You usually want
+    to display the results in a {@link android.widget.ListView}, to allow the user to choose among
+    the matched contacts.
+</p>
+<h3 id="DefineListView">Define ListView and item layouts</h3>
+<p>
+    To display the search results in a {@link android.widget.ListView}, you need a main layout file
+    that defines the entire UI including the {@link android.widget.ListView}, and an item layout
+    file that defines one line of the {@link android.widget.ListView}. For example, you can define
+    the main layout file <code>res/layout/contacts_list_view.xml</code> that contains the
+    following XML:
+</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;ListView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="&#64;android:id/list"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"/&gt;
+</pre>
+<p>
+    This XML uses the built-in Android {@link android.widget.ListView} widget
+    {@link android.R.id#list android:id/list}.
+</p>
+<p>
+    Define the item layout file <code>contacts_list_item.xml</code> with the following XML:
+</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="&#64;android:id/text1"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:clickable="true"/&gt;
+</pre>
+<p>
+    This XML uses the built-in Android {@link android.widget.TextView} widget
+    {@link android.R.id#text1 android:text1}.
+</p>
+<p class="note">
+    <strong>Note:</strong> This lesson doesn't describe the UI for getting a search string from the
+    user, because you may want to get the string indirectly. For example, you can give the user
+    an option to search for contacts whose name matches a string in an incoming text message.
+</p>
+<p>
+    The two layout files you've written define a user interface that shows a
+    {@link android.widget.ListView}. The next step is to write code that uses this UI to display a
+    list of contacts.
+</p>
+<h3 id="Fragment">Define a Fragment that displays the list of contacts</h3>
+<p>
+    To display the list of contacts, start by defining a {@link android.support.v4.app.Fragment}
+    that's loaded by an {@link android.app.Activity}. Using a
+    {@link android.support.v4.app.Fragment} is a more flexible technique, because you can use
+    one {@link android.support.v4.app.Fragment} to display the list and a second
+    {@link android.support.v4.app.Fragment} to display the details for a contact that the user
+    chooses from the list. Using this approach, you can combine one of the techniques presented in
+    this lesson with one from the lesson <a href="retrieve-details.html">
+    Retrieving Details for a Contact</a>.
+</p>
+<p>
+    To learn how to use one or more {@link android.support.v4.app.Fragment} objects from an
+    an {@link android.app.Activity}, read the training class
+    <a href="{@docRoot}training/basics/fragments/index.html">
+    Building a Dynamic UI with Fragments</a>.
+</p>
+<p>
+    To help you write queries against the Contacts Provider, the Android framework provides a
+    contracts class called {@link android.provider.ContactsContract}, which defines useful
+    constants and methods for accessing the provider. When you use this class, you don't have to
+    define your own constants for content URIs, table names, or columns. To use this class,
+    include the following statement:
+</p>
+<pre>
+import android.provider.ContactsContract;
+</pre>
+<p>
+    Since the code uses a {@link android.support.v4.content.CursorLoader} to retrieve data
+    from the provider, you must specify that it implements the loader interface
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks}. Also, to help detect which contact
+    the user selects from the list of search results, implement the adapter interface
+    {@link android.widget.AdapterView.OnItemClickListener}. For example:
+</p>
+<pre>
+...
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager.LoaderCallbacks;
+import android.widget.AdapterView;
+...
+public class ContactsFragment extends Fragment implements
+        LoaderManager.LoaderCallbacks&lt;Cursor&gt;,
+        AdapterView.OnItemClickListener {
+</pre>
+<h3 id="DefineVariables">Define global variables</h3>
+<p>
+    Define global variables that are used in other parts of the code:
+</p>
+<pre>
+    ...
+    /*
+     * Defines an array that contains column names to move from
+     * the Cursor to the ListView.
+     */
+    &#64;SuppressLint("InlinedApi")
+    private final static String[] FROM_COLUMNS = {
+            Build.VERSION.SDK_INT
+                    &gt;= Build.VERSION_CODES.HONEYCOMB ?
+                    Contacts.DISPLAY_NAME_PRIMARY :
+                    Contacts.DISPLAY_NAME
+    };
+    /*
+     * Defines an array that contains resource ids for the layout views
+     * that get the Cursor column contents. The id is pre-defined in
+     * the Android framework, so it is prefaced with "android.R.id"
+     */
+    private final static int[] TO_IDS = {
+           android.R.id.text1
+    };
+    // Define global mutable variables
+    // Define a ListView object
+    ListView mContactsList;
+    // Define variables for the contact the user selects
+    // The contact's _ID value
+    long mContactId;
+    // The contact's LOOKUP_KEY
+    String mContactKey;
+    // A content URI for the selected contact
+    Uri mContactUri;
+    // An adapter that binds the result Cursor to the ListView
+    private SimpleCursorAdapter mCursorAdapter;
+    ...
+</pre>
+<p class="note">
+    <strong>Note:</strong> Since
+    {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY
+    Contacts.DISPLAY_NAME_PRIMARY} requires Android 3.0 (API version 11) or later, setting your
+    app's <code>minSdkVersion</code> to 10 or below generates an Android Lint warning in
+    Eclipse with ADK. To turn off this warning, add the annotation
+    <code>@SuppressLint("InlinedApi")</code> before the definition of <code>FROM_COLUMNS</code>.
+</p>
+<h3 id="InitializeFragment">Initialize the Fragment</h3>
+<p>
+
+    Initialize the {@link android.support.v4.app.Fragment}. Add the empty, public constructor
+    required by the Android system, and inflate the {@link android.support.v4.app.Fragment} object's
+    UI in the callback method {@link android.support.v4.app.Fragment#onCreateView onCreateView()}.
+    For example:
+</p>
+<pre>
+    // Empty public constructor, required by the system
+    public ContactsFragment() {}
+
+    // A UI Fragment must inflate its View
+    &#64;Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        // Inflate the fragment layout
+        return inflater.inflate(R.layout.contacts_list_layout, container, false);
+    }
+</pre>
+<h3 id="DefineAdapter">Set up the CursorAdapter for the ListView</h3>
+<p>
+    Set up the {@link android.support.v4.widget.SimpleCursorAdapter} that binds the results of the
+    search to the {@link android.widget.ListView}. To get the {@link android.widget.ListView} object
+    that displays the contacts, you need to call {@link android.app.Activity#findViewById
+    Activity.findViewById()} using the parent activity of the
+    {@link android.support.v4.app.Fragment}. Use the {@link android.content.Context} of the
+    parent activity when you call {@link android.widget.ListView#setAdapter setAdapter()}.
+    For example:
+</p>
+<pre>
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        ...
+        // Gets the ListView from the View list of the parent activity
+        mContactsList = (ListView) getActivity().findViewById(R.layout.contact_list_view);
+        // Gets a CursorAdapter
+        mCursorAdapter = new SimpleCursorAdapter(
+                getActivity(),
+                R.layout.contact_list_item,
+                null,
+                FROM_COLUMNS, TO_IDS,
+                0);
+        // Sets the adapter for the ListView
+        mContactsList.setAdapter(mCursorAdapter);
+    }
+</pre>
+<h3 id="SetListener">Set the selected contact listener</h3>
+<p>
+    When you display the results of a search, you usually want to allow the user to select a
+    single contact for further processing. For example, when the user clicks a contact you can
+    display the contact's address on a map. To provide this feature, you first defined the current
+    {@link android.support.v4.app.Fragment} as the click listener by specifying that the class
+    implements {@link android.widget.AdapterView.OnItemClickListener}, as shown in the section
+    <a href="#Fragment">Define a Fragment that displays the list of contacts</a>.
+</p>
+<p>
+    To continue setting up the listener, bind it to the {@link android.widget.ListView} by
+    calling the method {@link android.widget.ListView#setOnItemClickListener
+    setOnItemClickListener()} in {@link android.support.v4.app.Fragment#onActivityCreated
+    onActivityCreated()}. For example:
+</p>
+<pre>
+    public void onActivityCreated(Bundle savedInstanceState) {
+        ...
+        // Set the item click listener to be the current fragment.
+        mContactsList.setOnItemClickListener(this);
+        ...
+    }
+</pre>
+<p>
+    Since you specified that the current {@link android.support.v4.app.Fragment} is the
+    {@link android.widget.AdapterView.OnItemClickListener OnItemClickListener} for the
+    {@link android.widget.ListView}, you now need to implement its required method
+    {@link android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()}, which
+    handles the click event. This is described in a succeeding section.
+</p>
+<h3 id="DefineProjection">Define a projection</h3>
+<p>
+    Define a constant that contains the columns you want to return from your query. Each item in
+    the {@link android.widget.ListView} displays the contact's display name,
+    which contains the main form of the contact's name. In Android 3.0 (API version 11) and later,
+    the name of this column is
+    {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY
+    Contacts.DISPLAY_NAME_PRIMARY}; in versions previous to that, its name is
+    {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME Contacts.DISPLAY_NAME}.
+</p>
+<p>
+    The column {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} is used by the
+    {@link android.support.v4.widget.SimpleCursorAdapter} binding process.
+    {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
+    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} are used together to
+    construct a content URI for the contact the user selects.
+</p>
+<pre>
+...
+&#64;SuppressLint("InlinedApi")
+private static final String[] PROJECTION =
+        {
+            Contacts._ID,
+            Contacts.LOOKUP_KEY,
+            Build.VERSION.SDK_INT
+                    &gt;= Build.VERSION_CODES.HONEYCOMB ?
+                    Contacts.DISPLAY_NAME_PRIMARY :
+                    Contacts.DISPLAY_NAME
+
+        };
+</pre>
+<h3 id="DefineConstants">Define constants for the Cursor column indexes</h3>
+<p>
+    To get data from an individual column in a {@link android.database.Cursor}, you need
+    the column's index within the {@link android.database.Cursor}. You can define constants
+    for the indexes of the {@link android.database.Cursor} columns, because the indexes are
+    the same as the order of the column names in your projection. For example:
+</p>
+<pre>
+// The column index for the _ID column
+private static final int CONTACT_ID_INDEX = 0;
+// The column index for the LOOKUP_KEY column
+private static final int LOOKUP_KEY_INDEX = 1;
+</pre>
+<h3 id="SelectionCriteria">Specify the selection criteria</h3>
+<p>
+    To specify the data you want, create a combination of text expressions and variables
+    that tell the provider the data columns to search and the values to find.
+</p>
+<p>
+    For the text expression, define a constant that lists the search columns. Although this
+    expression can contain values as well, the preferred practice is to represent the values with
+    a "?" placeholder. During retrieval, the placeholder is replaced with values from an
+    array. Using "?" as a placeholder ensures that the search specification is generated by binding
+    rather than by SQL compilation. This practice eliminates the possibility of malicious SQL
+    injection. For example:
+</p>
+<pre>
+    // Defines the text expression
+    &#64;SuppressLint("InlinedApi")
+    private static final String SELECTION =
+            Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB ?
+            Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
+            Contacts.DISPLAY_NAME + " LIKE ?";
+    // Defines a variable for the search string
+    private String mSearchString;
+    // Defines the array to hold values that replace the ?
+    private String[] mSelectionArgs = { mSearchString };
+</pre>
+<h3 id="OnItemClick">Define the onItemClick() method</h3>
+<p>
+    In a previous section, you set the item click listener for the {@link android.widget.ListView}.
+    Now implement the action for the listener by defining the method
+    {@link android.widget.AdapterView.OnItemClickListener#onItemClick
+    AdapterView.OnItemClickListener.onItemClick()}:
+</p>
+<pre>
+    &#64;Override
+    public void onItemClick(
+        AdapterView&lt;?&gt; parent, View item, int position, long rowID) {
+        // Get the Cursor
+        Cursor cursor = parent.getAdapter().getCursor();
+        // Move to the selected contact
+        cursor.moveToPosition(position);
+        // Get the _ID value
+        mContactId = getLong(CONTACT_ID_INDEX);
+        // Get the selected LOOKUP KEY
+        mContactKey = getString(CONTACT_KEY_INDEX);
+        // Create the contact's content Uri
+        mContactUri = Contacts.getLookupUri(mContactId, mContactKey);
+        /*
+         * You can use mContactUri as the content URI for retrieving
+         * the details for a contact.
+         */
+    }
+</pre>
+<h3 id="InitializeLoader">Initialize the loader</h3>
+<p>
+    Since you're using a {@link android.support.v4.content.CursorLoader} to retrieve data,
+    you must initialize the background thread and other variables that control asynchronous
+    retrieval. Do the initialization in
+    {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, which
+    is invoked immediately before the {@link android.support.v4.app.Fragment} UI appears, as
+    shown in the following example:
+</p>
+<pre>
+public class ContactsFragment extends Fragment implements
+        LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
+    ...
+    // Called just before the Fragment displays its UI
+    &#64;Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        // Always call the super method first
+        super.onActivityCreated(savedInstanceState);
+        ...
+        // Initializes the loader
+        getLoaderManager().initLoader(0, null, this);
+</pre>
+<h3 id="OnCreateLoader">Implement onCreateLoader()</h3>
+<p>
+    Implement the method
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()},
+    which is called by the loader framework immediately after you call
+    {@link android.support.v4.app.LoaderManager#initLoader initLoader()}.
+<p>
+    In {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()},
+    set up the search string pattern. To make a string into a pattern, insert "%"
+    (percent) characters to represent a sequence of zero or more characters, or "_" (underscore)
+    characters to represent a single character, or both. For example, the pattern "%Jefferson%"
+    would match both "Thomas Jefferson" and "Jefferson Davis".
+</p>
+<p>
+    Return a new {@link android.support.v4.content.CursorLoader} from the method. For the content
+    URI, use {@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}.
+    This URI refers to the entire table, as shown in the following example:
+</p>
+<pre>
+    ...
+    &#64;Override
+    public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
+        /*
+         * Makes search string into pattern and
+         * stores it in the selection array
+         */
+        mSelectionArgs[0] = "%" + mSearchString + "%";
+        // Starts the query
+        return new CursorLoader(
+                getActivity(),
+                Contacts.CONTENT_URI,
+                PROJECTION,
+                SELECTION,
+                mSelectionArgs,
+                null
+        );
+    }
+</pre>
+<h3 id="FinishedReset">Implement onLoadFinished() and onLoaderReset()</h3>
+<p>
+    Implement the
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+    method. The loader framework calls
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+    when the Contacts Provider returns the results of the query. In this method, put the
+    result {@link android.database.Cursor} in the
+    {@link android.support.v4.widget.SimpleCursorAdapter}. This automatically updates the
+    {@link android.widget.ListView} with the search results:
+</p>
+<pre>
+    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
+        // Put the result Cursor in the adapter for the ListView
+        mCursorAdapter.swapCursor(cursor);
+    }
+</pre>
+<p>
+    The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset
+    onLoaderReset()} is invoked when the loader framework detects that the
+    result {@link android.database.Cursor} contains stale data. Delete the
+    {@link android.support.v4.widget.SimpleCursorAdapter} reference to the existing
+    {@link android.database.Cursor}. If you don't, the loader framework will not
+    recycle the {@link android.database.Cursor}, which causes a memory leak. For example:
+</p>
+<pre>
+    &#64;Override
+    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
+        // Delete the reference to the existing Cursor
+        mCursorAdapter.swapCursor(null);
+
+    }
+</pre>
+
+<p>
+    You now have the key pieces of an app that matches a search string to contact names and returns
+    the result in a {@link android.widget.ListView}. The user can click a contact name to select it.
+    This triggers a listener, in which you can work further with the contact's data. For example,
+    you can retrieve the contact's details. To learn how to do this, continue with the next
+    lesson, <a href="#retrieve-details.html">Retrieving Details for a Contact</a>.
+</p>
+<p>
+    To learn more about search user interfaces, read the API guide
+    <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>.
+</p>
+<p>
+    The remaining sections in this lesson demonstrate other ways of finding contacts in the
+    Contacts Provider.
+</p>
+<h2 id="TypeMatch">Match a Contact By a Specific Type of Data</h2>
+<p>
+    This technique allows you to specify the type of data you want to match. Retrieving
+    by name is a specific example of this type of query, but you can also do it for any of the types
+    of detail data associated with a contact. For example, you can retrieve contacts that have a
+    specific postal code; in this case, the search string has to match data stored in a postal code
+    row.
+</p>
+<p>
+    To implement this type of retrieval, first implement the following code, as listed in
+    previous sections:
+</p>
+<ul>
+    <li>
+        Request Permission to Read the Provider.
+    </li>
+    <li>
+        Define ListView and item layouts.
+    </li>
+    <li>
+        Define a Fragment that displays the list of contacts.
+    </li>
+    <li>
+        Define global variables.
+    </li>
+    <li>
+        Initialize the Fragment.
+    </li>
+    <li>
+        Set up the CursorAdapter for the ListView.
+    </li>
+    <li>
+        Set the selected contact listener.
+    </li>
+    <li>
+        Define constants for the Cursor column indexes.
+        <p>
+            Although you're retrieving data from a different table, the order of the columns in
+            the projection is the same, so you can use the same indexes for the Cursor.
+        </p>
+    </li>
+    <li>
+        Define the onItemClick() method.
+    </li>
+    <li>
+        Initialize the loader.
+    </li>
+    <li>
+
+        Implement onLoadFinished() and onLoaderReset().
+    </li>
+</ul>
+<p>
+    The following steps show you the additional code you need to match a search string to
+    a particular type of detail data and display the results.
+</p>
+<h3>Choose the data type and table</h3>
+<p>
+    To search for a particular type of detail data, you have to know the custom MIME type value
+    for the data type. Each data type has a unique MIME type
+    value defined by a constant <code>CONTENT_ITEM_TYPE</code> in the subclass of
+    {@link android.provider.ContactsContract.CommonDataKinds} associated with the data type.
+    The subclasses have names that indicate their data type; for example, the subclass for email
+    data is {@link android.provider.ContactsContract.CommonDataKinds.Email}, and the custom MIME
+    type for email data is defined by the constant
+    {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
+    Email.CONTENT_ITEM_TYPE}.
+</p>
+<p>
+    Use the {@link android.provider.ContactsContract.Data} table for your search. All of the
+    constants you need for your projection, selection clause, and sort order are defined in or
+    inherited by this table.
+</p>
+<h3 id="SpecificProjection">Define a projection</h3>
+<p>
+    To define a projection, choose one or more of the columns defined in
+    {@link android.provider.ContactsContract.Data} or the classes from which it inherits. The
+    Contacts Provider does an implicit join between {@link android.provider.ContactsContract.Data}
+    and other tables before it returns rows. For example:
+</p>
+<pre>
+    &#64;SuppressLint("InlinedApi")
+    private static final String[] PROJECTION =
+        {
+            /*
+             * The detail data row ID. To make a ListView work,
+             * this column is required.
+             */
+            Data._ID,
+            // The primary display name
+            Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB ?
+                    Data.DISPLAY_NAME_PRIMARY :
+                    Data.DISPLAY_NAME,
+            // The contact's _ID, to construct a content URI
+            Data.CONTACT_ID
+            // The contact's LOOKUP_KEY, to construct a content URI
+            Data.LOOKUP_KEY (a permanent link to the contact
+        };
+</pre>
+<h3 id="SpecificCriteria">Define search criteria</h3>
+<p>
+    To search for a string within a particular type of data, construct a selection clause from
+    the following:
+</p>
+<ul>
+    <li>
+        The name of the column that contains your search string. This name varies by data type,
+        so you need to find the subclass of
+        {@link android.provider.ContactsContract.CommonDataKinds} that corresponds to the data type
+        and then choose the column name from that subclass. For example, to search for
+        email addresses, use the column
+        {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS Email.ADDRESS}.
+    </li>
+    <li>
+        The search string itself, represented as the "?" character in the selection clause.
+    </li>
+    <li>
+        The name of the column that contains the custom MIME type value. This name is always
+        {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}.
+    </li>
+    <li>
+        The custom MIME type value for the data type. As described previously, this is the constant
+        <code>CONTENT_ITEM_TYPE</code> in the
+        {@link android.provider.ContactsContract.CommonDataKinds} subclass. For example, the MIME
+        type value for email data is
+        {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
+        Email.CONTENT_ITEM_TYPE}. Enclose the value in single quotes by concatenating a
+        "<code>'</code>" (single quote) character to the start and end of the constant; otherwise,
+        the provider interprets the value as a variable name rather than as a string value.
+        You don't need to use a placeholder for this value, because you're using a constant
+        rather than a user-supplied value.
+    </li>
+</ul>
+<p>
+    For example:
+</p>
+<pre>
+    /*
+     * Constructs search criteria from the search string
+     * and email MIME type
+     */
+    private static final String SELECTION =
+            /*
+             * Searches for an email address
+             * that matches the search string
+             */
+            Email.ADDRESS + " LIKE ? " + "AND " +
+            /*
+             * Searches for a MIME type that matches
+             * the value of the constant
+             * Email.CONTENT_ITEM_TYPE. Note the
+             * single quotes surrounding Email.CONTENT_ITEM_TYPE.
+             */
+            Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";
+</pre>
+<p>
+    Next, define variables to contain the selection argument:
+</p>
+<pre>
+    String mSearchString;
+    String[] mSelectionArgs = { "" };
+</pre>
+<h3 id="SpecificLoader">Implement onCreateLoader()</h3>
+<p>
+    Now that you've specified the data you want and how to find it, define a query in your
+    implementation of {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
+    onCreateLoader()}. Return a new {@link android.support.v4.content.CursorLoader} from this
+    method, using your projection, selection text expression, and selection array as
+    arguments. For a content URI, use
+    {@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI}. For example:
+</p>
+<pre>
+    &#64;Override
+    public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
+        // OPTIONAL: Makes search string into pattern
+        mSearchString = "%" + mSearchString + "%";
+        // Puts the search string into the selection criteria
+        mSelectionArgs[0] = mSearchString;
+        // Starts the query
+        return new CursorLoader(
+                getActivity(),
+                Data.CONTENT_URI,
+                PROJECTION,
+                SELECTION,
+                mSelectionArgs,
+                null
+        );
+    }
+</pre>
+<p>
+    These code snippets are the basis of a simple reverse lookup based on a specific type of detail
+    data. This is the best technique to use if your app focuses on a particular type of data, such
+    as emails, and you want allow users to get the names associated with a piece of data.
+</p>
+<h2 id="GeneralMatch">Match a Contact By Any Type of Data</h2>
+<p>
+    Retrieving a contact based on any type of data returns contacts if any of their data matches a
+    the search string, including name, email address, postal address, phone number, and so forth.
+    This results in a broad set of search results. For example, if the search string
+    is "Doe", then searching for any data type returns the contact "John Doe"; it also returns
+    contacts who live on "Doe Street".
+</p>
+<p>
+    To implement this type of retrieval, first implement the following code, as listed in
+    previous sections:
+</p>
+<ul>
+    <li>
+        Request Permission to Read the Provider.
+    </li>
+    <li>
+        Define ListView and item layouts.
+    </li>
+    <li>
+    <li>
+        Define a Fragment that displays the list of contacts.
+    </li>
+    <li>
+        Define global variables.
+    </li>
+    <li>
+        Initialize the Fragment.
+    </li>
+    <li>
+        Set up the CursorAdapter for the ListView.
+    </li>
+    <li>
+        Set the selected contact listener.
+    </li>
+    <li>
+        Define a projection.
+    </li>
+    <li>
+        Define constants for the Cursor column indexes.
+        <p>
+            For this type of retrieval, you're using the same table you used in the section
+            <a href="#NameMatch">Match a Contact by Name and List the Results</a>. Use the
+            same column indexes as well.
+        </p>
+    </li>
+    <li>
+        Define the onItemClick() method.
+    </li>
+    <li>
+        Initialize the loader.
+    </li>
+    <li>
+
+        Implement onLoadFinished() and onLoaderReset().
+    </li>
+</ul>
+<p>
+    The following steps show you the additional code you need to match a search string to
+    any type of data and display the results.
+</p>
+<h3 id="NoSelection">Remove selection criteria</h3>
+<p>
+    Don't define the <code>SELECTION</code> constants or the <code>mSelectionArgs</code> variable.
+    These aren't used in this type of retrieval.
+</p>
+<h3 id="CreateLoaderAny">Implement onCreateLoader()</h3>
+<p>
+    Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
+    onCreateLoader()} method, returning a new {@link android.support.v4.content.CursorLoader}.
+    You don't need to convert the search string into a pattern, because the Contacts Provider does
+    that automatically. Use
+    {@link android.provider.ContactsContract.Contacts#CONTENT_FILTER_URI
+    Contacts.CONTENT_FILTER_URI} as the base URI, and append your search string to it by calling
+    {@link android.net.Uri#withAppendedPath Uri.withAppendedPath()}. Using this URI
+    automatically triggers searching for any data type, as shown in the following example:
+</p>
+<pre>
+    &#64;Override
+    public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
+        /*
+         * Appends the search string to the base URI. Always
+         * encode search strings to ensure they're in proper
+         * format.
+         */
+        Uri contentUri = Uri.withAppendedPath(
+                Contacts.CONTENT_FILTER_URI,
+                Uri.encode(mSearchString));
+        // Starts the query
+        return new CursorLoader(
+                getActivity(),
+                contentUri,
+                PROJECTION,
+                null,
+                null,
+                null
+        );
+    }
+</pre>
+<p>
+    These code snippets are the basis of an app that does a broad search of the Contacts Provider.
+    The technique is useful for apps that want to implement functionality similar to the
+    People app's contact list screen.
+</p>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 985fc44..7a3f2ca 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -484,7 +484,37 @@
       </a>
     </div>
     <ul>
-    
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>training/contacts-provider/index.html"
+             description=
+             "How to use Android's central address book, the Contacts Provider, to
+             display contacts and their details and modify contact information.">
+          Accessing Contacts Data</a>
+        </div>
+        <ul>
+          <li>
+                <a href="<?cs var:toroot ?>training/contacts-provider/retrieve-names.html">
+                Retrieving a List of Contacts
+                </a>
+          </li>
+          <li>
+                <a href="<?cs var:toroot ?>training/contacts-provider/retrieve-details.html">
+                Retrieving Details for a Contact
+                </a>
+          </li>
+          <li>
+                <a href="<?cs var:toroot ?>training/contacts-provider/modify-data.html">
+                Modifying Contacts Using Intents
+                </a>
+          </li>
+          <li>
+                <a href="<?cs var:toroot ?>training/contacts-provider/display-contact-badge.html">
+                Displaying the Quick Contact Badge
+                </a>
+          </li>
+        </ul>
+      </li>
       <li class="nav-section">
         <div class="nav-section-header">
           <a href="<?cs var:toroot ?>training/id-auth/index.html"
@@ -879,7 +909,7 @@
         </ul>
       </li>
     </ul>
-  </li> <!-- end of User Input -->    
+  </li> <!-- end of User Input -->
 
   <li class="nav-section">
     <div class="nav-section-header">
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 9507030..d7c8255 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -110,6 +110,16 @@
     }
 
     /**
+     * Get the YUV format
+     *
+     * @hide
+     * @return int
+     */
+    public int getYuv() {
+        return mDimYuv;
+    }
+
+    /**
      * Return if the Type has a mipmap chain.
      *
      * @return boolean
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index cb9da8f..2afc8c1 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -52,6 +52,8 @@
         kOpBatch_Count, // Add other batch ids before this
     };
 
+    void clear();
+
     bool isEmpty() { return mBatches.isEmpty(); }
 
     /**
@@ -78,8 +80,6 @@
      */
     void resetBatchingState();
 
-    void clear();
-
     void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op);
     void storeRestoreToCountBarrier(int newSaveCount);
 
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 1899002..2998535 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,6 +18,8 @@
 
 #include <utils/Log.h>
 
+#include "DisplayList.h"
+#include "DeferredDisplayList.h"
 #include "Layer.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
@@ -43,15 +45,18 @@
     fbo = 0;
     stencil = NULL;
     debugDrawUpdate = false;
+    deferredList = NULL;
     Caches::getInstance().resourceCache.incrementRefcount(this);
 }
 
 Layer::~Layer() {
-    if (mesh) delete mesh;
-    if (meshIndices) delete meshIndices;
     if (colorFilter) Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
     removeFbo();
     deleteTexture();
+
+    delete[] mesh;
+    delete[] meshIndices;
+    delete deferredList;
 }
 
 uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
@@ -133,5 +138,43 @@
     }
 }
 
+void Layer::defer() {
+    if (!deferredList) {
+        deferredList = new DeferredDisplayList;
+    }
+    DeferStateStruct deferredState(*deferredList, *renderer,
+            DisplayList::kReplayFlag_ClipChildren);
+
+    const float width = layer.getWidth();
+    const float height = layer.getHeight();
+
+    if (dirtyRect.isEmpty() || (dirtyRect.left <= 0 && dirtyRect.top <= 0 &&
+            dirtyRect.right >= width && dirtyRect.bottom >= height)) {
+        dirtyRect.set(0, 0, width, height);
+    }
+
+    renderer->initViewport(width, height);
+    renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
+            dirtyRect.right, dirtyRect.bottom, !isBlend());
+
+    displayList->defer(deferredState, 0);
+}
+
+void Layer::flush() {
+    if (deferredList && !deferredList->isEmpty()) {
+        renderer->setViewport(layer.getWidth(), layer.getHeight());
+        renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
+                !isBlend());
+
+        deferredList->flush(*renderer, dirtyRect);
+
+        renderer->finish();
+        renderer = NULL;
+
+        dirtyRect.setEmpty();
+        deferredList->clear();
+    }
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index ccf1da5..0e00191 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -42,6 +42,8 @@
 // Forward declarations
 class OpenGLRenderer;
 class DisplayList;
+class DeferredDisplayList;
+class DeferStateStruct;
 
 /**
  * A layer has dimensions and is backed by an OpenGL texture or FBO.
@@ -271,6 +273,9 @@
         return transform;
     }
 
+    void defer();
+    void flush();
+
     /**
      * Bounds of the layer.
      */
@@ -379,6 +384,12 @@
      */
     mat4 transform;
 
+    /**
+     * Used to defer display lists when the layer is updated with a
+     * display list.
+     */
+    DeferredDisplayList* deferredList;
+
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 9aa9615..bb02286 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -129,8 +129,8 @@
 void LayerRenderer::generateMesh() {
     if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
         if (mLayer->mesh) {
-            delete mLayer->mesh;
-            delete mLayer->meshIndices;
+            delete[] mLayer->mesh;
+            delete[] mLayer->meshIndices;
 
             mLayer->mesh = NULL;
             mLayer->meshIndices = NULL;
@@ -153,8 +153,8 @@
     GLsizei elementCount = count * 6;
 
     if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
-        delete mLayer->mesh;
-        delete mLayer->meshIndices;
+        delete[] mLayer->mesh;
+        delete[] mLayer->meshIndices;
 
         mLayer->mesh = NULL;
         mLayer->meshIndices = NULL;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2cf7183..1c36a23 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -120,6 +120,7 @@
     memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
 
     mFirstSnapshot = new Snapshot;
+    mFrameStarted = false;
 
     mScissorOptimizationDisabled = false;
 }
@@ -179,14 +180,11 @@
     mFirstSnapshot->viewport.set(0, 0, width, height);
 }
 
-status_t OpenGLRenderer::prepare(bool opaque) {
-    return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
-}
-
-status_t OpenGLRenderer::prepareDirty(float left, float top,
+void OpenGLRenderer::setupFrameState(float left, float top,
         float right, float bottom, bool opaque) {
     mCaches.clearGarbage();
 
+    mOpaque = opaque;
     mSnapshot = new Snapshot(mFirstSnapshot,
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     mSnapshot->fbo = getTargetFbo();
@@ -194,13 +192,17 @@
 
     mSnapshot->setClip(left, top, right, bottom);
     mTilingClip.set(left, top, right, bottom);
+}
+
+status_t OpenGLRenderer::startFrame() {
+    if (mFrameStarted) return DrawGlInfo::kStatusDone;
+    mFrameStarted = true;
+
     mDirtyClip = true;
 
-    updateLayers();
+    discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
 
-    discardFramebuffer(left, top, right, bottom);
-
-    syncState();
+    glViewport(0, 0, mWidth, mHeight);
 
     // Functors break the tiling extension in pretty spectacular ways
     // This ensures we don't use tiling when a functor is going to be
@@ -211,7 +213,30 @@
 
     debugOverdraw(true, true);
 
-    return clear(left, top, right, bottom, opaque);
+    return clear(mTilingClip.left, mTilingClip.top,
+            mTilingClip.right, mTilingClip.bottom, mOpaque);
+}
+
+status_t OpenGLRenderer::prepare(bool opaque) {
+    return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
+}
+
+status_t OpenGLRenderer::prepareDirty(float left, float top,
+        float right, float bottom, bool opaque) {
+    setupFrameState(left, top, right, bottom, opaque);
+
+    // Layer renderers will start the frame immediately
+    // The framebuffer renderer will first defer the display list
+    // for each layer and wait until the first drawing command
+    // to start the frame
+    if (mSnapshot->fbo == 0) {
+        syncState();
+        updateLayers();
+    } else {
+        return startFrame();
+    }
+
+    return DrawGlInfo::kStatusDone;
 }
 
 void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
@@ -241,8 +266,6 @@
 }
 
 void OpenGLRenderer::syncState() {
-    glViewport(0, 0, mWidth, mHeight);
-
     if (mCaches.blend) {
         glEnable(GL_BLEND);
     } else {
@@ -312,6 +335,8 @@
         }
 #endif
     }
+
+    mFrameStarted = false;
 }
 
 void OpenGLRenderer::interrupt() {
@@ -503,8 +528,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
-    if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
-        OpenGLRenderer* renderer = layer->renderer;
+    if (layer->deferredUpdateScheduled && layer->renderer &&
+            layer->displayList && layer->displayList->isRenderable()) {
         Rect& dirty = layer->dirtyRect;
 
         if (inFrame) {
@@ -512,19 +537,29 @@
             debugOverdraw(false, false);
         }
 
-        renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
-        renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
-        renderer->drawDisplayList(layer->displayList, dirty, DisplayList::kReplayFlag_ClipChildren);
-        renderer->finish();
+        if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
+            OpenGLRenderer* renderer = layer->renderer;
+            renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
+            renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom,
+                    !layer->isBlend());
+            renderer->drawDisplayList(layer->displayList, dirty,
+                    DisplayList::kReplayFlag_ClipChildren);
+            renderer->finish();
+        } else {
+            layer->defer();
+        }
 
         if (inFrame) {
             resumeAfterLayer();
             startTiling(mSnapshot);
         }
 
-        dirty.setEmpty();
+        if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
+            dirty.setEmpty();
+            layer->renderer = NULL;
+        }
+
         layer->deferredUpdateScheduled = false;
-        layer->renderer = NULL;
         layer->displayList = NULL;
         layer->debugDrawUpdate = mCaches.debugLayersUpdates;
 
@@ -535,19 +570,54 @@
 }
 
 void OpenGLRenderer::updateLayers() {
+    // If draw deferring is enabled this method will simply defer
+    // the display list of each individual layer. The layers remain
+    // in the layer updates list which will be cleared by flushLayers().
     int count = mLayerUpdates.size();
     if (count > 0) {
-        startMark("Layer Updates");
+        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+            startMark("Layer Updates");
+        } else {
+            startMark("Defer Layer Updates");
+        }
 
         // Note: it is very important to update the layers in reverse order
         for (int i = count - 1; i >= 0; i--) {
             Layer* layer = mLayerUpdates.itemAt(i);
             updateLayer(layer, false);
-            mCaches.resourceCache.decrementRefcount(layer);
+            if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+                mCaches.resourceCache.decrementRefcount(layer);
+            }
         }
-        mLayerUpdates.clear();
 
+        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+            mLayerUpdates.clear();
+            glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+        }
+        endMark();
+    }
+}
+
+void OpenGLRenderer::flushLayers() {
+    int count = mLayerUpdates.size();
+    if (count > 0) {
+        startMark("Apply Layer Updates");
+        char layerName[12];
+
+        // Note: it is very important to update the layers in reverse order
+        for (int i = count - 1; i >= 0; i--) {
+            sprintf(layerName, "Layer #%d", i);
+            startMark(layerName); {
+                Layer* layer = mLayerUpdates.itemAt(i);
+                layer->flush();
+                mCaches.resourceCache.decrementRefcount(layer);
+            }
+            endMark();
+        }
+
+        mLayerUpdates.clear();
         glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+
         endMark();
     }
 }
@@ -1832,6 +1902,7 @@
     // will be performed by the display list itself
     if (displayList && displayList->isRenderable()) {
         if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+            startFrame();
             ReplayStateStruct replayStruct(*this, dirty, replayFlags);
             displayList->replay(replayStruct, 0);
             return replayStruct.mDrawGlStatus;
@@ -1840,6 +1911,10 @@
         DeferredDisplayList deferredList;
         DeferStateStruct deferStruct(deferredList, *this, replayFlags);
         displayList->defer(deferStruct, 0);
+
+        flushLayers();
+        startFrame();
+
         return deferredList.flush(*this, dirty);
     }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 7bb9395..31dc9c8 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -362,6 +362,18 @@
     void initViewport(int width, int height);
 
     /**
+     * Perform the setup specific to a frame. This method does not
+     * issue any OpenGL commands.
+     */
+    void setupFrameState(float left, float top, float right, float bottom, bool opaque);
+
+    /**
+     * Indicates the start of rendering. This method will setup the
+     * initial OpenGL state (viewport, clearing the buffer, etc.)
+     */
+    status_t startFrame();
+
+    /**
      * Clears the underlying surface if needed.
      */
     virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
@@ -897,6 +909,7 @@
 
     bool updateLayer(Layer* layer, bool inFrame);
     void updateLayers();
+    void flushLayers();
 
     /**
      * Renders the specified region as a series of rectangles. This method
@@ -948,6 +961,10 @@
     sp<Snapshot> mSnapshot;
     // State used to define the clipping region
     Rect mTilingClip;
+    // Is the target render surface opaque
+    bool mOpaque;
+    // Is a frame currently being rendered
+    bool mFrameStarted;
 
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
@@ -996,6 +1013,7 @@
     String8 mName;
 
     friend class DisplayListRenderer;
+    friend class Layer;
     friend class TextSetupFunctor;
 
 }; // class OpenGLRenderer
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 07c4207..fdb10e2 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -262,12 +262,13 @@
 
     PathTexture* texture = createTexture(left, top, offset, width, height,
             path->getGenerationID());
-    addTexture(entry, &bitmap, texture);
+    generateTexture(entry, &bitmap, texture);
 
     return texture;
 }
 
-void PathCache::addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture) {
+void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap,
+        PathTexture* texture, bool addToCache) {
     generateTexture(*bitmap, texture);
 
     uint32_t size = texture->width * texture->height;
@@ -278,7 +279,9 @@
         if (mDebugEnabled) {
             ALOGD("Shape created, size = %d", size);
         }
-        mCache.put(entry, texture);
+        if (addToCache) {
+            mCache.put(entry, texture);
+        }
     } else {
         texture->cleanup = true;
     }
@@ -414,7 +417,7 @@
             // producing the bitmap, so let's wait
             SkBitmap* bitmap = task->getResult();
             if (bitmap) {
-                addTexture(entry, bitmap, texture);
+                generateTexture(entry, bitmap, texture, false);
                 texture->clearTask();
             } else {
                 ALOGW("Path too large to be rendered into a texture");
@@ -423,6 +426,8 @@
                 mCache.remove(entry);
             }
         } else if (path->getGenerationID() != texture->generation) {
+            // The size of the path might have changed so we first
+            // remove the entry from the cache
             mCache.remove(entry);
             texture = addTexture(entry, path, paint);
         }
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 1467231..dd1f996 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -253,7 +253,13 @@
     PathTexture* addTexture(const PathDescription& entry,
             const SkPath *path, const SkPaint* paint);
     PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
-    void addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture);
+
+    /**
+     * Generates the texture from a bitmap into the specified texture structure.
+     */
+    void generateTexture(SkBitmap& bitmap, Texture* texture);
+    void generateTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture,
+            bool addToCache = true);
 
     PathTexture* get(const PathDescription& entry) {
         return mCache.get(entry);
@@ -282,11 +288,6 @@
         return true;
     }
 
-    /**
-     * Generates the texture from a bitmap into the specified texture structure.
-     */
-    void generateTexture(SkBitmap& bitmap, Texture* texture);
-
     void init();
 
     class PathTask: public Task<SkBitmap*> {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 18f3b1e..38cdb8a 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -165,6 +165,7 @@
     private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28;
     private static final int MSG_PROMOTE_RCC = 29;
     private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
+    private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
 
 
     // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -1685,6 +1686,12 @@
     private static final String ASSET_FILE_VERSION = "1.0";
     private static final String GROUP_TOUCH_SOUNDS = "touch_sounds";
 
+    private static final int SOUND_EFECTS_LOAD_TIMEOUT_MS = 5000;
+
+    class LoadSoundEffectReply {
+        public int mStatus = 1;
+    };
+
     private void loadTouchSoundAssetDefaults() {
         SOUND_EFFECT_FILES.add("Effect_Tick.ogg");
         for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {
@@ -1696,6 +1703,11 @@
     private void loadTouchSoundAssets() {
         XmlResourceParser parser = null;
 
+        // only load assets once.
+        if (!SOUND_EFFECT_FILES.isEmpty()) {
+            return;
+        }
+
         loadTouchSoundAssetDefaults();
 
         try {
@@ -1765,14 +1777,12 @@
 
     /** @see AudioManager#playSoundEffect(int) */
     public void playSoundEffect(int effectType) {
-        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
-                effectType, -1, null, 0);
+        playSoundEffectVolume(effectType, -1.0f);
     }
 
     /** @see AudioManager#playSoundEffect(int, float) */
     public void playSoundEffectVolume(int effectType, float volume) {
-        loadSoundEffects();
-        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
+        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
                 effectType, (int) (volume * 1000), null, 0);
     }
 
@@ -1781,116 +1791,20 @@
      * This method must be called at first when sound effects are enabled
      */
     public boolean loadSoundEffects() {
-        int status;
+        int attempts = 3;
+        LoadSoundEffectReply reply = new LoadSoundEffectReply();
 
-        loadTouchSoundAssets();
-
-        synchronized (mSoundEffectsLock) {
-            if (!mBootCompleted) {
-                Log.w(TAG, "loadSoundEffects() called before boot complete");
-                return false;
-            }
-
-            if (mSoundPool != null) {
-                return true;
-            }
-            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
-
-            try {
-                mSoundPoolCallBack = null;
-                mSoundPoolListenerThread = new SoundPoolListenerThread();
-                mSoundPoolListenerThread.start();
-                // Wait for mSoundPoolCallBack to be set by the other thread
-                mSoundEffectsLock.wait();
-            } catch (InterruptedException e) {
-                Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
-            }
-
-            if (mSoundPoolCallBack == null) {
-                Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
-                if (mSoundPoolLooper != null) {
-                    mSoundPoolLooper.quit();
-                    mSoundPoolLooper = null;
-                }
-                mSoundPoolListenerThread = null;
-                mSoundPool.release();
-                mSoundPool = null;
-                return false;
-            }
-            /*
-             * poolId table: The value -1 in this table indicates that corresponding
-             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
-             * Once loaded, the value in poolId is the sample ID and the same
-             * sample can be reused for another effect using the same file.
-             */
-            int[] poolId = new int[SOUND_EFFECT_FILES.size()];
-            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
-                poolId[fileIdx] = -1;
-            }
-            /*
-             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
-             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
-             * this indicates we have a valid sample loaded for this effect.
-             */
-
-            int lastSample = 0;
-            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
-                // Do not load sample if this effect uses the MediaPlayer
-                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
-                    continue;
-                }
-                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
-                    String filePath = Environment.getRootDirectory()
-                            + SOUND_EFFECTS_PATH
-                            + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
-                    int sampleId = mSoundPool.load(filePath, 0);
-                    if (sampleId <= 0) {
-                        Log.w(TAG, "Soundpool could not load file: "+filePath);
-                    } else {
-                        SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
-                        poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
-                        lastSample = sampleId;
-                    }
-                } else {
-                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
-                }
-            }
-            // wait for all samples to be loaded
-            if (lastSample != 0) {
-                mSoundPoolCallBack.setLastSample(lastSample);
-
+        synchronized (reply) {
+            sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);
+            while ((reply.mStatus == 1) && (attempts-- > 0)) {
                 try {
-                    mSoundEffectsLock.wait();
-                    status = mSoundPoolCallBack.status();
-                } catch (java.lang.InterruptedException e) {
-                    Log.w(TAG, "Interrupted while waiting sound pool callback.");
-                    status = -1;
+                    reply.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+                } catch (InterruptedException e) {
+                    Log.w(TAG, "loadSoundEffects Interrupted while waiting sound pool loaded.");
                 }
-            } else {
-                status = -1;
-            }
-
-            if (mSoundPoolLooper != null) {
-                mSoundPoolLooper.quit();
-                mSoundPoolLooper = null;
-            }
-            mSoundPoolListenerThread = null;
-            if (status != 0) {
-                Log.w(TAG,
-                        "loadSoundEffects(), Error "
-                                + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
-                                + " while loading samples");
-                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
-                    if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
-                        SOUND_EFFECT_FILES_MAP[effect][1] = -1;
-                    }
-                }
-
-                mSoundPool.release();
-                mSoundPool = null;
             }
         }
-        return (status == 0);
+        return (reply.mStatus == 0);
     }
 
     /**
@@ -1899,32 +1813,7 @@
      *  sound effects are disabled.
      */
     public void unloadSoundEffects() {
-        synchronized (mSoundEffectsLock) {
-            if (mSoundPool == null) {
-                return;
-            }
-
-            mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
-            mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
-
-            int[] poolId = new int[SOUND_EFFECT_FILES.size()];
-            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
-                poolId[fileIdx] = 0;
-            }
-
-            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
-                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
-                    continue;
-                }
-                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
-                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
-                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
-                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
-                }
-            }
-            mSoundPool.release();
-            mSoundPool = null;
-        }
+        sendMsg(mAudioHandler, MSG_UNLOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
     }
 
     class SoundPoolListenerThread extends Thread {
@@ -1952,23 +1841,30 @@
     private final class SoundPoolCallback implements
             android.media.SoundPool.OnLoadCompleteListener {
 
-        int mStatus;
-        int mLastSample;
+        int mStatus = 1; // 1 means neither error nor last sample loaded yet
+        List<Integer> mSamples = new ArrayList<Integer>();
 
         public int status() {
             return mStatus;
         }
 
-        public void setLastSample(int sample) {
-            mLastSample = sample;
+        public void setSamples(int[] samples) {
+            for (int i = 0; i < samples.length; i++) {
+                // do not wait ack for samples rejected upfront by SoundPool
+                if (samples[i] > 0) {
+                    mSamples.add(samples[i]);
+                }
+            }
         }
 
         public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
             synchronized (mSoundEffectsLock) {
-                if (status != 0) {
-                    mStatus = status;
+                int i = mSamples.indexOf(sampleId);
+                if (i >= 0) {
+                    mSamples.remove(i);
                 }
-                if (sampleId == mLastSample) {
+                if ((status != 0) || mSamples. isEmpty()) {
+                    mStatus = status;
                     mSoundEffectsLock.notify();
                 }
             }
@@ -3404,8 +3300,160 @@
             Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
         }
 
-        private void playSoundEffect(int effectType, int volume) {
+        private boolean onLoadSoundEffects() {
+            int status;
+
             synchronized (mSoundEffectsLock) {
+                if (!mBootCompleted) {
+                    Log.w(TAG, "onLoadSoundEffects() called before boot complete");
+                    return false;
+                }
+
+                if (mSoundPool != null) {
+                    return true;
+                }
+
+                loadTouchSoundAssets();
+
+                mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
+                mSoundPoolCallBack = null;
+                mSoundPoolListenerThread = new SoundPoolListenerThread();
+                mSoundPoolListenerThread.start();
+                int attempts = 3;
+                while ((mSoundPoolCallBack == null) && (attempts-- > 0)) {
+                    try {
+                        // Wait for mSoundPoolCallBack to be set by the other thread
+                        mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+                    } catch (InterruptedException e) {
+                        Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
+                    }
+                }
+
+                if (mSoundPoolCallBack == null) {
+                    Log.w(TAG, "onLoadSoundEffects() SoundPool listener or thread creation error");
+                    if (mSoundPoolLooper != null) {
+                        mSoundPoolLooper.quit();
+                        mSoundPoolLooper = null;
+                    }
+                    mSoundPoolListenerThread = null;
+                    mSoundPool.release();
+                    mSoundPool = null;
+                    return false;
+                }
+                /*
+                 * poolId table: The value -1 in this table indicates that corresponding
+                 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
+                 * Once loaded, the value in poolId is the sample ID and the same
+                 * sample can be reused for another effect using the same file.
+                 */
+                int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+                for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
+                    poolId[fileIdx] = -1;
+                }
+                /*
+                 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
+                 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
+                 * this indicates we have a valid sample loaded for this effect.
+                 */
+
+                int numSamples = 0;
+                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+                    // Do not load sample if this effect uses the MediaPlayer
+                    if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
+                        continue;
+                    }
+                    if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
+                        String filePath = Environment.getRootDirectory()
+                                + SOUND_EFFECTS_PATH
+                                + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
+                        int sampleId = mSoundPool.load(filePath, 0);
+                        if (sampleId <= 0) {
+                            Log.w(TAG, "Soundpool could not load file: "+filePath);
+                        } else {
+                            SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
+                            poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
+                            numSamples++;
+                        }
+                    } else {
+                        SOUND_EFFECT_FILES_MAP[effect][1] =
+                                poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
+                    }
+                }
+                // wait for all samples to be loaded
+                if (numSamples > 0) {
+                    mSoundPoolCallBack.setSamples(poolId);
+
+                    attempts = 3;
+                    status = 1;
+                    while ((status == 1) && (attempts-- > 0)) {
+                        try {
+                            mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+                            status = mSoundPoolCallBack.status();
+                        } catch (InterruptedException e) {
+                            Log.w(TAG, "Interrupted while waiting sound pool callback.");
+                        }
+                    }
+                } else {
+                    status = -1;
+                }
+
+                if (mSoundPoolLooper != null) {
+                    mSoundPoolLooper.quit();
+                    mSoundPoolLooper = null;
+                }
+                mSoundPoolListenerThread = null;
+                if (status != 0) {
+                    Log.w(TAG,
+                            "onLoadSoundEffects(), Error "+status+ " while loading samples");
+                    for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+                        if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
+                            SOUND_EFFECT_FILES_MAP[effect][1] = -1;
+                        }
+                    }
+
+                    mSoundPool.release();
+                    mSoundPool = null;
+                }
+            }
+            return (status == 0);
+        }
+
+        /**
+         *  Unloads samples from the sound pool.
+         *  This method can be called to free some memory when
+         *  sound effects are disabled.
+         */
+        private void onUnloadSoundEffects() {
+            synchronized (mSoundEffectsLock) {
+                if (mSoundPool == null) {
+                    return;
+                }
+
+                int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+                for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
+                    poolId[fileIdx] = 0;
+                }
+
+                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+                    if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
+                        continue;
+                    }
+                    if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
+                        mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
+                        SOUND_EFFECT_FILES_MAP[effect][1] = -1;
+                        poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
+                    }
+                }
+                mSoundPool.release();
+                mSoundPool = null;
+            }
+        }
+
+        private void onPlaySoundEffect(int effectType, int volume) {
+            synchronized (mSoundEffectsLock) {
+
+                onLoadSoundEffects();
+
                 if (mSoundPool == null) {
                     return;
                 }
@@ -3418,7 +3466,8 @@
                 }
 
                 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
-                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
+                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1],
+                                        volFloat, volFloat, 0, 0, 1.0f);
                 } else {
                     MediaPlayer mediaPlayer = new MediaPlayer();
                     try {
@@ -3598,12 +3647,25 @@
                     AudioSystem.setParameters("restarting=false");
                     break;
 
+                case MSG_UNLOAD_SOUND_EFFECTS:
+                    onUnloadSoundEffects();
+                    break;
+
                 case MSG_LOAD_SOUND_EFFECTS:
-                    loadSoundEffects();
+                    //FIXME: onLoadSoundEffects() should be executed in a separate thread as it
+                    // can take several dozens of milliseconds to complete
+                    boolean loaded = onLoadSoundEffects();
+                    if (msg.obj != null) {
+                        LoadSoundEffectReply reply = (LoadSoundEffectReply)msg.obj;
+                        synchronized (reply) {
+                            reply.mStatus = loaded ? 0 : -1;
+                            reply.notify();
+                        }
+                    }
                     break;
 
                 case MSG_PLAY_SOUND_EFFECT:
-                    playSoundEffect(msg.arg1, msg.arg2);
+                    onPlaySoundEffect(msg.arg1, msg.arg2);
                     break;
 
                 case MSG_BTA2DP_DOCK_TIMEOUT:
@@ -4151,7 +4213,7 @@
                 }
             } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
                 mBootCompleted = true;
-                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
+                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
                         0, 0, null, 0);
 
                 mKeyguardManager =
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index d4f932e..430721e 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -165,6 +165,8 @@
  * Input dispatcher configuration.
  *
  * Specifies various options that modify the behavior of the input dispatcher.
+ * The values provided here are merely defaults. The actual values will come from ViewConfiguration
+ * and are passed into the dispatcher during initialization.
  */
 struct InputDispatcherConfiguration {
     // The key repeat initial timeout.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index a45c0ff..2a3c87e 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -63,6 +63,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -746,6 +747,9 @@
         // correct directory.
         mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
         mBaseStateDir.mkdirs();
+        if (!SELinux.restorecon(mBaseStateDir)) {
+            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+        }
         mDataDir = Environment.getDownloadCacheDirectory();
 
         mPasswordHashFile = new File(mBaseStateDir, "pwhash");
@@ -2136,6 +2140,10 @@
                         ParcelFileDescriptor.MODE_CREATE |
                         ParcelFileDescriptor.MODE_TRUNCATE);
 
+                if (!SELinux.restorecon(mBackupDataName)) {
+                    Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
+                }
+
                 mNewState = ParcelFileDescriptor.open(mNewStateName,
                         ParcelFileDescriptor.MODE_READ_WRITE |
                         ParcelFileDescriptor.MODE_CREATE |
@@ -4697,6 +4705,10 @@
                             ParcelFileDescriptor.MODE_CREATE |
                             ParcelFileDescriptor.MODE_TRUNCATE);
 
+                if (!SELinux.restorecon(mBackupDataName)) {
+                    Slog.e(TAG, "SElinux restorecon failed for " + mBackupDataName);
+                }
+
                 if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
                     // Transport-level failure, so we wind everything up and
                     // terminate the restore operation.
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 18b4ec1..d3e7c24 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -41,10 +41,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -62,6 +66,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.AtomicFile;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
@@ -88,10 +93,11 @@
  * Implementation of the device policy APIs.
  */
 public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
-    private static final String DEVICE_POLICIES_XML = "device_policies.xml";
 
     private static final String TAG = "DevicePolicyManagerService";
 
+    private static final String DEVICE_POLICIES_XML = "device_policies.xml";
+
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
     private static final long MS_PER_DAY = 86400 * 1000;
@@ -109,6 +115,8 @@
     IPowerManager mIPowerManager;
     IWindowManager mIWindowManager;
 
+    private DeviceOwner mDeviceOwner;
+
     public static class DevicePolicyData {
         int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
         int mActivePasswordLength = 0;
@@ -507,6 +515,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
         filter.addDataScheme("package");
         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
     }
@@ -545,6 +554,14 @@
         }
     }
 
+    void loadDeviceOwner() {
+        synchronized (this) {
+            if (DeviceOwner.isRegistered()) {
+                mDeviceOwner = new DeviceOwner();
+            }
+        }
+    }
+
     /**
      * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
      * reminders.  Clears alarm if no expirations are configured.
@@ -709,7 +726,9 @@
         Intent resolveIntent = new Intent();
         resolveIntent.setComponent(adminName);
         List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
-                resolveIntent, PackageManager.GET_META_DATA, userHandle);
+                resolveIntent,
+                PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+                userHandle);
         if (infos == null || infos.size() <= 0) {
             throw new IllegalArgumentException("Unknown admin: " + adminName);
         }
@@ -994,6 +1013,7 @@
     public void systemReady() {
         synchronized (this) {
             loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER);
+            loadDeviceOwner();
         }
     }
 
@@ -1052,6 +1072,7 @@
                 }
                 if (replaceIndex == -1) {
                     policy.mAdminList.add(newAdmin);
+                    enableIfNecessary(info.getPackageName(), userHandle);
                 } else {
                     policy.mAdminList.set(replaceIndex, newAdmin);
                 }
@@ -1119,6 +1140,11 @@
                 return;
             }
             if (admin.getUid() != Binder.getCallingUid()) {
+                // If trying to remove device owner, refuse when the caller is not the owner.
+                if (mDeviceOwner != null
+                        && adminReceiver.getPackageName().equals(mDeviceOwner.getPackageName())) {
+                    return;
+                }
                 mContext.enforceCallingOrSelfPermission(
                         android.Manifest.permission.BIND_DEVICE_ADMIN, null);
             }
@@ -2351,6 +2377,49 @@
         }
     }
 
+    @Override
+    public boolean setDeviceOwner(String packageName) {
+        if (packageName == null
+                || !DeviceOwner.isInstalled(packageName, mContext.getPackageManager())) {
+            throw new IllegalArgumentException("Invalid package name " + packageName
+                    + " for device owner");
+        }
+        synchronized (this) {
+            if (mDeviceOwner == null && !isDeviceProvisioned()) {
+                mDeviceOwner = new DeviceOwner(packageName);
+                mDeviceOwner.writeOwnerFile();
+                return true;
+            } else {
+                throw new IllegalStateException("Trying to set device owner to " + packageName
+                        + ", owner=" + mDeviceOwner.getPackageName()
+                        + ", device_provisioned=" + isDeviceProvisioned());
+            }
+        }
+    }
+
+    @Override
+    public boolean isDeviceOwner(String packageName) {
+        synchronized (this) {
+            return mDeviceOwner != null
+                    && mDeviceOwner.getPackageName().equals(packageName);
+        }
+    }
+
+    @Override
+    public String getDeviceOwner() {
+        synchronized (this) {
+            if (mDeviceOwner != null) {
+                return mDeviceOwner.getPackageName();
+            }
+        }
+        return null;
+    }
+
+    private boolean isDeviceProvisioned() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) > 0;
+    }
+
     private void enforceCrossUserPermission(int userHandle) {
         if (userHandle < 0) {
             throw new IllegalArgumentException("Invalid userId " + userHandle);
@@ -2364,6 +2433,22 @@
         }
     }
 
+    private void enableIfNecessary(String packageName, int userId) {
+        try {
+            IPackageManager ipm = AppGlobals.getPackageManager();
+            ApplicationInfo ai = ipm.getApplicationInfo(packageName,
+                    PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+                    userId);
+            if (ai.enabledSetting
+                    == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+                ipm.setApplicationEnabledSetting(packageName,
+                        PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                        PackageManager.DONT_KILL_APP, userId);
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -2399,4 +2484,92 @@
             }
         }
     }
+
+    static class DeviceOwner {
+        private static final String DEVICE_OWNER_XML = "device_owner.xml";
+        private static final String TAG_DEVICE_OWNER = "device-owner";
+        private static final String ATTR_PACKAGE = "package";
+        private String mPackageName;
+
+        DeviceOwner() {
+            readOwnerFile();
+        }
+
+        DeviceOwner(String packageName) {
+            this.mPackageName = packageName;
+        }
+
+        static boolean isRegistered() {
+            return new File(Environment.getSystemSecureDirectory(),
+                    DEVICE_OWNER_XML).exists();
+        }
+
+        String getPackageName() {
+            return mPackageName;
+        }
+
+        static boolean isInstalled(String packageName, PackageManager pm) {
+            try {
+                PackageInfo pi;
+                if ((pi = pm.getPackageInfo(packageName, 0)) != null) {
+                    if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        return true;
+                    }
+                }
+            } catch (NameNotFoundException nnfe) {
+                Slog.w(TAG, "Device Owner package " + packageName + " not installed.");
+            }
+            return false;
+        }
+
+        void readOwnerFile() {
+            AtomicFile file = new AtomicFile(new File(Environment.getSystemSecureDirectory(),
+                    DEVICE_OWNER_XML));
+            try {
+                FileInputStream input = file.openRead();
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(input, null);
+                int type;
+                while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                        && type != XmlPullParser.START_TAG) {
+                }
+                String tag = parser.getName();
+                if (!TAG_DEVICE_OWNER.equals(tag)) {
+                    throw new XmlPullParserException(
+                            "Device Owner file does not start with device-owner tag: found " + tag);
+                }
+                mPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+                input.close();
+            } catch (XmlPullParserException xppe) {
+                Slog.e(TAG, "Error parsing device-owner file\n" + xppe);
+            } catch (IOException ioe) {
+                Slog.e(TAG, "IO Exception when reading device-owner file\n" + ioe);
+            }
+        }
+
+        void writeOwnerFile() {
+            synchronized (this) {
+                writeOwnerFileLocked();
+            }
+        }
+
+        private void writeOwnerFileLocked() {
+            AtomicFile file = new AtomicFile(new File(Environment.getSystemSecureDirectory(),
+                    DEVICE_OWNER_XML));
+            try {
+                FileOutputStream output = file.startWrite();
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(output, "utf-8");
+                out.startDocument(null, true);
+                out.startTag(null, TAG_DEVICE_OWNER);
+                out.attribute(null, ATTR_PACKAGE, mPackageName);
+                out.endTag(null, TAG_DEVICE_OWNER);
+                out.endDocument();
+                out.flush();
+                file.finishWrite(output);
+            } catch (IOException ioe) {
+                Slog.e(TAG, "IO Exception when writing device-owner file\n" + ioe);
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index dd081a1..2d53023 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -88,6 +88,7 @@
 import android.util.Slog;
 import android.util.Xml;
 import android.view.IWindowManager;
+import android.view.InputChannel;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -170,7 +171,7 @@
     private final HardKeyboardListener mHardKeyboardListener;
     private final WindowManagerService mWindowManagerService;
 
-    final InputBindResult mNoBinding = new InputBindResult(null, null, -1);
+    final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1);
 
     // All known input methods.  mMethodMap also serves as the global
     // lock for this class.
@@ -202,7 +203,9 @@
     class SessionState {
         final ClientState client;
         final IInputMethod method;
-        final IInputMethodSession session;
+
+        IInputMethodSession session;
+        InputChannel channel;
 
         @Override
         public String toString() {
@@ -211,18 +214,20 @@
                             System.identityHashCode(method))
                     + " session " + Integer.toHexString(
                             System.identityHashCode(session))
+                    + " channel " + channel
                     + "}";
         }
 
         SessionState(ClientState _client, IInputMethod _method,
-                IInputMethodSession _session) {
+                IInputMethodSession _session, InputChannel _channel) {
             client = _client;
             method = _method;
             session = _session;
+            channel = _channel;
         }
     }
 
-    class ClientState {
+    static final class ClientState {
         final IInputMethodClient client;
         final IInputContext inputContext;
         final int uid;
@@ -555,18 +560,21 @@
         }
     }
 
-    private static class MethodCallback extends IInputSessionCallback.Stub {
-        private final IInputMethod mMethod;
+    private static final class MethodCallback extends IInputSessionCallback.Stub {
         private final InputMethodManagerService mParentIMMS;
+        private final IInputMethod mMethod;
+        private final InputChannel mChannel;
 
-        MethodCallback(final IInputMethod method, final InputMethodManagerService imms) {
-            mMethod = method;
+        MethodCallback(InputMethodManagerService imms, IInputMethod method,
+                InputChannel channel) {
             mParentIMMS = imms;
+            mMethod = method;
+            mChannel = channel;
         }
 
         @Override
-        public void sessionCreated(IInputMethodSession session) throws RemoteException {
-            mParentIMMS.onSessionCreated(mMethod, session);
+        public void sessionCreated(IInputMethodSession session) {
+            mParentIMMS.onSessionCreated(mMethod, session, mChannel);
         }
     }
 
@@ -984,7 +992,10 @@
             return;
         }
         synchronized (mMethodMap) {
-            mClients.remove(client.asBinder());
+            ClientState cs = mClients.remove(client.asBinder());
+            if (cs != null) {
+                clearClientSessionLocked(cs);
+            }
         }
     }
 
@@ -1059,7 +1070,7 @@
             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
             showCurrentInputLocked(getAppShowFlags(), null);
         }
-        return new InputBindResult(session.session, mCurId, mCurSeq);
+        return new InputBindResult(session.session, session.channel, mCurId, mCurSeq);
     }
 
     InputBindResult startInputLocked(IInputMethodClient client,
@@ -1137,16 +1148,10 @@
             }
             if (mHaveConnection) {
                 if (mCurMethod != null) {
-                    if (!cs.sessionRequested) {
-                        cs.sessionRequested = true;
-                        if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
-                        executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
-                                MSG_CREATE_SESSION, mCurMethod,
-                                new MethodCallback(mCurMethod, this)));
-                    }
                     // Return to client, and we will get back with it when
                     // we have had a session made for it.
-                    return new InputBindResult(null, mCurId, mCurSeq);
+                    requestClientSessionLocked(cs);
+                    return new InputBindResult(null, null, mCurId, mCurSeq);
                 } else if (SystemClock.uptimeMillis()
                         < (mLastBindTime+TIME_TO_RECONNECT)) {
                     // In this case we have connected to the service, but
@@ -1156,7 +1161,7 @@
                     // we can report back.  If it has been too long, we want
                     // to fall through so we can try a disconnect/reconnect
                     // to see if we can get back in touch with the service.
-                    return new InputBindResult(null, mCurId, mCurSeq);
+                    return new InputBindResult(null, null, mCurId, mCurSeq);
                 } else {
                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
                             mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -1175,7 +1180,7 @@
         if (!mSystemReady) {
             // If the system is not yet ready, we shouldn't be running third
             // party code.
-            return new InputBindResult(null, mCurMethodId, mCurSeq);
+            return new InputBindResult(null, null, mCurMethodId, mCurSeq);
         }
 
         InputMethodInfo info = mMethodMap.get(mCurMethodId);
@@ -1203,7 +1208,7 @@
                         WindowManager.LayoutParams.TYPE_INPUT_METHOD);
             } catch (RemoteException e) {
             }
-            return new InputBindResult(null, mCurId, mCurSeq);
+            return new InputBindResult(null, null, mCurId, mCurSeq);
         } else {
             mCurIntent = null;
             Slog.w(TAG, "Failure connecting to input method service: "
@@ -1246,32 +1251,34 @@
                 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                         MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
                 if (mCurClient != null) {
-                    if (DEBUG) Slog.v(TAG, "Creating first session while with client "
-                            + mCurClient);
-                    executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
-                            MSG_CREATE_SESSION, mCurMethod,
-                            new MethodCallback(mCurMethod, this)));
+                    clearClientSessionLocked(mCurClient);
+                    requestClientSessionLocked(mCurClient);
                 }
             }
         }
     }
 
-    void onSessionCreated(IInputMethod method, IInputMethodSession session) {
+    void onSessionCreated(IInputMethod method, IInputMethodSession session,
+            InputChannel channel) {
         synchronized (mMethodMap) {
             if (mCurMethod != null && method != null
                     && mCurMethod.asBinder() == method.asBinder()) {
                 if (mCurClient != null) {
+                    clearClientSessionLocked(mCurClient);
                     mCurClient.curSession = new SessionState(mCurClient,
-                            method, session);
-                    mCurClient.sessionRequested = false;
+                            method, session, channel);
                     InputBindResult res = attachNewInputLocked(true);
                     if (res.method != null) {
                         executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
                                 MSG_BIND_METHOD, mCurClient.client, res));
                     }
+                    return;
                 }
             }
         }
+
+        // Session abandoned.  Close its associated input channel.
+        channel.dispose();
     }
 
     void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) {
@@ -1306,14 +1313,38 @@
                     MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
         }
     }
-    
-    private void finishSession(SessionState sessionState) {
-        if (sessionState != null && sessionState.session != null) {
-            try {
-                sessionState.session.finishSession();
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Session failed to close due to remote exception", e);
-                setImeWindowVisibilityStatusHiddenLocked();
+
+    void requestClientSessionLocked(ClientState cs) {
+        if (!cs.sessionRequested) {
+            if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
+            InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
+            cs.sessionRequested = true;
+            executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
+                    MSG_CREATE_SESSION, mCurMethod, channels[1],
+                    new MethodCallback(this, mCurMethod, channels[0])));
+        }
+    }
+
+    void clearClientSessionLocked(ClientState cs) {
+        finishSessionLocked(cs.curSession);
+        cs.curSession = null;
+        cs.sessionRequested = false;
+    }
+
+    private void finishSessionLocked(SessionState sessionState) {
+        if (sessionState != null) {
+            if (sessionState.session != null) {
+                try {
+                    sessionState.session.finishSession();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Session failed to close due to remote exception", e);
+                    setImeWindowVisibilityStatusHiddenLocked();
+                }
+                sessionState.session = null;
+            }
+            if (sessionState.channel != null) {
+                sessionState.channel.dispose();
+                sessionState.channel = null;
             }
         }
     }
@@ -1321,12 +1352,10 @@
     void clearCurMethodLocked() {
         if (mCurMethod != null) {
             for (ClientState cs : mClients.values()) {
-                cs.sessionRequested = false;
-                finishSession(cs.curSession);
-                cs.curSession = null;
+                clearClientSessionLocked(cs);
             }
 
-            finishSession(mEnabledSession);
+            finishSessionLocked(mEnabledSession);
             mEnabledSession = null;
             mCurMethod = null;
         }
@@ -2325,15 +2354,21 @@
                 }
                 args.recycle();
                 return true;
-            case MSG_CREATE_SESSION:
+            case MSG_CREATE_SESSION: {
                 args = (SomeArgs)msg.obj;
+                InputChannel channel = (InputChannel)args.arg2;
                 try {
-                    ((IInputMethod)args.arg1).createSession(
-                            (IInputSessionCallback)args.arg2);
+                    ((IInputMethod)args.arg1).createSession(channel,
+                            (IInputSessionCallback)args.arg3);
                 } catch (RemoteException e) {
+                } finally {
+                    if (channel != null) {
+                        channel.dispose();
+                    }
                 }
                 args.recycle();
                 return true;
+            }
             // ---------------------------------------------------------
 
             case MSG_START_INPUT:
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c6aca2f..e8d7882 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1603,7 +1603,7 @@
             boolean mounted = false;
             try {
                 mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
-            } catch (IllegalStateException e) {
+            } catch (IllegalArgumentException e) {
             }
 
             if (!mounted) {
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 09daf56..c4b98ad 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -1425,9 +1425,9 @@
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         checkManageAccountsPermission();
 
-        // Is user allowed to modify accounts?
-        if (!getUserManager().getUserRestrictions(Binder.getCallingUserHandle())
-                .getBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS)) {
+        // Is user disallowed from modifying accounts?
+        if (getUserManager().getUserRestrictions(Binder.getCallingUserHandle())
+                .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                         "User is not allowed to add an account!");
@@ -2572,7 +2572,7 @@
         if (callingUid != android.os.Process.myUid()) {
             Bundle restrictions = getUserManager().getUserRestrictions(
                     new UserHandle(UserHandle.getUserId(callingUid)));
-            if (!restrictions.getBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS)) {
+            if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
                 return false;
             }
         }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6f092bf..88ef884 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.app.AppOpsManager;
+import android.appwidget.AppWidgetManager;
 import com.android.internal.R;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessStats;
@@ -2194,7 +2195,7 @@
             // the PID of the new process, or else throw a RuntimeException.
             Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                     app.processName, uid, uid, gids, debugFlags, mountExternal,
-                    app.info.targetSdkVersion, null, null);
+                    app.info.targetSdkVersion, app.info.seinfo, null);
 
             BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
             synchronized (bs) {
@@ -11791,6 +11792,32 @@
                             + callingPid + ", uid=" + callingUid;
                     Slog.w(TAG, msg);
                     throw new SecurityException(msg);
+                } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {
+                    // Special case for compatibility: we don't want apps to send this,
+                    // but historically it has not been protected and apps may be using it
+                    // to poke their own app widget.  So, instead of making it protected,
+                    // just limit it to the caller.
+                    if (callerApp == null) {
+                        String msg = "Permission Denial: not allowed to send broadcast "
+                                + intent.getAction() + " from unknown caller.";
+                        Slog.w(TAG, msg);
+                        throw new SecurityException(msg);
+                    } else if (intent.getComponent() != null) {
+                        // They are good enough to send to an explicit component...  verify
+                        // it is being sent to the calling app.
+                        if (!intent.getComponent().getPackageName().equals(
+                                callerApp.info.packageName)) {
+                            String msg = "Permission Denial: not allowed to send broadcast "
+                                    + intent.getAction() + " to "
+                                    + intent.getComponent().getPackageName() + " from "
+                                    + callerApp.info.packageName;
+                            Slog.w(TAG, msg);
+                            throw new SecurityException(msg);
+                        }
+                    } else {
+                        // Limit broadcast to their own package.
+                        intent.setPackage(callerApp.info.packageName);
+                    }
                 }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception", e);
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index e8e8f25..ac7eb89 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -29,7 +29,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -1080,6 +1079,9 @@
                 pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
                 pw.print("    ");
                 pw.println(r.intent.toShortString(false, true, true, false));
+                if (r.targetComp != null && r.targetComp != r.intent.getComponent()) {
+                    pw.print("    targetComp: "); pw.println(r.targetComp.toShortString());
+                }
                 Bundle bundle = r.intent.getExtras();
                 if (bundle != null) {
                     pw.print("    extras: "); pw.println(bundle.toString());
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index a98afb6..83cc0ea 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -38,6 +38,7 @@
  */
 class BroadcastRecord extends Binder {
     final Intent intent;    // the original intent that generated us
+    final ComponentName targetComp; // original component name set on the intent
     final ProcessRecord callerApp; // process that sent this
     final String callerPackage; // who sent this
     final int callingPid;   // the pid of who sent this
@@ -84,9 +85,12 @@
 
         pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
         pw.print(prefix); pw.println(intent.toInsecureString());
+        if (targetComp != null && targetComp != intent.getComponent()) {
+            pw.print(prefix); pw.print("  targetComp: "); pw.println(targetComp.toShortString());
+        }
         Bundle bundle = intent.getExtras();
         if (bundle != null) {
-            pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
+            pw.print(prefix); pw.print("  extras: "); pw.println(bundle.toString());
         }
         pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.print(" ");
                 pw.print(callerApp != null ? callerApp.toShortString() : "null");
@@ -174,6 +178,7 @@
             int _userId) {
         queue = _queue;
         intent = _intent;
+        targetComp = _intent.getComponent();
         callerApp = _callerApp;
         callerPackage = _callerPackage;
         callingPid = _callingPid;
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 02a2c1b..d9c85bf 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -188,7 +188,7 @@
         }
     }
 
-    public int install(String name, int uid, int gid) {
+    public int install(String name, int uid, int gid, String seinfo) {
         StringBuilder builder = new StringBuilder("install");
         builder.append(' ');
         builder.append(name);
@@ -196,6 +196,8 @@
         builder.append(uid);
         builder.append(' ');
         builder.append(gid);
+        builder.append(' ');
+        builder.append(seinfo != null ? seinfo : "!");
         return execute(builder.toString());
     }
 
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index f1af634..afdd294 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -362,6 +362,9 @@
     final HashMap<String, FeatureInfo> mAvailableFeatures =
             new HashMap<String, FeatureInfo>();
 
+    // If mac_permissions.xml was found for seinfo labeling.
+    boolean mFoundPolicyFile;
+
     // All available activities, for your resolving pleasure.
     final ActivityIntentResolver mActivities =
             new ActivityIntentResolver();
@@ -1029,8 +1032,11 @@
 
             readPermissions();
 
+            mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
+
             mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
                     mSdkVersion, mOnlyCore);
+
             long startTime = SystemClock.uptimeMillis();
 
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
@@ -3694,9 +3700,9 @@
         }
     }
 
-    private int createDataDirsLI(String packageName, int uid) {
+    private int createDataDirsLI(String packageName, int uid, String seinfo) {
         int[] users = sUserManager.getUserIds();
-        int res = mInstaller.install(packageName, uid, uid);
+        int res = mInstaller.install(packageName, uid, uid, seinfo);
         if (res < 0) {
             return res;
         }
@@ -4038,6 +4044,10 @@
                 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
 
+            if (mFoundPolicyFile) {
+                SELinuxMMAC.assignSeinfoValue(pkg);
+            }
+
             pkg.applicationInfo.uid = pkgSetting.appId;
             pkg.mExtras = pkgSetting;
 
@@ -4176,7 +4186,8 @@
                             recovered = true;
 
                             // And now re-install the app.
-                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
+                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
+                                                   pkg.applicationInfo.seinfo);
                             if (ret == -1) {
                                 // Ack should not happen!
                                 msg = prefix + pkg.packageName
@@ -4222,7 +4233,8 @@
                         Log.v(TAG, "Want this data dir: " + dataPath);
                 }
                 //invoke installer to do the actual installation
-                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
+                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
+                                           pkg.applicationInfo.seinfo);
                 if (ret < 0) {
                     // Error from installer
                     mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -5933,7 +5945,7 @@
                 null);
 
         final int uid = Binder.getCallingUid();
-        if (!isUserAllowed(UserHandle.getUserId(uid), UserManager.ALLOW_INSTALL_APPS)) {
+        if (isUserRestricted(UserHandle.getUserId(uid), UserManager.DISALLOW_INSTALL_APPS)) {
             try {
                 observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);
             } catch (RemoteException re) {
@@ -5981,7 +5993,7 @@
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     "installExistingPackage for user " + userId);
         }
-        if (!isUserAllowed(userId, UserManager.ALLOW_INSTALL_APPS)) {
+        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
             return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
         }
 
@@ -6015,13 +6027,13 @@
         return PackageManager.INSTALL_SUCCEEDED;
     }
 
-    private boolean isUserAllowed(int userId, String restrictionKey) {
+    private boolean isUserRestricted(int userId, String restrictionKey) {
         Bundle restrictions = sUserManager.getUserRestrictions(userId);
-        if (!restrictions.getBoolean(UserManager.ALLOW_INSTALL_APPS)) {
-            Log.w(TAG, "User does not have permission to: " + restrictionKey);
-            return false;
+        if (restrictions.getBoolean(restrictionKey, false)) {
+            Log.w(TAG, "User is restricted: " + restrictionKey);
+            return true;
         }
-        return true;
+        return false;
     }
 
     @Override
@@ -8418,7 +8430,7 @@
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     "deletePackage for user " + userId);
         }
-        if (!isUserAllowed(userId, UserManager.ALLOW_UNINSTALL_APPS)) {
+        if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {
             try {
                 observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_USER_RESTRICTED);
             } catch (RemoteException re) {
@@ -8464,7 +8476,8 @@
         IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
                 ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
         try {
-            if (dpm != null && dpm.packageHasActiveAdmins(packageName, userId)) {
+            if (dpm != null && (dpm.packageHasActiveAdmins(packageName, userId)
+                    || dpm.isDeviceOwner(packageName))) {
                 Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
                 return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
             }
diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java
new file mode 100644
index 0000000..15d2a5a
--- /dev/null
+++ b/services/java/com/android/server/pm/SELinuxMMAC.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
+import android.os.Environment;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+import java.util.HashMap;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Centralized access to SELinux MMAC (middleware MAC) implementation.
+ * {@hide}
+ */
+public final class SELinuxMMAC {
+
+    private static final String TAG = "SELinuxMMAC";
+
+    private static final boolean DEBUG_POLICY = false;
+    private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
+
+    // Signature seinfo values read from policy.
+    private static final HashMap<Signature, String> sSigSeinfo =
+        new HashMap<Signature, String>();
+
+    // Package name seinfo values read from policy.
+    private static final HashMap<String, String> sPackageSeinfo =
+        new HashMap<String, String>();
+
+    // Locations of potential install policy files.
+    private static final File[] INSTALL_POLICY_FILE = {
+        new File(Environment.getDataDirectory(), "system/mac_permissions.xml"),
+        new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
+        null};
+
+    private static void flushInstallPolicy() {
+        sSigSeinfo.clear();
+        sPackageSeinfo.clear();
+    }
+
+    /**
+     * Parses an MMAC install policy from a predefined list of locations.
+     * @param none
+     * @return boolean indicating whether an install policy was correctly parsed.
+     */
+    public static boolean readInstallPolicy() {
+
+        return readInstallPolicy(INSTALL_POLICY_FILE);
+    }
+
+    /**
+     * Parses an MMAC install policy given as an argument.
+     * @param File object representing the path of the policy.
+     * @return boolean indicating whether the install policy was correctly parsed.
+     */
+    public static boolean readInstallPolicy(File policyFile) {
+
+        return readInstallPolicy(new File[]{policyFile,null});
+    }
+
+    private static boolean readInstallPolicy(File[] policyFiles) {
+
+        FileReader policyFile = null;
+        int i = 0;
+        while (policyFile == null && policyFiles != null && policyFiles[i] != null) {
+            try {
+                policyFile = new FileReader(policyFiles[i]);
+                break;
+            } catch (FileNotFoundException e) {
+                Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());
+            }
+            i++;
+        }
+
+        if (policyFile == null) {
+            Slog.d(TAG, "No policy file found. All seinfo values will be null.");
+            return false;
+        }
+
+        Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
+
+        flushInstallPolicy();
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(policyFile);
+
+            XmlUtils.beginDocument(parser, "policy");
+            while (true) {
+                XmlUtils.nextElement(parser);
+                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+                    break;
+                }
+
+                String tagName = parser.getName();
+                if ("signer".equals(tagName)) {
+                    String cert = parser.getAttributeValue(null, "signature");
+                    if (cert == null) {
+                        Slog.w(TAG, "<signer> without signature at "
+                               + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    Signature signature;
+                    try {
+                        signature = new Signature(cert);
+                    } catch (IllegalArgumentException e) {
+                        Slog.w(TAG, "<signer> with bad signature at "
+                               + parser.getPositionDescription(), e);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<signer> tag: (" + cert + ") assigned seinfo="
+                                   + seinfo);
+
+                        sSigSeinfo.put(signature, seinfo);
+                    }
+                } else if ("default".equals(tagName)) {
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<default> tag assigned seinfo=" + seinfo);
+
+                        // The 'null' signature is the default seinfo value
+                        sSigSeinfo.put(null, seinfo);
+                    }
+                } else if ("package".equals(tagName)) {
+                    String pkgName = parser.getAttributeValue(null, "name");
+                    if (pkgName == null) {
+                        Slog.w(TAG, "<package> without name at "
+                               + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<package> tag: (" + pkgName +
+                                   ") assigned seinfo=" + seinfo);
+
+                        sPackageSeinfo.put(pkgName, seinfo);
+                    }
+                } else {
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                }
+            }
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Got execption parsing ", e);
+        } catch (IOException e) {
+            Slog.w(TAG, "Got execption parsing ", e);
+        }
+        try {
+            policyFile.close();
+        } catch (IOException e) {
+            //omit
+        }
+        return true;
+    }
+
+    private static String readSeinfoTag(XmlPullParser parser) throws
+            IOException, XmlPullParserException {
+
+        int type;
+        int outerDepth = parser.getDepth();
+        String seinfo = null;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                   || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if ("seinfo".equals(tagName)) {
+                String seinfoValue = parser.getAttributeValue(null, "value");
+                if (seinfoValue != null) {
+                    seinfo = seinfoValue;
+                } else {
+                    Slog.w(TAG, "<seinfo> without value at "
+                           + parser.getPositionDescription());
+                }
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+        return seinfo;
+    }
+
+    /**
+     * Labels a package based on an seinfo tag from install policy.
+     * The label is attached to the ApplicationInfo instance of the package.
+     * @param PackageParser.Package object representing the package
+     *         to labeled.
+     * @return String holding the value of the seinfo label that was assigned.
+     *         Value may be null which indicates no seinfo label was assigned.
+     */
+    public static void assignSeinfoValue(PackageParser.Package pkg) {
+
+        /*
+         * Non system installed apps should be treated the same. This
+         * means that any post-loaded apk will be assigned the default
+         * tag, if one exists in the policy, else null, without respect
+         * to the signing key.
+         */
+        if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
+            ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {
+
+            // We just want one of the signatures to match.
+            for (Signature s : pkg.mSignatures) {
+                if (s == null)
+                    continue;
+
+                if (sSigSeinfo.containsKey(s)) {
+                    String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);
+                    if (DEBUG_POLICY_INSTALL)
+                        Slog.i(TAG, "package (" + pkg.packageName +
+                               ") labeled with seinfo=" + seinfo);
+
+                    return;
+                }
+            }
+
+            // Check for seinfo labeled by package.
+            if (sPackageSeinfo.containsKey(pkg.packageName)) {
+                String seinfo = pkg.applicationInfo.seinfo = sPackageSeinfo.get(pkg.packageName);
+                if (DEBUG_POLICY_INSTALL)
+                    Slog.i(TAG, "package (" + pkg.packageName +
+                           ") labeled with seinfo=" + seinfo);
+                return;
+            }
+        }
+
+        // If we have a default seinfo value then great, otherwise
+        // we set a null object and that is what we started with.
+        String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null);
+        if (DEBUG_POLICY_INSTALL)
+            Slog.i(TAG, "package (" + pkg.packageName +
+                   ") labeled with seinfo=" + (seinfo == null ? "null" : seinfo));
+    }
+}
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 636b0e5..fecc2df 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -226,6 +226,13 @@
         }
     }
 
+    @Override
+    public boolean isRestricted() {
+        synchronized (mPackagesLock) {
+            return getUserInfoLocked(UserHandle.getCallingUserId()).isRestricted();
+        }
+    }
+
     /*
      * Should be locked on mUsers before calling this.
      */
@@ -558,7 +565,6 @@
         mNextSerialNumber = MIN_USER_ID;
 
         Bundle restrictions = new Bundle();
-        initRestrictionsToDefaults(restrictions);
         mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);
 
         updateUserIdsLocked();
@@ -608,11 +614,11 @@
             Bundle restrictions = mUserRestrictions.get(userInfo.id);
             if (restrictions != null) {
                 serializer.startTag(null, TAG_RESTRICTIONS);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_CONFIG_WIFI);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_MODIFY_ACCOUNTS);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_INSTALL_APPS);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_UNINSTALL_APPS);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_CONFIG_LOCATION_ACCESS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
                 serializer.endTag(null, TAG_RESTRICTIONS);
             }
             serializer.endTag(null, TAG_USER);
@@ -676,7 +682,6 @@
         long lastLoggedInTime = 0L;
         boolean partial = false;
         Bundle restrictions = new Bundle();
-        initRestrictionsToDefaults(restrictions);
 
         FileInputStream fis = null;
         try {
@@ -725,11 +730,11 @@
                             name = parser.getText();
                         }
                     } else if (TAG_RESTRICTIONS.equals(tag)) {
-                        readBoolean(parser, restrictions, UserManager.ALLOW_CONFIG_WIFI);
-                        readBoolean(parser, restrictions, UserManager.ALLOW_MODIFY_ACCOUNTS);
-                        readBoolean(parser, restrictions, UserManager.ALLOW_INSTALL_APPS);
-                        readBoolean(parser, restrictions, UserManager.ALLOW_UNINSTALL_APPS);
-                        readBoolean(parser, restrictions, UserManager.ALLOW_CONFIG_LOCATION_ACCESS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
                     }
                 }
             }
@@ -758,7 +763,9 @@
     private void readBoolean(XmlPullParser parser, Bundle restrictions,
             String restrictionKey) {
         String value = parser.getAttributeValue(null, restrictionKey);
-        restrictions.putBoolean(restrictionKey, value == null ? true : Boolean.parseBoolean(value));
+        if (value != null) {
+            restrictions.putBoolean(restrictionKey, Boolean.parseBoolean(value));
+        }
     }
 
     private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey)
@@ -769,14 +776,6 @@
         }
     }
 
-    private void initRestrictionsToDefaults(Bundle restrictions) {
-        restrictions.putBoolean(UserManager.ALLOW_CONFIG_WIFI, true);
-        restrictions.putBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS, true);
-        restrictions.putBoolean(UserManager.ALLOW_INSTALL_APPS, true);
-        restrictions.putBoolean(UserManager.ALLOW_UNINSTALL_APPS, true);
-        restrictions.putBoolean(UserManager.ALLOW_CONFIG_LOCATION_ACCESS, true);
-    }
-
     private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
         String valueString = parser.getAttributeValue(null, attr);
         if (valueString == null) return defaultValue;
@@ -823,7 +822,6 @@
                     writeUserLocked(userInfo);
                     updateUserIdsLocked();
                     Bundle restrictions = new Bundle();
-                    initRestrictionsToDefaults(restrictions);
                     mUserRestrictions.append(userId, restrictions);
                 }
             }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b46fb2f..34052f3 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -7291,6 +7291,7 @@
         return false;
     }
 
+    @Override
     public void getInitialDisplaySize(int displayId, Point size) {
         synchronized (mWindowMap) {
             final DisplayContent displayContent = getDisplayContentLocked(displayId);
@@ -7304,6 +7305,19 @@
     }
 
     @Override
+    public void getBaseDisplaySize(int displayId, Point size) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
+            if (displayContent != null) {
+                synchronized(displayContent.mDisplaySizeLock) {
+                    size.x = displayContent.mBaseDisplayWidth;
+                    size.y = displayContent.mBaseDisplayHeight;
+                }
+            }
+        }
+    }
+
+    @Override
     public void setForcedDisplaySize(int displayId, int width, int height) {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
@@ -7406,6 +7420,32 @@
     }
 
     @Override
+    public int getInitialDisplayDensity(int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
+            if (displayContent != null) {
+                synchronized(displayContent.mDisplaySizeLock) {
+                    return displayContent.mInitialDisplayDensity;
+                }
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public int getBaseDisplayDensity(int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
+            if (displayContent != null) {
+                synchronized(displayContent.mDisplaySizeLock) {
+                    return displayContent.mBaseDisplayDensity;
+                }
+            }
+        }
+        return -1;
+    }
+
+    @Override
     public void setForcedDisplayDensity(int displayId, int density) {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7ef1485..2e309be 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -66,8 +66,8 @@
                     && !user.isPrimary()) {
                 found = true;
                 Bundle restrictions = mUserManager.getUserRestrictions(user.getUserHandle());
-                assertTrue("New user should have ALLOW_CONFIG_WIFI =true by default",
-                        restrictions.getBoolean(UserManager.ALLOW_CONFIG_WIFI));
+                assertFalse("New user should have DISALLOW_CONFIG_WIFI =false by default",
+                        restrictions.getBoolean(UserManager.DISALLOW_CONFIG_WIFI));
             }
         }
         assertTrue(found);
@@ -147,13 +147,13 @@
         List<UserInfo> users = mUserManager.getUsers();
         if (users.size() > 1) {
             Bundle restrictions = new Bundle();
-            restrictions.putBoolean(UserManager.ALLOW_INSTALL_APPS, false);
-            restrictions.putBoolean(UserManager.ALLOW_CONFIG_WIFI, true);
+            restrictions.putBoolean(UserManager.DISALLOW_INSTALL_APPS, true);
+            restrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, false);
             mUserManager.setUserRestrictions(restrictions, new UserHandle(users.get(1).id));
             Bundle stored = mUserManager.getUserRestrictions(new UserHandle(users.get(1).id));
-            assertEquals(stored.getBoolean(UserManager.ALLOW_CONFIG_WIFI), true);
-            assertEquals(stored.getBoolean(UserManager.ALLOW_UNINSTALL_APPS), true);
-            assertEquals(stored.getBoolean(UserManager.ALLOW_INSTALL_APPS), false);
+            assertEquals(stored.getBoolean(UserManager.DISALLOW_CONFIG_WIFI), false);
+            assertEquals(stored.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS), false);
+            assertEquals(stored.getBoolean(UserManager.DISALLOW_INSTALL_APPS), true);
         }
     }
 
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 59ae1a1..6fbe32f 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.graphics.Point;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 
@@ -348,11 +349,31 @@
     }
 
     @Override
+    public void getInitialDisplaySize(int displayId, Point size) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void getBaseDisplaySize(int displayId, Point size) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
     public void setForcedDisplaySize(int displayId, int arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
     }
 
     @Override
+    public int getInitialDisplayDensity(int displayId) {
+        return -1;
+    }
+
+    @Override
+    public int getBaseDisplayDensity(int displayId) {
+        return -1;
+    }
+
+    @Override
     public void setForcedDisplayDensity(int displayId, int density) throws RemoteException {
         // TODO Auto-generated method stub
     }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index b57910f..c0a3bc1 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1677,12 +1677,12 @@
         setNetworkDetailedState(DetailedState.DISCONNECTED);
         mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
 
-        /* send event to CM & network change broadcast */
-        sendNetworkStateChangeBroadcast(mLastBssid);
-
         /* Clear network properties */
         mLinkProperties.clear();
 
+        /* send event to CM & network change broadcast */
+        sendNetworkStateChangeBroadcast(mLastBssid);
+
         /* Clear IP settings if the network used DHCP */
         if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
             mWifiConfigStore.clearLinkProperties(mLastNetworkId);