Merge "Remove blocking from SM before boot completed." into klp-dev
diff --git a/Android.mk b/Android.mk
index 13b717a..49dc6af 100644
--- a/Android.mk
+++ b/Android.mk
@@ -433,6 +433,7 @@
 
 non_base_dirs := \
 	../../external/apache-http/src/org/apache/http \
+	../opt/telephony/src/java/android/provider \
 	../opt/telephony/src/java/android/telephony \
 	../opt/telephony/src/java/android/telephony/gsm \
 	../opt/net/voip/src/java/android/net/rtp \
diff --git a/api/current.txt b/api/current.txt
index 77df600..ff72c4a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -33,6 +33,7 @@
     field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
     field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
     field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
+    field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
     field public static final java.lang.String BRICK = "android.permission.BRICK";
     field public static final java.lang.String BROADCAST_PACKAGE_REMOVED = "android.permission.BROADCAST_PACKAGE_REMOVED";
     field public static final java.lang.String BROADCAST_SMS = "android.permission.BROADCAST_SMS";
@@ -5103,6 +5104,7 @@
 
   public final class BluetoothDevice implements android.os.Parcelable {
     method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
+    method public boolean createBond();
     method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
     method public int describeContents();
@@ -5113,6 +5115,8 @@
     method public java.lang.String getName();
     method public int getType();
     method public android.os.ParcelUuid[] getUuids();
+    method public boolean setPairingConfirmation(boolean);
+    method public boolean setPin(byte[]);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
     field public static final java.lang.String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED";
@@ -5121,6 +5125,7 @@
     field public static final java.lang.String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED";
     field public static final java.lang.String ACTION_FOUND = "android.bluetooth.device.action.FOUND";
     field public static final java.lang.String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED";
+    field public static final java.lang.String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
     field public static final java.lang.String ACTION_UUID = "android.bluetooth.device.action.UUID";
     field public static final int BOND_BONDED = 12; // 0xc
     field public static final int BOND_BONDING = 11; // 0xb
@@ -5135,9 +5140,13 @@
     field public static final java.lang.String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
     field public static final java.lang.String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
     field public static final java.lang.String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
+    field public static final java.lang.String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
+    field public static final java.lang.String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT";
     field public static final java.lang.String EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
     field public static final java.lang.String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
     field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
+    field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2
+    field public static final int PAIRING_VARIANT_PIN = 0; // 0x0
   }
 
   public final class BluetoothGatt implements android.bluetooth.BluetoothProfile {
@@ -10904,7 +10913,6 @@
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void flush() throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
-    method public abstract android.hardware.camera2.CameraCharacteristics getProperties() throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
@@ -20989,7 +20997,6 @@
     field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
     field public static final java.lang.String COLUMN_SIZE = "_size";
     field public static final java.lang.String COLUMN_SUMMARY = "summary";
-    field public static final int FLAG_DIR_HIDE_GRID_TITLES = 64; // 0x40
     field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
     field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
     field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -21006,18 +21013,12 @@
     field public static final java.lang.String COLUMN_ICON = "icon";
     field public static final java.lang.String COLUMN_MIME_TYPES = "mime_types";
     field public static final java.lang.String COLUMN_ROOT_ID = "root_id";
-    field public static final java.lang.String COLUMN_ROOT_TYPE = "root_type";
     field public static final java.lang.String COLUMN_SUMMARY = "summary";
     field public static final java.lang.String COLUMN_TITLE = "title";
-    field public static final int FLAG_ADVANCED = 4; // 0x4
-    field public static final int FLAG_EMPTY = 32; // 0x20
     field public static final int FLAG_LOCAL_ONLY = 2; // 0x2
     field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
-    field public static final int FLAG_SUPPORTS_RECENTS = 8; // 0x8
-    field public static final int FLAG_SUPPORTS_SEARCH = 16; // 0x10
-    field public static final int ROOT_TYPE_DEVICE = 3; // 0x3
-    field public static final int ROOT_TYPE_SERVICE = 1; // 0x1
-    field public static final int ROOT_TYPE_SHORTCUT = 2; // 0x2
+    field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4
+    field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8
   }
 
   public abstract class DocumentsProvider extends android.content.ContentProvider {
@@ -21735,6 +21736,277 @@
     method public static void update(android.content.ContentProviderClient, android.net.Uri, byte[]) throws android.os.RemoteException;
   }
 
+  public final class Telephony {
+  }
+
+  public static abstract interface Telephony.BaseMmsColumns implements android.provider.BaseColumns {
+    field public static final java.lang.String CONTENT_CLASS = "ct_cls";
+    field public static final java.lang.String CONTENT_LOCATION = "ct_l";
+    field public static final java.lang.String CONTENT_TYPE = "ct_t";
+    field public static final java.lang.String DATE = "date";
+    field public static final java.lang.String DATE_SENT = "date_sent";
+    field public static final java.lang.String DELIVERY_REPORT = "d_rpt";
+    field public static final java.lang.String DELIVERY_TIME = "d_tm";
+    field public static final java.lang.String EXPIRY = "exp";
+    field public static final java.lang.String LOCKED = "locked";
+    field public static final java.lang.String MESSAGE_BOX = "msg_box";
+    field public static final int MESSAGE_BOX_ALL = 0; // 0x0
+    field public static final int MESSAGE_BOX_DRAFTS = 3; // 0x3
+    field public static final int MESSAGE_BOX_INBOX = 1; // 0x1
+    field public static final int MESSAGE_BOX_OUTBOX = 4; // 0x4
+    field public static final int MESSAGE_BOX_SENT = 2; // 0x2
+    field public static final java.lang.String MESSAGE_CLASS = "m_cls";
+    field public static final java.lang.String MESSAGE_ID = "m_id";
+    field public static final java.lang.String MESSAGE_SIZE = "m_size";
+    field public static final java.lang.String MESSAGE_TYPE = "m_type";
+    field public static final java.lang.String MMS_VERSION = "v";
+    field public static final java.lang.String PRIORITY = "pri";
+    field public static final java.lang.String READ = "read";
+    field public static final java.lang.String READ_REPORT = "rr";
+    field public static final java.lang.String READ_STATUS = "read_status";
+    field public static final java.lang.String REPORT_ALLOWED = "rpt_a";
+    field public static final java.lang.String RESPONSE_STATUS = "resp_st";
+    field public static final java.lang.String RESPONSE_TEXT = "resp_txt";
+    field public static final java.lang.String RETRIEVE_STATUS = "retr_st";
+    field public static final java.lang.String RETRIEVE_TEXT = "retr_txt";
+    field public static final java.lang.String RETRIEVE_TEXT_CHARSET = "retr_txt_cs";
+    field public static final java.lang.String SEEN = "seen";
+    field public static final java.lang.String STATUS = "st";
+    field public static final java.lang.String SUBJECT = "sub";
+    field public static final java.lang.String SUBJECT_CHARSET = "sub_cs";
+    field public static final java.lang.String TEXT_ONLY = "text_only";
+    field public static final java.lang.String THREAD_ID = "thread_id";
+    field public static final java.lang.String TRANSACTION_ID = "tr_id";
+  }
+
+  public static abstract interface Telephony.CanonicalAddressesColumns implements android.provider.BaseColumns {
+    field public static final java.lang.String ADDRESS = "address";
+  }
+
+  public static final class Telephony.Carriers implements android.provider.BaseColumns {
+    field public static final java.lang.String APN = "apn";
+    field public static final java.lang.String AUTH_TYPE = "authtype";
+    field public static final java.lang.String BEARER = "bearer";
+    field public static final java.lang.String CARRIER_ENABLED = "carrier_enabled";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String CURRENT = "current";
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "name ASC";
+    field public static final java.lang.String MCC = "mcc";
+    field public static final java.lang.String MMSC = "mmsc";
+    field public static final java.lang.String MMSPORT = "mmsport";
+    field public static final java.lang.String MMSPROXY = "mmsproxy";
+    field public static final java.lang.String MNC = "mnc";
+    field public static final java.lang.String MVNO_MATCH_DATA = "mvno_match_data";
+    field public static final java.lang.String MVNO_TYPE = "mvno_type";
+    field public static final java.lang.String NAME = "name";
+    field public static final java.lang.String NUMERIC = "numeric";
+    field public static final java.lang.String PASSWORD = "password";
+    field public static final java.lang.String PORT = "port";
+    field public static final java.lang.String PROTOCOL = "protocol";
+    field public static final java.lang.String PROXY = "proxy";
+    field public static final java.lang.String ROAMING_PROTOCOL = "roaming_protocol";
+    field public static final java.lang.String SERVER = "server";
+    field public static final java.lang.String TYPE = "type";
+    field public static final java.lang.String USER = "user";
+  }
+
+  public static final class Telephony.Mms implements android.provider.Telephony.BaseMmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+    field public static final android.net.Uri REPORT_REQUEST_URI;
+    field public static final android.net.Uri REPORT_STATUS_URI;
+  }
+
+  public static final class Telephony.Mms.Addr implements android.provider.BaseColumns {
+    field public static final java.lang.String ADDRESS = "address";
+    field public static final java.lang.String CHARSET = "charset";
+    field public static final java.lang.String CONTACT_ID = "contact_id";
+    field public static final java.lang.String MSG_ID = "msg_id";
+    field public static final java.lang.String TYPE = "type";
+  }
+
+  public static final class Telephony.Mms.Draft implements android.provider.Telephony.BaseMmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static final class Telephony.Mms.Inbox implements android.provider.Telephony.BaseMmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static final class Telephony.Mms.Intents {
+    field public static final java.lang.String CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED";
+    field public static final java.lang.String DELETED_CONTENTS = "deleted_contents";
+  }
+
+  public static final class Telephony.Mms.Outbox implements android.provider.Telephony.BaseMmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static final class Telephony.Mms.Part implements android.provider.BaseColumns {
+    field public static final java.lang.String CHARSET = "chset";
+    field public static final java.lang.String CONTENT_DISPOSITION = "cd";
+    field public static final java.lang.String CONTENT_ID = "cid";
+    field public static final java.lang.String CONTENT_LOCATION = "cl";
+    field public static final java.lang.String CONTENT_TYPE = "ct";
+    field public static final java.lang.String CT_START = "ctt_s";
+    field public static final java.lang.String CT_TYPE = "ctt_t";
+    field public static final java.lang.String FILENAME = "fn";
+    field public static final java.lang.String MSG_ID = "mid";
+    field public static final java.lang.String NAME = "name";
+    field public static final java.lang.String SEQ = "seq";
+    field public static final java.lang.String TEXT = "text";
+    field public static final java.lang.String _DATA = "_data";
+  }
+
+  public static final class Telephony.Mms.Rate {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String SENT_TIME = "sent_time";
+  }
+
+  public static final class Telephony.Mms.Sent implements android.provider.Telephony.BaseMmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static final class Telephony.MmsSms implements android.provider.BaseColumns {
+    field public static final android.net.Uri CONTENT_CONVERSATIONS_URI;
+    field public static final android.net.Uri CONTENT_DRAFT_URI;
+    field public static final android.net.Uri CONTENT_FILTER_BYPHONE_URI;
+    field public static final android.net.Uri CONTENT_LOCKED_URI;
+    field public static final android.net.Uri CONTENT_UNDELIVERED_URI;
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final int ERR_TYPE_GENERIC = 1; // 0x1
+    field public static final int ERR_TYPE_GENERIC_PERMANENT = 10; // 0xa
+    field public static final int ERR_TYPE_MMS_PROTO_PERMANENT = 12; // 0xc
+    field public static final int ERR_TYPE_MMS_PROTO_TRANSIENT = 3; // 0x3
+    field public static final int ERR_TYPE_SMS_PROTO_PERMANENT = 11; // 0xb
+    field public static final int ERR_TYPE_SMS_PROTO_TRANSIENT = 2; // 0x2
+    field public static final int ERR_TYPE_TRANSPORT_FAILURE = 4; // 0x4
+    field public static final int MMS_PROTO = 1; // 0x1
+    field public static final int NO_ERROR = 0; // 0x0
+    field public static final android.net.Uri SEARCH_URI;
+    field public static final int SMS_PROTO = 0; // 0x0
+    field public static final java.lang.String TYPE_DISCRIMINATOR_COLUMN = "transport_type";
+  }
+
+  public static final class Telephony.MmsSms.PendingMessages implements android.provider.BaseColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DUE_TIME = "due_time";
+    field public static final java.lang.String ERROR_CODE = "err_code";
+    field public static final java.lang.String ERROR_TYPE = "err_type";
+    field public static final java.lang.String LAST_TRY = "last_try";
+    field public static final java.lang.String MSG_ID = "msg_id";
+    field public static final java.lang.String MSG_TYPE = "msg_type";
+    field public static final java.lang.String PROTO_TYPE = "proto_type";
+    field public static final java.lang.String RETRY_INDEX = "retry_index";
+  }
+
+  public static final class Telephony.Sms implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
+    method public static java.lang.String getDefaultSmsPackage(android.content.Context);
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static final class Telephony.Sms.Conversations implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+    field public static final java.lang.String MESSAGE_COUNT = "msg_count";
+    field public static final java.lang.String SNIPPET = "snippet";
+  }
+
+  public static final class Telephony.Sms.Draft implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static final class Telephony.Sms.Inbox implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static final class Telephony.Sms.Intents {
+    method public static android.telephony.SmsMessage[] getMessagesFromIntent(android.content.Intent);
+    field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.provider.Telephony.ACTION_CHANGE_DEFAULT";
+    field public static final java.lang.String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
+    field public static final int RESULT_SMS_DUPLICATED = 5; // 0x5
+    field public static final int RESULT_SMS_GENERIC_ERROR = 2; // 0x2
+    field public static final int RESULT_SMS_HANDLED = 1; // 0x1
+    field public static final int RESULT_SMS_OUT_OF_MEMORY = 3; // 0x3
+    field public static final int RESULT_SMS_UNSUPPORTED = 4; // 0x4
+    field public static final java.lang.String SIM_FULL_ACTION = "android.provider.Telephony.SIM_FULL";
+    field public static final java.lang.String SMS_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_CB_RECEIVED";
+    field public static final java.lang.String SMS_DELIVER_ACTION = "android.provider.Telephony.SMS_DELIVER";
+    field public static final java.lang.String SMS_EMERGENCY_CB_RECEIVED_ACTION = "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED";
+    field public static final java.lang.String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
+    field public static final java.lang.String SMS_REJECTED_ACTION = "android.provider.Telephony.SMS_REJECTED";
+    field public static final java.lang.String SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION = "android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED";
+    field public static final java.lang.String WAP_PUSH_DELIVER_ACTION = "android.provider.Telephony.WAP_PUSH_DELIVER";
+    field public static final java.lang.String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";
+  }
+
+  public static final class Telephony.Sms.Outbox implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static final class Telephony.Sms.Sent implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
+  }
+
+  public static abstract interface Telephony.TextBasedSmsColumns {
+    field public static final java.lang.String ADDRESS = "address";
+    field public static final java.lang.String BODY = "body";
+    field public static final java.lang.String DATE = "date";
+    field public static final java.lang.String DATE_SENT = "date_sent";
+    field public static final java.lang.String ERROR_CODE = "error_code";
+    field public static final java.lang.String LOCKED = "locked";
+    field public static final int MESSAGE_TYPE_ALL = 0; // 0x0
+    field public static final int MESSAGE_TYPE_DRAFT = 3; // 0x3
+    field public static final int MESSAGE_TYPE_FAILED = 5; // 0x5
+    field public static final int MESSAGE_TYPE_INBOX = 1; // 0x1
+    field public static final int MESSAGE_TYPE_OUTBOX = 4; // 0x4
+    field public static final int MESSAGE_TYPE_QUEUED = 6; // 0x6
+    field public static final int MESSAGE_TYPE_SENT = 2; // 0x2
+    field public static final java.lang.String PERSON = "person";
+    field public static final java.lang.String PROTOCOL = "protocol";
+    field public static final java.lang.String READ = "read";
+    field public static final java.lang.String REPLY_PATH_PRESENT = "reply_path_present";
+    field public static final java.lang.String SEEN = "seen";
+    field public static final java.lang.String SERVICE_CENTER = "service_center";
+    field public static final java.lang.String STATUS = "status";
+    field public static final int STATUS_COMPLETE = 0; // 0x0
+    field public static final int STATUS_FAILED = 64; // 0x40
+    field public static final int STATUS_NONE = -1; // 0xffffffff
+    field public static final int STATUS_PENDING = 32; // 0x20
+    field public static final java.lang.String SUBJECT = "subject";
+    field public static final java.lang.String THREAD_ID = "thread_id";
+    field public static final java.lang.String TYPE = "type";
+  }
+
+  public static final class Telephony.Threads implements android.provider.Telephony.ThreadsColumns {
+    field public static final int BROADCAST_THREAD = 1; // 0x1
+    field public static final int COMMON_THREAD = 0; // 0x0
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final android.net.Uri OBSOLETE_THREADS_URI;
+  }
+
+  public static abstract interface Telephony.ThreadsColumns implements android.provider.BaseColumns {
+    field public static final java.lang.String DATE = "date";
+    field public static final java.lang.String ERROR = "error";
+    field public static final java.lang.String HAS_ATTACHMENT = "has_attachment";
+    field public static final java.lang.String MESSAGE_COUNT = "message_count";
+    field public static final java.lang.String READ = "read";
+    field public static final java.lang.String RECIPIENT_IDS = "recipient_ids";
+    field public static final java.lang.String SNIPPET = "snippet";
+    field public static final java.lang.String SNIPPET_CHARSET = "snippet_cs";
+    field public static final java.lang.String TYPE = "type";
+  }
+
   public class UserDictionary {
     ctor public UserDictionary();
     field public static final java.lang.String AUTHORITY = "user_dictionary";
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 2c85382..1efdc81 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -219,7 +219,7 @@
      * {@link #BOND_NONE},
      * {@link #BOND_BONDING},
      * {@link #BOND_BONDED}.
-      */
+     */
     public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
     /**
      * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
@@ -228,7 +228,7 @@
      * {@link #BOND_NONE},
      * {@link #BOND_BONDING},
      * {@link #BOND_BONDED}.
-      */
+     */
     public static final String EXTRA_PREVIOUS_BOND_STATE =
             "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
     /**
@@ -253,12 +253,26 @@
      */
     public static final int BOND_BONDED = 12;
 
-    /** @hide */
+    /**
+     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
+     * intents for unbond reason.
+     * @hide
+     */
     public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON";
-    /** @hide */
+
+    /**
+     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
+     * intents to indicate pairing method used. Possible values are:
+     * {@link #PAIRING_VARIANT_PIN},
+     * {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION},
+     */
     public static final String EXTRA_PAIRING_VARIANT =
             "android.bluetooth.device.extra.PAIRING_VARIANT";
-    /** @hide */
+
+    /**
+     * Used as an int extra field in {@link #ACTION_PAIRING_REQUEST}
+     * intents as the value of passkey.
+     */
     public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
 
     /**
@@ -306,7 +320,10 @@
     public static final String ACTION_NAME_FAILED =
             "android.bluetooth.device.action.NAME_FAILED";
 
-    /** @hide */
+    /**
+     * Broadcast Action: This intent is used to broadcast PAIRING REQUEST
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PAIRING_REQUEST =
             "android.bluetooth.device.action.PAIRING_REQUEST";
@@ -446,8 +463,8 @@
     public static final int UNBOND_REASON_REMOVED = 9;
 
     /**
-     * The user will be prompted to enter a pin
-     * @hide
+     * The user will be prompted to enter a pin or
+     * a privileged app will enter a pin for user.
      */
     public static final int PAIRING_VARIANT_PIN = 0;
 
@@ -458,8 +475,8 @@
     public static final int PAIRING_VARIANT_PASSKEY = 1;
 
     /**
-     * The user will be prompted to confirm the passkey displayed on the screen
-     * @hide
+     * The user will be prompted to confirm the passkey displayed on the screen or
+     * a privileged app will confirm the passkey for the user.
      */
     public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2;
 
@@ -707,10 +724,9 @@
      * the bonding process completes, and its result.
      * <p>Android system services will handle the necessary user interactions
      * to confirm and complete the bonding process.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
      *
      * @return false on immediate error, true if bonding will begin
-     * @hide
      */
     public boolean createBond() {
         if (sService == null) {
@@ -946,7 +962,13 @@
          return BluetoothDevice.ERROR;
     }
 
-    /** @hide */
+    /**
+     * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
+     *
+     * @return true pin has been set
+     *         false for error
+     */
     public boolean setPin(byte[] pin) {
         if (sService == null) {
             Log.e(TAG, "BT not enabled. Cannot set Remote Device pin");
@@ -968,7 +990,13 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * Confirm passkey for {@link #PAIRING_VARIANT_PASSKEY_CONFIRMATION} pairing.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
+     *
+     * @return true confirmation has been sent out
+     *         false for error
+     */
     public boolean setPairingConfirmation(boolean confirm) {
         if (sService == null) {
             Log.e(TAG, "BT not enabled. Cannot set pairing confirmation");
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ec89041..a9a72b0 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,11 +16,9 @@
 
 package android.hardware.camera2;
 
-import android.view.Surface;
 import android.os.Handler;
-import android.util.Log;
+import android.view.Surface;
 
-import java.lang.AutoCloseable;
 import java.util.List;
 
 /**
@@ -127,24 +125,11 @@
      * @return the ID for this camera device
      *
      * @see CameraManager#getCameraCharacteristics
-     * @see CameraManager#getDeviceIdList
+     * @see CameraManager#getCameraIdList
      */
     public String getId();
 
     /**
-     * Get the static properties for this camera. These are identical to the
-     * properties returned by {@link CameraManager#getCameraCharacteristics}.
-     *
-     * @return the static properties of the camera
-     *
-     * @throws CameraAccessException if the camera device is no longer connected or has
-     *                               encountered a fatal error
-     * @throws IllegalStateException if the camera device has been closed
-     *
-     * @see CameraManager#getCameraCharacteristics
-     */
-    public CameraCharacteristics getProperties() throws CameraAccessException;
-    /**
      * <p>Set up a new output set of Surfaces for the camera device.</p>
      *
      * <p>The configuration determines the set of potential output Surfaces for
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index f126472..70a6f44 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -88,24 +88,6 @@
     }
 
     @Override
-    public CameraCharacteristics getProperties() throws CameraAccessException {
-
-        CameraMetadataNative info = new CameraMetadataNative();
-
-        try {
-            mRemoteDevice.getCameraInfo(/*out*/info);
-        } catch(CameraRuntimeException e) {
-            throw e.asChecked();
-        } catch(RemoteException e) {
-            // impossible
-            return null;
-        }
-
-        CameraCharacteristics properties = new CameraCharacteristics(info);
-        return properties;
-    }
-
-    @Override
     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
         synchronized (mLock) {
             HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index 4044b31..3bd515b 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -45,6 +45,7 @@
     void removePrintJobStateChangeListener(in IPrintJobStateChangeListener listener,
             int userId);
 
+    List<PrintServiceInfo> getInstalledPrintServices(int userId);
     List<PrintServiceInfo> getEnabledPrintServices(int userId);
 
     void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId);
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 291e81f..96b168d 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -36,7 +36,6 @@
  */
 oneway interface IPrintSpooler {
     void removeObsoletePrintJobs();
-    void forgetPrintJobs(in List<PrintJobId> printJob);
     void getPrintJobInfos(IPrintSpoolerCallbacks callback, in ComponentName componentName,
             int state, int appId, int sequence);
     void getPrintJobInfo(in PrintJobId printJobId, IPrintSpoolerCallbacks callback,
diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl
index 0cf00cc..8270812 100644
--- a/core/java/android/print/IPrintSpoolerClient.aidl
+++ b/core/java/android/print/IPrintSpoolerClient.aidl
@@ -29,5 +29,5 @@
     void onPrintJobQueued(in PrintJobInfo printJob);
     void onAllPrintJobsForServiceHandled(in ComponentName printService);
     void onAllPrintJobsHandled();
-    void onPrintJobStateChanged(in PrintJobId printJobId, int appId);
+    void onPrintJobStateChanged(in PrintJobInfo printJob);
 }
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index a015388..0859fdd 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -289,7 +289,26 @@
                 return enabledServices;
             }
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error getting the enalbed print services", re);
+            Log.e(LOG_TAG, "Error getting the enabled print services", re);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Gets the list of installed print services.
+     *
+     * @return The installed service list or an empty list.
+     *
+     * @hide
+     */
+    public List<PrintServiceInfo> getInstalledPrintServices() {
+        try {
+            List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId);
+            if (installedServices != null) {
+                return installedServices;
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error getting the installed print services", re);
         }
         return Collections.emptyList();
     }
diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java
index 6432a37..d32b71b 100644
--- a/core/java/android/print/PrinterDiscoverySession.java
+++ b/core/java/android/print/PrinterDiscoverySession.java
@@ -28,6 +28,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
 
 /**
@@ -40,8 +41,8 @@
     private static final int MSG_PRINTERS_ADDED = 1;
     private static final int MSG_PRINTERS_REMOVED = 2;
 
-    private final ArrayMap<PrinterId, PrinterInfo> mPrinters =
-            new ArrayMap<PrinterId, PrinterInfo>();
+    private final LinkedHashMap<PrinterId, PrinterInfo> mPrinters =
+            new LinkedHashMap<PrinterId, PrinterInfo>();
 
     private final IPrintManager mPrintManager;
 
@@ -218,9 +219,7 @@
         }
 
         // Update printers we already have.
-        final int oldPrinterCount = mPrinters.size();
-        for (int i = 0; i < oldPrinterCount; i++) {
-            PrinterId oldPrinterId = mPrinters.keyAt(i);
+        for (PrinterId oldPrinterId : mPrinters.keySet()) {
             PrinterInfo updatedPrinter = addedPrintersMap.remove(oldPrinterId);
             if (updatedPrinter != null) {
                 mPrinters.put(oldPrinterId, updatedPrinter);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4c9af19..85ec803 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -99,7 +99,7 @@
         /**
          * Unique ID of a document. This ID is both provided by and interpreted
          * by a {@link DocumentsProvider}, and should be treated as an opaque
-         * value by client applications.
+         * value by client applications. This column is required.
          * <p>
          * Each document must have a unique ID within a provider, but that
          * single document may be included as a child of multiple directories.
@@ -117,7 +117,7 @@
          * Concrete MIME type of a document. For example, "image/png" or
          * "application/pdf" for openable files. A document can also be a
          * directory containing additional documents, which is represented with
-         * the {@link #MIME_TYPE_DIR} MIME type.
+         * the {@link #MIME_TYPE_DIR} MIME type. This column is required.
          * <p>
          * Type: STRING
          *
@@ -127,15 +127,15 @@
 
         /**
          * Display name of a document, used as the primary title displayed to a
-         * user.
+         * user. This column is required.
          * <p>
          * Type: STRING
          */
         public static final String COLUMN_DISPLAY_NAME = OpenableColumns.DISPLAY_NAME;
 
         /**
-         * Summary of a document, which may be shown to a user. The summary may
-         * be {@code null}.
+         * Summary of a document, which may be shown to a user. This column is
+         * optional, and may be {@code null}.
          * <p>
          * Type: STRING
          */
@@ -143,9 +143,9 @@
 
         /**
          * Timestamp when a document was last modified, in milliseconds since
-         * January 1, 1970 00:00:00.0 UTC, or {@code null} if unknown. A
-         * {@link DocumentsProvider} can update this field using events from
-         * {@link OnCloseListener} or other reliable
+         * January 1, 1970 00:00:00.0 UTC. This column is required, and may be
+         * {@code null} if unknown. A {@link DocumentsProvider} can update this
+         * field using events from {@link OnCloseListener} or other reliable
          * {@link ParcelFileDescriptor} transports.
          * <p>
          * Type: INTEGER (long)
@@ -155,15 +155,16 @@
         public static final String COLUMN_LAST_MODIFIED = "last_modified";
 
         /**
-         * Specific icon resource ID for a document, or {@code null} to use
-         * platform default icon based on {@link #COLUMN_MIME_TYPE}.
+         * Specific icon resource ID for a document. This column is optional,
+         * and may be {@code null} to use a platform-provided default icon based
+         * on {@link #COLUMN_MIME_TYPE}.
          * <p>
          * Type: INTEGER (int)
          */
         public static final String COLUMN_ICON = "icon";
 
         /**
-         * Flags that apply to a document.
+         * Flags that apply to a document. This column is required.
          * <p>
          * Type: INTEGER (int)
          *
@@ -171,12 +172,13 @@
          * @see #FLAG_SUPPORTS_DELETE
          * @see #FLAG_SUPPORTS_THUMBNAIL
          * @see #FLAG_DIR_PREFERS_GRID
-         * @see #FLAG_DIR_SUPPORTS_CREATE
+         * @see #FLAG_DIR_PREFERS_LAST_MODIFIED
          */
         public static final String COLUMN_FLAGS = "flags";
 
         /**
-         * Size of a document, in bytes, or {@code null} if unknown.
+         * Size of a document, in bytes, or {@code null} if unknown. This column
+         * is required.
          * <p>
          * Type: INTEGER (long)
          */
@@ -211,7 +213,7 @@
          * writability of a document may change over time, for example due to
          * remote access changes. This flag indicates that a document client can
          * expect {@link ContentResolver#openOutputStream(Uri)} to succeed.
-         *
+         * 
          * @see #COLUMN_FLAGS
          */
         public static final int FLAG_SUPPORTS_WRITE = 1 << 1;
@@ -265,8 +267,9 @@
          *
          * @see #COLUMN_FLAGS
          * @see #FLAG_DIR_PREFERS_GRID
+         * @hide
          */
-        public static final int FLAG_DIR_HIDE_GRID_TITLES = 1 << 6;
+        public static final int FLAG_DIR_HIDE_GRID_TITLES = 1 << 16;
     }
 
     /**
@@ -282,31 +285,17 @@
         /**
          * Unique ID of a root. This ID is both provided by and interpreted by a
          * {@link DocumentsProvider}, and should be treated as an opaque value
-         * by client applications.
+         * by client applications. This column is required.
          * <p>
          * Type: STRING
          */
         public static final String COLUMN_ROOT_ID = "root_id";
 
         /**
-         * Type of a root, used for clustering when presenting multiple roots to
-         * a user.
+         * Flags that apply to a root. This column is required.
          * <p>
          * Type: INTEGER (int)
          *
-         * @see #ROOT_TYPE_SERVICE
-         * @see #ROOT_TYPE_SHORTCUT
-         * @see #ROOT_TYPE_DEVICE
-         */
-        public static final String COLUMN_ROOT_TYPE = "root_type";
-
-        /**
-         * Flags that apply to a root.
-         * <p>
-         * Type: INTEGER (int)
-         *
-         * @see #FLAG_ADVANCED
-         * @see #FLAG_EMPTY
          * @see #FLAG_LOCAL_ONLY
          * @see #FLAG_SUPPORTS_CREATE
          * @see #FLAG_SUPPORTS_RECENTS
@@ -315,22 +304,23 @@
         public static final String COLUMN_FLAGS = "flags";
 
         /**
-         * Icon resource ID for a root.
+         * Icon resource ID for a root. This column is required.
          * <p>
          * Type: INTEGER (int)
          */
         public static final String COLUMN_ICON = "icon";
 
         /**
-         * Title for a root, which will be shown to a user.
+         * Title for a root, which will be shown to a user. This column is
+         * required.
          * <p>
          * Type: STRING
          */
         public static final String COLUMN_TITLE = "title";
 
         /**
-         * Summary for this root, which may be shown to a user. The summary may
-         * be {@code null}.
+         * Summary for this root, which may be shown to a user. This column is
+         * optional, and may be {@code null}.
          * <p>
          * Type: STRING
          */
@@ -338,7 +328,7 @@
 
         /**
          * Document which is a directory that represents the top directory of
-         * this root.
+         * this root. This column is required.
          * <p>
          * Type: STRING
          *
@@ -347,20 +337,20 @@
         public static final String COLUMN_DOCUMENT_ID = "document_id";
 
         /**
-         * Number of bytes available in this root, or {@code null} if unknown or
-         * unbounded.
+         * Number of bytes available in this root. This column is optional, and
+         * may be {@code null} if unknown or unbounded.
          * <p>
          * Type: INTEGER (long)
          */
         public static final String COLUMN_AVAILABLE_BYTES = "available_bytes";
 
         /**
-         * MIME types supported by this root, or {@code null} if the root
-         * supports all MIME types. Multiple MIME types can be separated by a
-         * newline. For example, a root supporting audio might use
-         * "audio/*\napplication/x-flac".
+         * MIME types supported by this root. This column is optional, and if
+         * {@code null} the root is assumed to support all MIME types. Multiple
+         * MIME types can be separated by a newline. For example, a root
+         * supporting audio might return "audio/*\napplication/x-flac".
          * <p>
-         * Type: String
+         * Type: STRING
          */
         public static final String COLUMN_MIME_TYPES = "mime_types";
 
@@ -368,29 +358,6 @@
         public static final String MIME_TYPE_ITEM = "vnd.android.document/root";
 
         /**
-         * Type of root that represents a storage service, such as a cloud-based
-         * service.
-         *
-         * @see #COLUMN_ROOT_TYPE
-         */
-        public static final int ROOT_TYPE_SERVICE = 1;
-
-        /**
-         * Type of root that represents a shortcut to content that may be
-         * available elsewhere through another storage root.
-         *
-         * @see #COLUMN_ROOT_TYPE
-         */
-        public static final int ROOT_TYPE_SHORTCUT = 2;
-
-        /**
-         * Type of root that represents a physical storage device.
-         *
-         * @see #COLUMN_ROOT_TYPE
-         */
-        public static final int ROOT_TYPE_DEVICE = 3;
-
-        /**
          * Flag indicating that at least one directory under this root supports
          * creating content. Roots with this flag will be shown when an
          * application interacts with {@link Intent#ACTION_CREATE_DOCUMENT}.
@@ -409,21 +376,13 @@
         public static final int FLAG_LOCAL_ONLY = 1 << 1;
 
         /**
-         * Flag indicating that this root should only be visible to advanced
-         * users.
-         *
-         * @see #COLUMN_FLAGS
-         */
-        public static final int FLAG_ADVANCED = 1 << 2;
-
-        /**
          * Flag indicating that this root can report recently modified
          * documents.
          *
          * @see #COLUMN_FLAGS
          * @see DocumentsContract#buildRecentDocumentsUri(String, String)
          */
-        public static final int FLAG_SUPPORTS_RECENTS = 1 << 3;
+        public static final int FLAG_SUPPORTS_RECENTS = 1 << 2;
 
         /**
          * Flag indicating that this root supports search.
@@ -432,19 +391,31 @@
          * @see DocumentsProvider#querySearchDocuments(String, String,
          *      String[])
          */
-        public static final int FLAG_SUPPORTS_SEARCH = 1 << 4;
+        public static final int FLAG_SUPPORTS_SEARCH = 1 << 3;
 
         /**
          * Flag indicating that this root is currently empty. This may be used
          * to hide the root when opening documents, but the root will still be
          * shown when creating documents and {@link #FLAG_SUPPORTS_CREATE} is
-         * also set.
+         * also set. If the value of this flag changes, such as when a root
+         * becomes non-empty, you must send a content changed notification for
+         * {@link DocumentsContract#buildRootsUri(String)}.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsProvider#querySearchDocuments(String, String,
-         *      String[])
+         * @see ContentResolver#notifyChange(Uri,
+         *      android.database.ContentObserver, boolean)
+         * @hide
          */
-        public static final int FLAG_EMPTY = 1 << 5;
+        public static final int FLAG_EMPTY = 1 << 16;
+
+        /**
+         * Flag indicating that this root should only be visible to advanced
+         * users.
+         *
+         * @see #COLUMN_FLAGS
+         * @hide
+         */
+        public static final int FLAG_ADVANCED = 1 << 17;
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6c6635d..b8cccb1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2797,7 +2797,6 @@
             MOVED_TO_GLOBAL.add(Settings.Global.USB_MASS_STORAGE_ENABLED);
             MOVED_TO_GLOBAL.add(Settings.Global.USE_GOOGLE_MAIL);
             MOVED_TO_GLOBAL.add(Settings.Global.WEB_AUTOFILL_QUERY_URL);
-            MOVED_TO_GLOBAL.add(Settings.Global.WIFI_COUNTRY_CODE);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_FREQUENCY_BAND);
             MOVED_TO_GLOBAL.add(Settings.Global.WIFI_IDLE_MS);
@@ -5208,12 +5207,6 @@
                "wifi_networks_available_repeat_delay";
 
        /**
-        * 802.11 country code in ISO 3166 format
-        * @hide
-        */
-       public static final String WIFI_COUNTRY_CODE = "wifi_country_code";
-
-       /**
         * The interval in milliseconds to issue wake up scans when wifi needs
         * to connect. This is necessary to connect to an access point when
         * device is on the move and the screen is off.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index aea2799c..caf9c8b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -249,12 +249,6 @@
     boolean isSafeModeEnabled();
 
     /**
-     * Tell keyguard to show the assistant (Intent.ACTION_ASSIST) after asking for the user's
-     * credentials.
-     */
-    void showAssistant();
-
-    /**
      * Sets the display magnification callbacks. These callbacks notify
      * the client for contextual changes related to display magnification.
      *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 38f28ae..28f7480 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4825,8 +4825,8 @@
 
             enqueueInputEvent(new KeyEvent(event.getDownTime(), event.getEventTime(),
                         event.getAction(), keyCode, event.getRepeatCount(), event.getMetaState(),
-                        event.getScanCode(), event.getFlags() | KeyEvent.FLAG_FALLBACK,
-                        event.getSource()));
+                        event.getDeviceId(), event.getScanCode(),
+                        event.getFlags() | KeyEvent.FLAG_FALLBACK, event.getSource()));
             return true;
         }
 
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index e116662..79c0b3c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1176,12 +1176,6 @@
     public void dump(String prefix, PrintWriter writer, String[] args);
 
     /**
-     * Ask keyguard to invoke the assist intent after dismissing keyguard
-     * {@link android.content.Intent#ACTION_ASSIST}
-     */
-    public void showAssistant();
-
-    /**
      * Returns whether a given window type can be magnified.
      *
      * @param windowType The window type.
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 41a8fd1..6f832cfd 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2231,12 +2231,10 @@
         View scrapView;
 
         scrapView = mRecycler.getTransientStateView(position);
-        if (scrapView != null) {
-            return scrapView;
+        if (scrapView == null) {
+            scrapView = mRecycler.getScrapView(position);
         }
 
-        scrapView = mRecycler.getScrapView(position);
-
         View child;
         if (scrapView != null) {
             child = mAdapter.getView(position, scrapView, this);
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index 6c4c39d..d4c5be0 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -26,9 +26,13 @@
 import android.view.ViewGroup;
 
 /**
- * Adapter that exposes data from a {@link android.database.Cursor Cursor} to a 
- * {@link android.widget.ListView ListView} widget. The Cursor must include 
- * a column named "_id" or this class will not work.
+ * Adapter that exposes data from a {@link android.database.Cursor Cursor} to a
+ * {@link android.widget.ListView ListView} widget.
+ * <p>
+ * The Cursor must include a column named "_id" or this class will not work.
+ * Additionally, using {@link android.database.MergeCursor} with this class will
+ * not work if the merged Cursors have overlapping values in their "_id"
+ * columns.
  */
 public abstract class CursorAdapter extends BaseAdapter implements Filterable,
         CursorFilter.CursorFilterClient {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 97cb815..61e071b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4679,8 +4679,6 @@
             assumeLayout();
         }
 
-        boolean changed = false;
-
         if (mMovement != null) {
             /* This code also provides auto-scrolling when a cursor is moved using a
              * CursorController (insertion point or selection limits).
@@ -4703,10 +4701,10 @@
             }
 
             if (curs >= 0) {
-                changed = bringPointIntoView(curs);
+                bringPointIntoView(curs);
             }
         } else {
-            changed = bringTextIntoView();
+            bringTextIntoView();
         }
 
         // This has to be checked here since:
@@ -4727,7 +4725,7 @@
         getViewTreeObserver().removeOnPreDrawListener(this);
         mPreDrawRegistered = false;
 
-        return !changed;
+        return true;
     }
 
     @Override
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 009b729..fbdf318 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -33,6 +33,7 @@
 import android.media.SubtitleTrack.RenderingWidget;
 import android.media.WebVttRenderer;
 import android.net.Uri;
+import android.os.Looper;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
@@ -879,4 +880,10 @@
 
         invalidate();
     }
+
+    /** @hide */
+    @Override
+    public Looper getSubtitleLooper() {
+        return Looper.getMainLooper();
+    }
 }
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index a281f7c..a95bac8 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -47,6 +47,10 @@
 
     public static final String SERVICE_NAME = "procstats";
 
+    // How often the service commits its data, giving the minimum batching
+    // that is done.
+    public static long COMMIT_PERIOD = 3*60*60*1000;  // Commit current stats every 3 hours
+
     public static final int STATE_NOTHING = -1;
     public static final int STATE_PERSISTENT = 0;
     public static final int STATE_TOP = 1;
@@ -1015,7 +1019,7 @@
                 pkgMap.removeAt(ip);
             }
         }
-        mStartTime = SystemClock.uptimeMillis();
+        mStartTime = now;
         if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
     }
 
@@ -1449,7 +1453,7 @@
                     mReadError = "bad uid: " + uid;
                     return;
                 }
-                PackageState pkgState = new PackageState(uid);
+                PackageState pkgState = new PackageState(pkgName, uid);
                 mPackages.put(pkgName, uid, pkgState);
                 int NPROCS = in.readInt();
                 if (NPROCS < 0) {
@@ -1620,7 +1624,7 @@
         if (as != null) {
             return as;
         }
-        as = new PackageState(uid);
+        as = new PackageState(packageName, uid);
         mPackages.put(packageName, uid, as);
         return as;
     }
@@ -1646,11 +1650,22 @@
                 // but it was created for a different package than the caller.
                 // We need to convert it to a multi-package process.
                 commonProc.mMultiPackage = true;
-                // The original package it was created for now needs to point
-                // to its own copy.
+                // To do this, we need to make two new process states, one a copy
+                // of the current state for the process under the original package
+                // name, and the second a free new process state for it as the
+                // new package name.
                 long now = SystemClock.uptimeMillis();
-                pkgState.mProcesses.put(commonProc.mName, commonProc.clone(
-                        commonProc.mPackage, now));
+                // First let's make a copy of the current process state and put
+                // that under the now unique state for its original package name.
+                final PackageState commonPkgState = getPackageStateLocked(commonProc.mPackage, uid);
+                if (commonPkgState != null) {
+                    commonPkgState.mProcesses.put(commonProc.mName, commonProc.clone(
+                            commonProc.mPackage, now));
+                } else {
+                    Slog.w(TAG, "Cloning proc state: no package state " + commonProc.mPackage
+                            + "/" + uid + " for proc " + commonProc.mName);
+                }
+                // And now make a fresh new process state for the new package name.
                 ps = new ProcessState(commonProc, packageName, uid, processName, now);
             }
         } else {
@@ -1677,6 +1692,32 @@
         return ss;
     }
 
+    private void dumpProcessInternalLocked(PrintWriter pw, String prefix, ProcessState proc,
+            boolean dumpAll) {
+        if (dumpAll) {
+            pw.print(prefix); pw.print("myID=");
+                    pw.print(Integer.toHexString(System.identityHashCode(proc)));
+                    pw.print(" mCommonProcess=");
+                    pw.print(Integer.toHexString(System.identityHashCode(proc.mCommonProcess)));
+                    pw.print(" mPackage="); pw.println(proc.mPackage);
+            if (proc.mMultiPackage) {
+                pw.print(prefix); pw.print("mMultiPackage="); pw.println(proc.mMultiPackage);
+            }
+            if (proc != proc.mCommonProcess) {
+                pw.print(prefix); pw.print("Common Proc: "); pw.print(proc.mCommonProcess.mName);
+                        pw.print("/"); pw.print(proc.mCommonProcess.mUid);
+                        pw.print(" pkg="); pw.println(proc.mCommonProcess.mPackage);
+            }
+        }
+        pw.print(prefix); pw.print("mActive="); pw.println(proc.mActive);
+        if (proc.mDead) {
+            pw.print(prefix); pw.print("mDead="); pw.println(proc.mDead);
+        }
+        pw.print(prefix); pw.print("mNumActiveServices="); pw.print(proc.mNumActiveServices);
+                pw.print(" mNumStartedServices=");
+                pw.println(proc.mNumStartedServices);
+    }
+
     public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
             boolean dumpAll) {
         long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
@@ -1715,10 +1756,7 @@
                                 ALL_PROC_STATES, now);
                         dumpProcessPss(pw, "        ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
                                 ALL_PROC_STATES);
-                        pw.print("        mActive="); pw.println(proc.mActive);
-                        pw.print("        mNumActiveServices="); pw.print(proc.mNumActiveServices);
-                                pw.print(" mNumStartedServices=");
-                                pw.println(proc.mNumStartedServices);
+                        dumpProcessInternalLocked(pw, "        ", proc, dumpAll);
                     }
                 } else {
                     ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
@@ -1737,6 +1775,7 @@
                     pw.print(pkgState.mServices.keyAt(isvc));
                     pw.println(":");
                     ServiceState svc = pkgState.mServices.valueAt(isvc);
+                    pw.print("        Process: "); pw.println(svc.mProcessName);
                     dumpServiceStats(pw, "        ", "          ", "    ", "Running", svc,
                             svc.mRunCount, ServiceState.SERVICE_RUN, svc.mRunState,
                             svc.mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
@@ -1759,16 +1798,19 @@
         if (reqPackage == null) {
             ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
             printedHeader = false;
+            int numShownProcs = 0, numTotalProcs = 0;
             for (int ip=0; ip<procMap.size(); ip++) {
                 String procName = procMap.keyAt(ip);
                 SparseArray<ProcessState> uids = procMap.valueAt(ip);
                 for (int iu=0; iu<uids.size(); iu++) {
                     int uid = uids.keyAt(iu);
+                    numTotalProcs++;
                     ProcessState proc = uids.valueAt(iu);
                     if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING
                             && proc.mPssTableSize == 0) {
                         continue;
                     }
+                    numShownProcs++;
                     if (!printedHeader) {
                         pw.println();
                         pw.println("Per-Process Stats:");
@@ -1783,13 +1825,15 @@
                     dumpProcessPss(pw, "        ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
                             ALL_PROC_STATES);
                     if (dumpAll) {
-                        pw.print("        mActive="); pw.println(proc.mActive);
-                        pw.print("        mNumActiveServices="); pw.print(proc.mNumActiveServices);
-                                pw.print(" mNumStartedServices=");
-                                pw.println(proc.mNumStartedServices);
+                        dumpProcessInternalLocked(pw, "        ", proc, dumpAll);
                     }
                 }
             }
+            if (dumpAll) {
+                pw.println();
+                pw.print("  Total procs: "); pw.print(numShownProcs);
+                        pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
+            }
 
             pw.println();
             if (dumpSummary) {
@@ -1876,7 +1920,7 @@
         long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
                 mStartTime, now);
         dumpFilteredSummaryLocked(pw, null, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
-                NON_CACHED_PROC_STATES, now, totalTime, reqPackage);
+                ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage);
         pw.println();
         dumpTotalsLocked(pw, now);
     }
@@ -1916,22 +1960,22 @@
     }
 
     void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
-            int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime,
-            String reqPackage) {
+            int[] screenStates, int[] memStates, int[] procStates,
+            int[] sortProcStates, long now, long totalTime, String reqPackage) {
         ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
-                procStates, now, reqPackage);
+                procStates, sortProcStates, now, reqPackage);
         if (procs.size() > 0) {
             if (header != null) {
                 pw.println();
                 pw.println(header);
             }
-            dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, procStates,
-                    now, totalTime);
+            dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates,
+                    sortProcStates, now, totalTime);
         }
     }
 
     public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
-            int[] procStates, long now, String reqPackage) {
+            int[] procStates, int sortProcStates[], long now, String reqPackage) {
         ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
         ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
         for (int ip=0; ip<pkgMap.size(); ip++) {
@@ -1953,6 +1997,9 @@
             if (computeProcessTimeLocked(proc, screenStates, memStates,
                     procStates, now) > 0) {
                 outProcs.add(proc);
+                if (procStates != sortProcStates) {
+                    computeProcessTimeLocked(proc, screenStates, memStates, sortProcStates, now);
+                }
             }
         }
         Collections.sort(outProcs, new Comparator<ProcessState>() {
@@ -2134,15 +2181,97 @@
         pw.println();
     }
 
-    public static final class ProcessState {
+    public static class DurationsTable {
         public final ProcessStats mStats;
+        public final String mName;
+        public int[] mDurationsTable;
+        public int mDurationsTableSize;
+
+        public DurationsTable(ProcessStats stats, String name) {
+            mStats = stats;
+            mName = name;
+        }
+
+        void copyDurationsTo(DurationsTable other) {
+            if (mDurationsTable != null) {
+                mStats.mAddLongTable = new int[mDurationsTable.length];
+                mStats.mAddLongTableSize = 0;
+                for (int i=0; i<mDurationsTableSize; i++) {
+                    int origEnt = mDurationsTable[i];
+                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                    int newOff = mStats.addLongData(i, type, 1);
+                    mStats.mAddLongTable[i] = newOff | type;
+                    mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0));
+                }
+                other.mDurationsTable = mStats.mAddLongTable;
+                other.mDurationsTableSize = mStats.mAddLongTableSize;
+            } else {
+                other.mDurationsTable = null;
+                other.mDurationsTableSize = 0;
+            }
+        }
+
+        void addDurations(DurationsTable other) {
+            for (int i=0; i<other.mDurationsTableSize; i++) {
+                int ent = other.mDurationsTable[i];
+                int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration "
+                        + other.mStats.getLong(ent, 0));
+                addDuration(state, other.mStats.getLong(ent, 0));
+            }
+        }
+
+        void resetDurationsSafely() {
+            mDurationsTable = null;
+            mDurationsTableSize = 0;
+        }
+
+        void writeDurationsToParcel(Parcel out) {
+            out.writeInt(mDurationsTableSize);
+            for (int i=0; i<mDurationsTableSize; i++) {
+                if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": "
+                        + printLongOffset(mDurationsTable[i]));
+                out.writeInt(mDurationsTable[i]);
+            }
+        }
+
+        boolean readDurationsFromParcel(Parcel in) {
+            mDurationsTable = mStats.readTableFromParcel(in, mName, "durations");
+            if (mDurationsTable == BAD_TABLE) {
+                return false;
+            }
+            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
+            return true;
+        }
+
+        void addDuration(int state, long dur) {
+            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
+            int off;
+            if (idx >= 0) {
+                off = mDurationsTable[idx];
+            } else {
+                mStats.mAddLongTable = mDurationsTable;
+                mStats.mAddLongTableSize = mDurationsTableSize;
+                off = mStats.addLongData(~idx, state, 1);
+                mDurationsTable = mStats.mAddLongTable;
+                mDurationsTableSize = mStats.mAddLongTableSize;
+            }
+            long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+            if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur
+                    + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]);
+            longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
+        }
+
+        long getDuration(int state, long now) {
+            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
+            return idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
+        }
+    }
+
+    public static final class ProcessState extends DurationsTable {
         public final ProcessState mCommonProcess;
         public final String mPackage;
         public final int mUid;
-        public final String mName;
-
-        int[] mDurationsTable;
-        int mDurationsTableSize;
 
         //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
         int mCurState = STATE_NOTHING;
@@ -2175,11 +2304,10 @@
          * a single package running in a process.  The initial state is not running.
          */
         public ProcessState(ProcessStats processStats, String pkg, int uid, String name) {
-            mStats = processStats;
+            super(processStats, name);
             mCommonProcess = this;
             mPackage = pkg;
             mUid = uid;
-            mName = name;
         }
 
         /**
@@ -2189,30 +2317,17 @@
          */
         public ProcessState(ProcessState commonProcess, String pkg, int uid, String name,
                 long now) {
-            mStats = commonProcess.mStats;
+            super(commonProcess.mStats, name);
             mCommonProcess = commonProcess;
             mPackage = pkg;
             mUid = uid;
-            mName = name;
             mCurState = commonProcess.mCurState;
             mStartTime = now;
         }
 
         ProcessState clone(String pkg, long now) {
             ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now);
-            if (mDurationsTable != null) {
-                mStats.mAddLongTable = new int[mDurationsTable.length];
-                mStats.mAddLongTableSize = 0;
-                for (int i=0; i<mDurationsTableSize; i++) {
-                    int origEnt = mDurationsTable[i];
-                    int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                    int newOff = mStats.addLongData(i, type, 1);
-                    mStats.mAddLongTable[i] = newOff | type;
-                    mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0));
-                }
-                pnew.mDurationsTable = mStats.mAddLongTable;
-                pnew.mDurationsTableSize = mStats.mAddLongTableSize;
-            }
+            copyDurationsTo(pnew);
             if (mPssTable != null) {
                 mStats.mAddLongTable = new int[mPssTable.length];
                 mStats.mAddLongTableSize = 0;
@@ -2240,13 +2355,7 @@
         }
 
         void add(ProcessState other) {
-            for (int i=0; i<other.mDurationsTableSize; i++) {
-                int ent = other.mDurationsTable[i];
-                int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration "
-                        + other.mStats.getLong(ent, 0));
-                addDuration(state, other.mStats.getLong(ent, 0));
-            }
+            addDurations(other);
             for (int i=0; i<other.mPssTableSize; i++) {
                 int ent = other.mPssTable[i];
                 int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
@@ -2267,8 +2376,7 @@
         }
 
         void resetSafely(long now) {
-            mDurationsTable = null;
-            mDurationsTableSize = 0;
+            resetDurationsSafely();
             mStartTime = now;
             mLastPssState = STATE_NOTHING;
             mLastPssTime = 0;
@@ -2294,12 +2402,7 @@
 
         void writeToParcel(Parcel out, long now) {
             out.writeInt(mMultiPackage ? 1 : 0);
-            out.writeInt(mDurationsTableSize);
-            for (int i=0; i<mDurationsTableSize; i++) {
-                if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": "
-                        + printLongOffset(mDurationsTable[i]));
-                out.writeInt(mDurationsTable[i]);
-            }
+            writeDurationsToParcel(out);
             out.writeInt(mPssTableSize);
             for (int i=0; i<mPssTableSize; i++) {
                 if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": "
@@ -2322,11 +2425,9 @@
                 mMultiPackage = multiPackage;
             }
             if (DEBUG) Slog.d(TAG, "Reading durations table...");
-            mDurationsTable = mStats.readTableFromParcel(in, mName, "durations");
-            if (mDurationsTable == BAD_TABLE) {
+            if (!readDurationsFromParcel(in)) {
                 return false;
             }
-            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
             if (DEBUG) Slog.d(TAG, "Reading pss table...");
             mPssTable = mStats.readTableFromParcel(in, mName, "pss");
             if (mPssTable == BAD_TABLE) {
@@ -2411,24 +2512,6 @@
             mStartTime = now;
         }
 
-        void addDuration(int state, long dur) {
-            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-            int off;
-            if (idx >= 0) {
-                off = mDurationsTable[idx];
-            } else {
-                mStats.mAddLongTable = mDurationsTable;
-                mStats.mAddLongTableSize = mDurationsTableSize;
-                off = mStats.addLongData(~idx, state, 1);
-                mDurationsTable = mStats.mAddLongTable;
-                mDurationsTableSize = mStats.mAddLongTableSize;
-            }
-            long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
-            if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur
-                    + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]);
-            longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
-        }
-
         void incActiveServices() {
             if (mCommonProcess != this) {
                 mCommonProcess.incActiveServices();
@@ -2470,7 +2553,8 @@
             }
         }
 
-        public void addPss(long pss, long uss, boolean always) {
+        public void addPss(long pss, long uss, boolean always,
+                ArrayMap<String, ProcessState> pkgList) {
             ensureNotDead();
             if (!always) {
                 if (mLastPssState == mCurState && SystemClock.uptimeMillis()
@@ -2481,7 +2565,20 @@
             mLastPssState = mCurState;
             mLastPssTime = SystemClock.uptimeMillis();
             if (mCurState != STATE_NOTHING) {
-                addPss(mCurState, 1, pss, pss, pss, uss, uss, uss);
+                // First update the common process.
+                mCommonProcess.addPss(mCurState, 1, pss, pss, pss, uss, uss, uss);
+
+                // If the common process is not multi-package, there is nothing else to do.
+                if (!mCommonProcess.mMultiPackage) {
+                    return;
+                }
+
+                if (pkgList != null) {
+                    for (int ip=pkgList.size()-1; ip>=0; ip--) {
+                        pullFixedProc(pkgList, ip).addPss(mCurState, 1,
+                                pss, pss, pss, uss, uss, uss);
+                    }
+                }
             }
         }
 
@@ -2632,8 +2729,7 @@
         }
 
         long getDuration(int state, long now) {
-            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-            long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
+            long time = super.getDuration(state, now);
             if (mCurState == state) {
                 time += now - mStartTime;
             }
@@ -2676,10 +2772,8 @@
         }
     }
 
-    public static final class ServiceState {
-        final ProcessStats mStats;
+    public static final class ServiceState extends DurationsTable {
         public final String mPackage;
-        public final String mName;
         public final String mProcessName;
         ProcessState mProc;
 
@@ -2691,9 +2785,6 @@
         public static final int SERVICE_EXEC = 3;
         static final int SERVICE_COUNT = 4;
 
-        int[] mDurationsTable;
-        int mDurationsTableSize;
-
         int mRunCount;
         public int mRunState = STATE_NOTHING;
         long mRunStartTime;
@@ -2712,9 +2803,8 @@
 
         public ServiceState(ProcessStats processStats, String pkg, String name,
                 String processName, ProcessState proc) {
-            mStats = processStats;
+            super(processStats, name);
             mPackage = pkg;
-            mName = name;
             mProcessName = processName;
             mProc = proc;
         }
@@ -2743,11 +2833,7 @@
         }
 
         void add(ServiceState other) {
-            for (int i=0; i<other.mDurationsTableSize; i++) {
-                int ent = other.mDurationsTable[i];
-                int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
-                addStateTime(state, other.mStats.getLong(ent, 0));
-            }
+            addDurations(other);
             mRunCount += other.mRunCount;
             mStartedCount += other.mStartedCount;
             mBoundCount += other.mBoundCount;
@@ -2755,22 +2841,16 @@
         }
 
         void resetSafely(long now) {
-            mDurationsTable = null;
-            mDurationsTableSize = 0;
+            resetDurationsSafely();
             mRunCount = mRunState != STATE_NOTHING ? 1 : 0;
             mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
             mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
             mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
-            mStartedStartTime = mBoundStartTime = mExecStartTime = now;
+            mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now;
         }
 
         void writeToParcel(Parcel out, long now) {
-            out.writeInt(mDurationsTableSize);
-            for (int i=0; i<mDurationsTableSize; i++) {
-                if (DEBUG) Slog.i(TAG, "Writing service in " + mPackage + " dur #" + i + ": "
-                        + printLongOffset(mDurationsTable[i]));
-                out.writeInt(mDurationsTable[i]);
-            }
+            writeDurationsToParcel(out);
             out.writeInt(mRunCount);
             out.writeInt(mStartedCount);
             out.writeInt(mBoundCount);
@@ -2778,12 +2858,9 @@
         }
 
         boolean readFromParcel(Parcel in) {
-            if (DEBUG) Slog.d(TAG, "Reading durations table...");
-            mDurationsTable = mStats.readTableFromParcel(in, mPackage, "service");
-            if (mDurationsTable == BAD_TABLE) {
+            if (!readDurationsFromParcel(in)) {
                 return false;
             }
-            mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
             mRunCount = in.readInt();
             mStartedCount = in.readInt();
             mBoundCount = in.readInt();
@@ -2791,40 +2868,22 @@
             return true;
         }
 
-        void addStateTime(int state, long time) {
-            if (time > 0) {
-                int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-                int off;
-                if (idx >= 0) {
-                    off = mDurationsTable[idx];
-                } else {
-                    mStats.mAddLongTable = mDurationsTable;
-                    mStats.mAddLongTableSize = mDurationsTableSize;
-                    off = mStats.addLongData(~idx, state, 1);
-                    mDurationsTable = mStats.mAddLongTable;
-                    mDurationsTableSize = mStats.mAddLongTableSize;
-                }
-                long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
-                longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += time;
-            }
-        }
-
         void commitStateTime(long now) {
             if (mRunState != STATE_NOTHING) {
-                addStateTime(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime);
+                addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime);
                 mRunStartTime = now;
             }
             if (mStartedState != STATE_NOTHING) {
-                addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
+                addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
                         now - mStartedStartTime);
                 mStartedStartTime = now;
             }
             if (mBoundState != STATE_NOTHING) {
-                addStateTime(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime);
+                addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime);
                 mBoundStartTime = now;
             }
             if (mExecState != STATE_NOTHING) {
-                addStateTime(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
+                addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
                 mExecStartTime = now;
             }
         }
@@ -2834,7 +2893,7 @@
                     || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
             if (mRunState != state) {
                 if (mRunState != STATE_NOTHING) {
-                    addStateTime(SERVICE_RUN + (mRunState*SERVICE_COUNT),
+                    addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
                             now - mRunStartTime);
                 } else if (state != STATE_NOTHING) {
                     mRunCount++;
@@ -2852,7 +2911,7 @@
             final int state = started ? memFactor : STATE_NOTHING;
             if (mStartedState != state) {
                 if (mStartedState != STATE_NOTHING) {
-                    addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
+                    addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
                             now - mStartedStartTime);
                 } else if (started) {
                     mStartedCount++;
@@ -2878,7 +2937,7 @@
             final int state = bound ? memFactor : STATE_NOTHING;
             if (mBoundState != state) {
                 if (mBoundState != STATE_NOTHING) {
-                    addStateTime(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
+                    addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
                             now - mBoundStartTime);
                 } else if (bound) {
                     mBoundCount++;
@@ -2896,7 +2955,7 @@
             final int state = executing ? memFactor : STATE_NOTHING;
             if (mExecState != state) {
                 if (mExecState != STATE_NOTHING) {
-                    addStateTime(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
+                    addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
                 } else if (executing) {
                     mExecCount++;
                 }
@@ -2909,8 +2968,7 @@
         private long getDuration(int opType, int curState, long startTime, int memFactor,
                 long now) {
             int state = opType + (memFactor*SERVICE_COUNT);
-            int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
-            long time = idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
+            long time = getDuration(state, now);
             if (curState == memFactor) {
                 time += now - startTime;
             }
@@ -2923,10 +2981,12 @@
                 = new ArrayMap<String, ProcessState>();
         public final ArrayMap<String, ServiceState> mServices
                 = new ArrayMap<String, ServiceState>();
-        final int mUid;
+        public final String mPackageName;
+        public final int mUid;
 
-        public PackageState(int uid) {
+        public PackageState(String packageName, int uid) {
             mUid = uid;
+            mPackageName = packageName;
         }
     }
 
@@ -2951,6 +3011,9 @@
         }
 
         void print(PrintWriter pw, long overallTime, boolean full) {
+            if (totalTime > overallTime) {
+                pw.print("*");
+            }
             printPercent(pw, (double) totalTime / (double) overallTime);
             if (numPss > 0) {
                 pw.print(" (");
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 15792e8..62f057f 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -156,31 +156,32 @@
  * Uses libmemtrack to retrieve graphics memory that the process is using.
  * Any graphics memory reported in /proc/pid/smaps is not included here.
  */
-static int read_memtrack_memory(struct memtrack_proc* p, int pid, struct graphics_memory_pss* graphics_mem)
+static int read_memtrack_memory(struct memtrack_proc* p, int pid,
+        struct graphics_memory_pss* graphics_mem)
 {
     int err = memtrack_proc_get(p, pid);
     if (err != 0) {
-        ALOGE("failed to get memory consumption info: %d", err);
+        ALOGW("failed to get memory consumption info: %d", err);
         return err;
     }
 
     ssize_t pss = memtrack_proc_graphics_pss(p);
     if (pss < 0) {
-        ALOGE("failed to get graphics pss: %d", pss);
+        ALOGW("failed to get graphics pss: %d", pss);
         return pss;
     }
     graphics_mem->graphics = pss / 1024;
 
     pss = memtrack_proc_gl_pss(p);
     if (pss < 0) {
-        ALOGE("failed to get gl pss: %d", pss);
+        ALOGW("failed to get gl pss: %d", pss);
         return pss;
     }
     graphics_mem->gl = pss / 1024;
 
     pss = memtrack_proc_other_pss(p);
     if (pss < 0) {
-        ALOGE("failed to get other pss: %d", pss);
+        ALOGW("failed to get other pss: %d", pss);
         return pss;
     }
     graphics_mem->other = pss / 1024;
@@ -199,7 +200,7 @@
 
     struct memtrack_proc* p = memtrack_proc_new();
     if (p == NULL) {
-        ALOGE("failed to create memtrack_proc");
+        ALOGW("failed to create memtrack_proc");
         return -1;
     }
 
@@ -418,8 +419,6 @@
         stats[HEAP_GL].privateDirty = graphics_mem.gl;
         stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
         stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
-    } else {
-        ALOGE("Failed to read gpu memory");
     }
 
     for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
@@ -623,7 +622,7 @@
         close(fd);
         if (len > 0) {
             buffer[len] = 0;
-            mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer);
+            mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
         }
     }
 
@@ -633,7 +632,7 @@
     }
     jlong* outArray = env->GetLongArrayElements(out, 0);
     if (outArray != NULL) {
-        for (int i=0; i<maxNum && tags[i]; i++) {
+        for (int i=0; i<maxNum; i++) {
             outArray[i] = mem[i];
         }
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b41b5b5..4c73cd7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -773,6 +773,13 @@
         android:description="@string/permdesc_bluetoothAdmin"
         android:label="@string/permlab_bluetoothAdmin" />
 
+    <!-- Allows applications to pair bluetooth devices without user interaction -->
+    <permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
+        android:permissionGroup="android.permission-group.BLUETOOTH_NETWORK"
+        android:protectionLevel="system|signature"
+        android:description="@string/permdesc_bluetoothPriv"
+        android:label="@string/permlab_bluetoothPriv" />
+
     <!-- Allows bluetooth stack to access files
          @hide This should only be used by Bluetooth apk.
     -->
diff --git a/core/res/res/drawable-hdpi/toast_frame_holo.9.png b/core/res/res/drawable-hdpi/toast_frame_holo.9.png
index f0d9b21..a804a8a 100644
--- a/core/res/res/drawable-hdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-hdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/toast_frame_holo.9.png b/core/res/res/drawable-mdpi/toast_frame_holo.9.png
index 458137c..778e4e6 100644
--- a/core/res/res/drawable-mdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-mdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
index f1209a2..77e69c7 100644
--- a/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-xhdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png
index 6c86258..edecb63 100644
--- a/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png
+++ b/core/res/res/drawable-xxhdpi/toast_frame_holo.9.png
Binary files differ
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6019e36..e39fd2a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -352,6 +352,10 @@
     <!-- Wifi driver supports batched scan -->
     <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
 
+    <!-- Default wifi country code.  If the device is going to be sold in the US this
+         needs to be US.  Uses ISO 3166 country code -->
+    <string translatable="false" name="config_wifi_default_country_code">US</string>
+
     <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d0c24e2..6c334e2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1801,6 +1801,15 @@
     <string name="permdesc_bluetoothAdmin" product="default">Allows the app to configure
       the local Bluetooth phone, and to discover and pair with remote devices.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bluetoothPriv">allow Bluetooth pairing by Application</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bluetoothPriv" product="tablet">Allows the app to
+      pair with remote devices without user interaction.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bluetoothPriv" product="default">Allows the app to
+      pair with remote devices without user interaction.</string>
+
     <string name="permlab_accessWimaxState">connect and disconnect from WiMAX</string>
     <string name="permdesc_accessWimaxState">Allows the app to determine whether
      WiMAX is enabled and information about any WiMAX networks that are
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f739bed..c786888 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -817,6 +817,7 @@
   <java-symbol type="string" name="wifi_tether_configure_ssid_default" />
   <java-symbol type="string" name="wifi_watchdog_network_disabled" />
   <java-symbol type="string" name="wifi_watchdog_network_disabled_detailed" />
+  <java-symbol type="string" name="config_wifi_default_country_code" />
   <java-symbol type="string" name="imei" />
   <java-symbol type="string" name="meid" />
   <java-symbol type="string" name="granularity_label_character" />
diff --git a/docs/html/guide/topics/ui/controls/checkbox.jd b/docs/html/guide/topics/ui/controls/checkbox.jd
index 99140b5..2a64e38 100644
--- a/docs/html/guide/topics/ui/controls/checkbox.jd
+++ b/docs/html/guide/topics/ui/controls/checkbox.jd
@@ -94,7 +94,7 @@
 android.view.View} that was clicked)</li>
 </ul>
 
-<p class="note"><strong>Tip:</strong> If you need to change the radio button state
+<p class="note"><strong>Tip:</strong> If you need to change the checkbox state
 yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
 use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
 android.widget.CompoundButton#toggle()} method.</p>
\ No newline at end of file
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index 09767bf..d934c4b 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -593,7 +593,7 @@
                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        // Send the negative button event back to the host activity
-                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
+                       mListener.onDialogNegativeClick(NoticeDialogFragment.this);
                    }
                });
         return builder.create();
diff --git a/docs/html/training/articles/security-ssl.jd b/docs/html/training/articles/security-ssl.jd
index d3f68e2..f52865a 100644
--- a/docs/html/training/articles/security-ssl.jd
+++ b/docs/html/training/articles/security-ssl.jd
@@ -250,7 +250,7 @@
 This is similar to an unknown certificate authority, so you can use the
 same approach from the previous section.</p>
 
-<p>You can create yout own {@link javax.net.ssl.TrustManager},
+<p>You can create your own {@link javax.net.ssl.TrustManager},
 this time trusting the server certificate directly. This has all of the
 downsides discussed earlier of tying your app directly to a certificate, but can be done
 securely. However, you should be careful to make sure your self-signed certificate has a
diff --git a/docs/html/training/articles/smp.jd b/docs/html/training/articles/smp.jd
index 0f667d7..7240eec 100644
--- a/docs/html/training/articles/smp.jd
+++ b/docs/html/training/articles/smp.jd
@@ -1057,7 +1057,7 @@
 fix them.  Before we do that, we need to discuss the use of a basic language
 feature.</p>
 
-<h4 id="volatile">C/C+++ and "volatile"</h4>
+<h4 id="volatile">C/C++ and "volatile"</h4>
 
 <p>When writing single-threaded code, declaring a variable “volatile” can be
 very useful.  The compiler will not omit or reorder accesses to volatile
diff --git a/docs/html/training/monitoring-device-state/connectivity-monitoring.jd b/docs/html/training/monitoring-device-state/connectivity-monitoring.jd
index 11a05e1..fb5096d 100644
--- a/docs/html/training/monitoring-device-state/connectivity-monitoring.jd
+++ b/docs/html/training/monitoring-device-state/connectivity-monitoring.jd
@@ -49,7 +49,8 @@
         (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
  
 NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
-boolean isConnected = activeNetwork.isConnectedOrConnecting();</pre>
+boolean isConnected = activeNetwork != null &&
+                      activeNetwork.isConnectedOrConnecting();</pre>
 
 
 <h2 id="DetermineType">Determine the Type of your Internet Connection</h2> 
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index a3c9dac..77ac235 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -576,29 +576,6 @@
           </li>
         </ul>
       </li>
-      <li class="nav-section">
-        <div class="nav-section-header">
-          <a href="<?cs var:toroot ?>training/id-auth/index.html"
-             description=
-             "How to remember the user by account, authenticate the user, acquire user permission
-             for the user's online data, and create custom accounts on the device."
-            >Remembering Users</a>
-        </div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>training/id-auth/identify.html">
-            Remembering Your User
-          </a>
-          </li>
-          <li><a href="<?cs var:toroot ?>training/id-auth/authenticate.html">
-            Authenticating to OAuth2 Services
-          </a>
-          </li>
-          <li><a href="<?cs var:toroot ?>training/id-auth/custom_auth.html">
-            Creating a Custom Account Type
-          </a>
-          </li>
-        </ul>
-      </li>
 
       <li class="nav-section">
         <div class="nav-section-header">
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index deba2cc..def9aa7 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1772,6 +1772,9 @@
             mSelectedSubtitleTrackIndex = -1;
         }
         setOnSubtitleDataListener(null);
+        if (track == null) {
+            return;
+        }
         for (int i = 0; i < mInbandSubtitleTracks.length; i++) {
             if (mInbandSubtitleTracks[i] == track) {
                 Log.v(TAG, "Selecting subtitle track " + i);
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 5127479..06af5de 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -374,7 +374,7 @@
          * Called when a sound has completed loading.
          *
          * @param soundPool SoundPool object from the load() method
-         * @param soundPool the sample ID of the sound loaded.
+         * @param sampleId the sample ID of the sound loaded.
          * @param status the status of the load operation (0 = success)
          */
         public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java
index 8090561..13205bc 100644
--- a/media/java/android/media/SubtitleController.java
+++ b/media/java/android/media/SubtitleController.java
@@ -21,6 +21,9 @@
 
 import android.content.Context;
 import android.media.SubtitleTrack.RenderingWidget;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.view.accessibility.CaptioningManager;
 
 /**
@@ -37,6 +40,34 @@
     private SubtitleTrack mSelectedTrack;
     private boolean mShowing;
     private CaptioningManager mCaptioningManager;
+    private Handler mHandler;
+
+    private static final int WHAT_SHOW = 1;
+    private static final int WHAT_HIDE = 2;
+    private static final int WHAT_SELECT_TRACK = 3;
+    private static final int WHAT_SELECT_DEFAULT_TRACK = 4;
+
+    private final Handler.Callback mCallback = new Handler.Callback() {
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+            case WHAT_SHOW:
+                doShow();
+                return true;
+            case WHAT_HIDE:
+                doHide();
+                return true;
+            case WHAT_SELECT_TRACK:
+                doSelectTrack((SubtitleTrack)msg.obj);
+                return true;
+            case WHAT_SELECT_DEFAULT_TRACK:
+                doSelectDefaultTrack();
+                return true;
+            default:
+                return false;
+            }
+        }
+    };
 
     private CaptioningManager.CaptioningChangeListener mCaptioningChangeListener =
         new CaptioningManager.CaptioningChangeListener() {
@@ -112,7 +143,7 @@
      * in-band data from the {@link MediaPlayer}.  However, this does
      * not change the subtitle visibility.
      *
-     * Must be called from the UI thread.
+     * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
      *
      * @param track The subtitle track to select.  This must be one of the
      *              tracks in {@link #getTracks}.
@@ -122,9 +153,15 @@
         if (track != null && !mTracks.contains(track)) {
             return false;
         }
+
+        processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_TRACK, track));
+        return true;
+    }
+
+    private void doSelectTrack(SubtitleTrack track) {
         mTrackIsExplicit = true;
         if (mSelectedTrack == track) {
-            return true;
+            return;
         }
 
         if (mSelectedTrack != null) {
@@ -145,7 +182,6 @@
         if (mListener != null) {
             mListener.onSubtitleTrackSelected(track);
         }
-        return true;
     }
 
     /**
@@ -170,8 +206,6 @@
      *
      * The default values for these flags are DEFAULT=no, AUTOSELECT=yes
      * and FORCED=no.
-     *
-     * Must be called from the UI thread.
      */
     public SubtitleTrack getDefaultTrack() {
         SubtitleTrack bestTrack = null;
@@ -226,8 +260,12 @@
     private boolean mTrackIsExplicit = false;
     private boolean mVisibilityIsExplicit = false;
 
-    /** @hide - called from UI thread */
+    /** @hide - should be called from anchor thread */
     public void selectDefaultTrack() {
+        processOnAnchor(mHandler.obtainMessage(WHAT_SELECT_DEFAULT_TRACK));
+    }
+
+    private void doSelectDefaultTrack() {
         if (mTrackIsExplicit) {
             // If track selection is explicit, but visibility
             // is not, it falls back to the captioning setting
@@ -259,8 +297,9 @@
         }
     }
 
-    /** @hide - called from UI thread */
+    /** @hide - must be called from anchor thread */
     public void reset() {
+        checkAnchorLooper();
         hide();
         selectTrack(null);
         mTracks.clear();
@@ -301,9 +340,13 @@
     /**
      * Show the selected (or default) subtitle track.
      *
-     * Must be called from the UI thread.
+     * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
      */
     public void show() {
+        processOnAnchor(mHandler.obtainMessage(WHAT_SHOW));
+    }
+
+    private void doShow() {
         mShowing = true;
         mVisibilityIsExplicit = true;
         if (mSelectedTrack != null) {
@@ -314,9 +357,13 @@
     /**
      * Hide the selected (or default) subtitle track.
      *
-     * Must be called from the UI thread.
+     * Should be called from the anchor's (UI) thread. {@see #Anchor.getSubtitleLooper}
      */
     public void hide() {
+        processOnAnchor(mHandler.obtainMessage(WHAT_HIDE));
+    }
+
+    private void doHide() {
         mVisibilityIsExplicit = true;
         if (mSelectedTrack != null) {
             mSelectedTrack.hide();
@@ -384,25 +431,53 @@
          * @hide
          */
         public void setSubtitleWidget(RenderingWidget subtitleWidget);
+
+        /**
+         * Anchors provide the looper on which all track visibility changes
+         * (track.show/hide, setSubtitleWidget) will take place.
+         * @hide
+         */
+        public Looper getSubtitleLooper();
     }
 
     private Anchor mAnchor;
 
-    /** @hide - called from UI thread */
+    /**
+     *  @hide - called from anchor's looper (if any, both when unsetting and
+     *  setting)
+     */
     public void setAnchor(Anchor anchor) {
         if (mAnchor == anchor) {
             return;
         }
 
         if (mAnchor != null) {
+            checkAnchorLooper();
             mAnchor.setSubtitleWidget(null);
         }
         mAnchor = anchor;
+        mHandler = null;
         if (mAnchor != null) {
+            mHandler = new Handler(mAnchor.getSubtitleLooper(), mCallback);
+            checkAnchorLooper();
             mAnchor.setSubtitleWidget(getRenderingWidget());
         }
     }
 
+    private void checkAnchorLooper() {
+        assert mHandler != null : "Should have a looper already";
+        assert Looper.myLooper() == mHandler.getLooper() : "Must be called from the anchor's looper";
+    }
+
+    private void processOnAnchor(Message m) {
+        assert mHandler != null : "Should have a looper already";
+        if (Looper.myLooper() == mHandler.getLooper()) {
+            mHandler.dispatchMessage(m);
+        } else {
+            mHandler.sendMessage(m);
+        }
+    }
+
     public interface Listener {
         /**
          * Called when a subtitle track has been selected.
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
index 0240874..904d525 100644
--- a/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..403eddb
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-hdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
index 0240874..068619b 100644
--- a/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..9a9cf5e
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-mdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
index 0240874..e38a868 100644
--- a/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..0d75172
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..205c34b
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xhdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
index 0240874..0b332e4 100644
--- a/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_dir_shadow.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png
new file mode 100644
index 0000000..32b5f98
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_hairline_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png
new file mode 100644
index 0000000..f47d50a
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable-xxhdpi/ic_drawer_tall_divider.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index 3bea166..851061f 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -21,17 +21,18 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
-    android:orientation="horizontal">
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <FrameLayout
         android:id="@android:id/icon"
         android:layout_width="@dimen/icon_size"
         android:layout_height="@dimen/icon_size"
         android:layout_marginStart="12dp"
-        android:layout_marginEnd="20dp"
-        android:layout_gravity="center_vertical">
+        android:layout_marginEnd="20dp">
 
         <ImageView
             android:id="@+id/icon_mime"
@@ -49,11 +50,11 @@
 
     </FrameLayout>
 
+    <!-- This is the one special case where we want baseline alignment! -->
     <LinearLayout
-        android:layout_width="0dip"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
-        android:layout_gravity="center_vertical"
         android:orientation="horizontal">
 
         <TextView
diff --git a/packages/DocumentsUI/res/layout-sw720dp/activity.xml b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
index 78735fd..9286277 100644
--- a/packages/DocumentsUI/res/layout-sw720dp/activity.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp/activity.xml
@@ -17,7 +17,8 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <FrameLayout
         android:layout_width="wrap_content"
@@ -47,7 +48,7 @@
         <com.android.documentsui.DirectoryContainerView
             android:id="@+id/container_directory"
             android:layout_width="match_parent"
-            android:layout_height="0dip"
+            android:layout_height="0dp"
             android:layout_weight="1" />
 
         <FrameLayout
diff --git a/packages/DocumentsUI/res/layout/activity.xml b/packages/DocumentsUI/res/layout/activity.xml
index 9937c39..2ef7e9c 100644
--- a/packages/DocumentsUI/res/layout/activity.xml
+++ b/packages/DocumentsUI/res/layout/activity.xml
@@ -27,7 +27,7 @@
         <com.android.documentsui.DirectoryContainerView
             android:id="@+id/container_directory"
             android:layout_width="match_parent"
-            android:layout_height="0dip"
+            android:layout_height="0dp"
             android:layout_weight="1" />
 
         <FrameLayout
diff --git a/packages/DocumentsUI/res/layout/fragment_roots.xml b/packages/DocumentsUI/res/layout/fragment_roots.xml
index 09782d9..c3a3da0 100644
--- a/packages/DocumentsUI/res/layout/fragment_roots.xml
+++ b/packages/DocumentsUI/res/layout/fragment_roots.xml
@@ -18,4 +18,4 @@
     android:id="@android:id/list"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:divider="@null" />
+    android:divider="@drawable/ic_drawer_hairline_divider" />
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 570b517..891f0a0 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -29,6 +29,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
+        android:baselineAligned="false"
         android:gravity="center_vertical"
         android:background="#ddd"
         android:minHeight="?android:attr/listPreferredItemHeightSmall">
@@ -44,7 +45,7 @@
 
         <EditText
             android:id="@android:id/title"
-            android:layout_width="0dip"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:singleLine="true"
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index b745bb9..bb5dce1 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -28,37 +28,25 @@
 
         <FrameLayout
             android:layout_width="match_parent"
-            android:layout_height="0dip"
+            android:layout_height="0dp"
             android:layout_weight="1"
             android:layout_marginBottom="6dp"
-            android:background="#fff">
-
-            <FrameLayout
-                android:id="@android:id/icon"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent">
-
-                <ImageView
-                    android:id="@+id/icon_mime"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:scaleType="centerInside"
-                    android:contentDescription="@null" />
-
-                <ImageView
-                    android:id="@+id/icon_thumb"
-                    android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:scaleType="centerCrop"
-                    android:contentDescription="@null" />
-
-            </FrameLayout>
+            android:background="#fff"
+            android:foreground="@drawable/ic_grid_gradient_bg"
+            android:foregroundGravity="fill">
 
             <ImageView
+                android:id="@+id/icon_mime"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:scaleType="fitXY"
-                android:src="@drawable/ic_grid_gradient_bg"
+                android:scaleType="centerInside"
+                android:contentDescription="@null" />
+
+            <ImageView
+                android:id="@+id/icon_thumb"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:scaleType="centerCrop"
                 android:contentDescription="@null" />
 
         </FrameLayout>
@@ -67,7 +55,9 @@
             android:id="@+id/line1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:gravity="center_vertical"
             android:orientation="horizontal"
+            android:baselineAligned="false"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
 
@@ -85,7 +75,7 @@
                 android:id="@android:id/icon1"
                 android:layout_width="@dimen/root_icon_size"
                 android:layout_height="@dimen/root_icon_size"
-                android:layout_marginStart="8dip"
+                android:layout_marginStart="8dp"
                 android:scaleType="centerInside"
                 android:contentDescription="@null" />
 
@@ -95,16 +85,17 @@
             android:id="@+id/line2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:gravity="center_vertical"
             android:orientation="horizontal"
+            android:baselineAligned="false"
             android:paddingStart="?android:attr/listPreferredItemPaddingStart"
             android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
 
             <TextView
                 android:id="@+id/date"
-                android:layout_width="wrap_content"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:minWidth="80dp"
+                android:layout_weight="0.5"
                 android:singleLine="true"
                 android:ellipsize="marquee"
                 android:textAlignment="viewStart"
@@ -112,26 +103,20 @@
 
             <TextView
                 android:id="@+id/size"
-                android:layout_width="wrap_content"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
+                android:layout_weight="0.5"
                 android:layout_marginStart="8dp"
-                android:minWidth="80dp"
                 android:singleLine="true"
                 android:ellipsize="marquee"
                 android:textAlignment="viewStart"
                 style="@style/TextAppearance.Small" />
 
-            <Space
-                android:layout_width="0dp"
-                android:layout_height="0dp"
-                android:layout_weight="1" />
-
             <ImageView
                 android:id="@android:id/icon2"
                 android:layout_width="@dimen/root_icon_size"
                 android:layout_height="@dimen/root_icon_size"
-                android:layout_marginStart="8dip"
+                android:layout_marginStart="8dp"
                 android:scaleType="centerInside"
                 android:contentDescription="@null"
                 android:visibility="gone" />
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index 84fda9d..4c5b2e3 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -21,17 +21,18 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
-    android:orientation="horizontal">
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <FrameLayout
         android:id="@android:id/icon"
         android:layout_width="@dimen/icon_size"
         android:layout_height="@dimen/icon_size"
         android:layout_marginStart="12dp"
-        android:layout_marginEnd="20dp"
-        android:layout_gravity="center_vertical">
+        android:layout_marginEnd="20dp">
 
         <ImageView
             android:id="@+id/icon_mime"
@@ -50,20 +51,20 @@
     </FrameLayout>
 
     <LinearLayout
-        android:layout_width="0dip"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_weight="1"
-        android:layout_gravity="center_vertical"
         android:orientation="vertical">
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="horizontal">
+            android:orientation="horizontal"
+            android:baselineAligned="false">
 
             <TextView
                 android:id="@android:id/title"
-                android:layout_width="0dip"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_weight="1"
                 android:singleLine="true"
@@ -75,7 +76,7 @@
                 android:id="@android:id/icon1"
                 android:layout_width="@dimen/root_icon_size"
                 android:layout_height="@dimen/root_icon_size"
-                android:layout_marginStart="8dip"
+                android:layout_marginStart="8dp"
                 android:scaleType="centerInside"
                 android:contentDescription="@null" />
 
@@ -85,13 +86,15 @@
             android:id="@+id/line2"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="horizontal">
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:baselineAligned="false">
 
             <TextView
                 android:id="@+id/date"
-                android:layout_width="wrap_content"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
+                android:layout_weight="0.25"
                 android:minWidth="70dp"
                 android:singleLine="true"
                 android:ellipsize="marquee"
@@ -100,11 +103,11 @@
 
             <TextView
                 android:id="@+id/size"
-                android:layout_width="wrap_content"
+                android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical"
-                android:minWidth="70dp"
+                android:layout_weight="0.25"
                 android:layout_marginStart="8dp"
+                android:minWidth="70dp"
                 android:singleLine="true"
                 android:ellipsize="marquee"
                 android:textAlignment="viewStart"
@@ -114,8 +117,7 @@
                 android:id="@android:id/summary"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:layout_gravity="center_vertical"
+                android:layout_weight="0.5"
                 android:layout_marginStart="8dp"
                 android:singleLine="true"
                 android:ellipsize="marquee"
diff --git a/packages/DocumentsUI/res/layout/item_loading_grid.xml b/packages/DocumentsUI/res/layout/item_loading_grid.xml
index 21be137..0bf6137 100644
--- a/packages/DocumentsUI/res/layout/item_loading_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_loading_grid.xml
@@ -20,8 +20,8 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
     android:orientation="horizontal">
 
     <ProgressBar
diff --git a/packages/DocumentsUI/res/layout/item_loading_list.xml b/packages/DocumentsUI/res/layout/item_loading_list.xml
index 7da71e3..cdcd01d 100644
--- a/packages/DocumentsUI/res/layout/item_loading_list.xml
+++ b/packages/DocumentsUI/res/layout/item_loading_list.xml
@@ -20,9 +20,8 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
-    android:orientation="horizontal">
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp">
 
     <ProgressBar
         android:layout_width="wrap_content"
diff --git a/packages/DocumentsUI/res/layout/item_message_list.xml b/packages/DocumentsUI/res/layout/item_message_list.xml
index ffda98c..2bcbc2d 100644
--- a/packages/DocumentsUI/res/layout/item_message_list.xml
+++ b/packages/DocumentsUI/res/layout/item_message_list.xml
@@ -21,15 +21,16 @@
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:paddingTop="8dip"
-    android:paddingBottom="8dip"
-    android:orientation="horizontal">
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp"
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <ImageView
         android:id="@android:id/icon"
         android:layout_width="@android:dimen/app_icon_size"
         android:layout_height="@android:dimen/app_icon_size"
-        android:layout_marginEnd="8dip"
+        android:layout_marginEnd="8dp"
         android:layout_gravity="center_vertical"
         android:scaleType="centerInside"
         android:contentDescription="@null" />
diff --git a/packages/DocumentsUI/res/layout/item_root.xml b/packages/DocumentsUI/res/layout/item_root.xml
index 98d78da..9b52d85 100644
--- a/packages/DocumentsUI/res/layout/item_root.xml
+++ b/packages/DocumentsUI/res/layout/item_root.xml
@@ -22,13 +22,14 @@
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:gravity="center_vertical"
     android:orientation="horizontal"
+    android:baselineAligned="false"
     android:background="@drawable/item_root">
 
     <ImageView
         android:id="@android:id/icon"
         android:layout_width="@dimen/icon_size"
         android:layout_height="@dimen/icon_size"
-        android:layout_marginEnd="8dip"
+        android:layout_marginEnd="8dp"
         android:scaleType="centerInside"
         android:contentDescription="@null" />
 
diff --git a/packages/DocumentsUI/res/layout/item_root_spacer.xml b/packages/DocumentsUI/res/layout/item_root_spacer.xml
new file mode 100644
index 0000000..7d96ac8
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/item_root_spacer.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/ic_drawer_tall_divider" />
diff --git a/packages/DocumentsUI/res/layout/item_title.xml b/packages/DocumentsUI/res/layout/item_title.xml
index 7eb100a..58016f1 100644
--- a/packages/DocumentsUI/res/layout/item_title.xml
+++ b/packages/DocumentsUI/res/layout/item_title.xml
@@ -21,7 +21,8 @@
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:gravity="center_vertical"
-    android:orientation="horizontal">
+    android:orientation="horizontal"
+    android:baselineAligned="false">
 
     <ImageView
         android:id="@+id/subdir"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 9d92cd8..48bfaf0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -25,6 +25,7 @@
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
@@ -35,6 +36,8 @@
 
 import com.android.documentsui.model.DocumentInfo;
 
+import java.io.FileNotFoundException;
+
 /**
  * Dialog to create a new directory.
  */
@@ -64,24 +67,45 @@
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 final String displayName = text1.getText().toString();
-
-                final DocumentsActivity activity = (DocumentsActivity) getActivity();
-                final DocumentInfo cwd = activity.getCurrentDirectory();
-
-                try {
-                    final Uri childUri = DocumentsContract.createDocument(
-                            resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, displayName);
-
-                    // Navigate into newly created child
-                    final DocumentInfo childDoc = DocumentInfo.fromUri(resolver, childUri);
-                    activity.onDocumentPicked(childDoc);
-                } catch (Exception e) {
-                    Toast.makeText(context, R.string.create_error, Toast.LENGTH_SHORT).show();
-                }
+                new CreateDirectoryTask(displayName).execute();
             }
         });
         builder.setNegativeButton(android.R.string.cancel, null);
 
         return builder.create();
     }
+
+    private class CreateDirectoryTask extends AsyncTask<Void, Void, DocumentInfo> {
+        private final String mDisplayName;
+
+        public CreateDirectoryTask(String displayName) {
+            mDisplayName = displayName;
+        }
+
+        @Override
+        protected DocumentInfo doInBackground(Void... params) {
+            final DocumentsActivity activity = (DocumentsActivity) getActivity();
+            final ContentResolver resolver = activity.getContentResolver();
+
+            final DocumentInfo cwd = activity.getCurrentDirectory();
+            final Uri childUri = DocumentsContract.createDocument(
+                    resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
+            try {
+                return DocumentInfo.fromUri(resolver, childUri);
+            } catch (FileNotFoundException e) {
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(DocumentInfo result) {
+            final DocumentsActivity activity = (DocumentsActivity) getActivity();
+            if (result != null) {
+                // Navigate into newly created child
+                activity.onDocumentPicked(result);
+            } else {
+                Toast.makeText(activity, R.string.create_error, Toast.LENGTH_SHORT).show();
+            }
+        }
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index c46dfb2..1f11aed 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -742,7 +742,6 @@
             final View line1 = convertView.findViewById(R.id.line1);
             final View line2 = convertView.findViewById(R.id.line2);
 
-            final View icon = convertView.findViewById(android.R.id.icon);
             final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime);
             final ImageView iconThumb = (ImageView) convertView.findViewById(R.id.icon_thumb);
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
@@ -786,10 +785,12 @@
             // loaded in background.
             if (cacheHit) {
                 iconMime.setAlpha(0f);
+                iconMime.setImageDrawable(null);
                 iconThumb.setAlpha(1f);
             } else {
                 iconMime.setAlpha(1f);
                 iconThumb.setAlpha(0f);
+                iconThumb.setImageDrawable(null);
                 if (docIcon != 0) {
                     iconMime.setImageDrawable(
                             IconUtils.loadPackageIcon(context, docAuthority, docIcon));
@@ -895,12 +896,14 @@
             final boolean enabled = isDocumentEnabled(docMimeType, docFlags);
             if (enabled) {
                 setEnabledRecursive(convertView, true);
-                icon.setAlpha(1f);
+                iconMime.setAlpha(1f);
+                iconThumb.setAlpha(1f);
                 if (icon1 != null) icon1.setAlpha(1f);
                 if (icon2 != null) icon2.setAlpha(1f);
             } else {
                 setEnabledRecursive(convertView, false);
-                icon.setAlpha(0.5f);
+                iconMime.setAlpha(0.5f);
+                iconThumb.setAlpha(0.5f);
                 if (icon1 != null) icon1.setAlpha(0.5f);
                 if (icon2 != null) icon2.setAlpha(0.5f);
             }
@@ -991,10 +994,11 @@
                 mIconThumb.setTag(null);
                 mIconThumb.setImageBitmap(result);
 
-                mIconMime.setAlpha(1f);
+                final float targetAlpha = mIconMime.isEnabled() ? 1f : 0.5f;
+                mIconMime.setAlpha(targetAlpha);
                 mIconMime.animate().alpha(0f).start();
                 mIconThumb.setAlpha(0f);
-                mIconThumb.animate().alpha(1f).start();
+                mIconThumb.animate().alpha(targetAlpha).start();
             }
         }
     }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 8627ecf..0b3ecf8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -63,6 +63,9 @@
 }
 
 public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
+
+    private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
+
     private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
 
     private final int mType;
@@ -164,8 +167,7 @@
 
             if (mType == DirectoryFragment.TYPE_SEARCH) {
                 // Filter directories out of search results, for now
-                cursor = new FilteringCursorWrapper(cursor, null, new String[] {
-                        Document.MIME_TYPE_DIR });
+                cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
             } else {
                 // Normal directories should have sorting applied
                 cursor = new SortingCursorWrapper(cursor, result.sortOrder);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 72fdc57..4caec8f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -854,14 +854,7 @@
         mState.stackTouched = true;
 
         if (!mRoots.isRecentsRoot(root)) {
-            try {
-                final Uri uri = DocumentsContract.buildDocumentUri(root.authority, root.documentId);
-                final DocumentInfo doc = DocumentInfo.fromUri(getContentResolver(), uri);
-                mState.stack.push(doc);
-                mState.stackTouched = true;
-                onCurrentDirectoryChanged(ANIM_SIDE);
-            } catch (FileNotFoundException e) {
-            }
+            new PickRootTask(root).execute();
         } else {
             onCurrentDirectoryChanged(ANIM_SIDE);
         }
@@ -871,6 +864,34 @@
         }
     }
 
+    private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+        private RootInfo mRoot;
+
+        public PickRootTask(RootInfo root) {
+            mRoot = root;
+        }
+
+        @Override
+        protected DocumentInfo doInBackground(Void... params) {
+            try {
+                final Uri uri = DocumentsContract.buildDocumentUri(
+                        mRoot.authority, mRoot.documentId);
+                return DocumentInfo.fromUri(getContentResolver(), uri);
+            } catch (FileNotFoundException e) {
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(DocumentInfo result) {
+            if (result != null) {
+                mState.stack.push(result);
+                mState.stackTouched = true;
+                onCurrentDirectoryChanged(ANIM_SIDE);
+            }
+        }
+    }
+
     public void onAppPicked(ResolveInfo info) {
         final Intent intent = new Intent(getIntent());
         intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
@@ -909,7 +930,7 @@
             onCurrentDirectoryChanged(ANIM_DOWN);
         } else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
             // Explicit file picked, return
-            onFinished(doc.derivedUri);
+            new ExistingFinishTask(doc.derivedUri).execute();
         } else if (mState.action == ACTION_CREATE) {
             // Replace selected file
             SaveFragment.get(fm).setReplaceTarget(doc);
@@ -943,29 +964,19 @@
             for (int i = 0; i < size; i++) {
                 uris[i] = docs.get(i).derivedUri;
             }
-            onFinished(uris);
+            new ExistingFinishTask(uris).execute();
         }
     }
 
     public void onSaveRequested(DocumentInfo replaceTarget) {
-        onFinished(replaceTarget.derivedUri);
+        new ExistingFinishTask(replaceTarget.derivedUri).execute();
     }
 
     public void onSaveRequested(String mimeType, String displayName) {
-        final DocumentInfo cwd = getCurrentDirectory();
-
-        final Uri childUri = DocumentsContract.createDocument(
-                getContentResolver(), cwd.derivedUri, mimeType, displayName);
-        if (childUri != null) {
-            onFinished(childUri);
-        } else {
-            Toast.makeText(this, R.string.save_error, Toast.LENGTH_SHORT).show();
-        }
+        new CreateFinishTask(mimeType, displayName).execute();
     }
 
-    private void onFinished(Uri... uris) {
-        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
-
+    private void saveStackBlocking() {
         final ContentResolver resolver = getContentResolver();
         final ContentValues values = new ContentValues();
 
@@ -973,6 +984,7 @@
         if (mState.action == ACTION_CREATE) {
             // Remember stack for last create
             values.clear();
+            values.put(RecentColumns.KEY, mState.stack.buildKey());
             values.put(RecentColumns.STACK, rawStack);
             resolver.insert(RecentsProvider.buildRecent(), values);
         }
@@ -983,6 +995,10 @@
         values.put(ResumeColumns.STACK, rawStack);
         values.put(ResumeColumns.EXTERNAL, 0);
         resolver.insert(RecentsProvider.buildResume(packageName), values);
+    }
+
+    private void onFinished(Uri... uris) {
+        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
 
         final Intent intent = new Intent();
         if (uris.length == 1) {
@@ -1008,6 +1024,56 @@
         finish();
     }
 
+    private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+        private final String mMimeType;
+        private final String mDisplayName;
+
+        public CreateFinishTask(String mimeType, String displayName) {
+            mMimeType = mimeType;
+            mDisplayName = displayName;
+        }
+
+        @Override
+        protected Uri doInBackground(Void... params) {
+            final DocumentInfo cwd = getCurrentDirectory();
+            final Uri childUri = DocumentsContract.createDocument(
+                    getContentResolver(), cwd.derivedUri, mMimeType, mDisplayName);
+            if (childUri != null) {
+                saveStackBlocking();
+            }
+            return childUri;
+        }
+
+        @Override
+        protected void onPostExecute(Uri result) {
+            if (result != null) {
+                onFinished(result);
+            } else {
+                Toast.makeText(DocumentsActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
+                        .show();
+            }
+        }
+    }
+
+    private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+        private final Uri[] mUris;
+
+        public ExistingFinishTask(Uri... uris) {
+            mUris = uris;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            saveStackBlocking();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            onFinished(mUris);
+        }
+    }
+
     public static class State implements android.os.Parcelable {
         public int action;
         public String[] acceptMimes;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
index 5f56963..52d816f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
@@ -34,10 +34,15 @@
     private int mCount;
 
     public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes) {
-        this(cursor, acceptMimes, null);
+        this(cursor, acceptMimes, null, Long.MIN_VALUE);
     }
 
     public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes, String[] rejectMimes) {
+        this(cursor, acceptMimes, rejectMimes, Long.MIN_VALUE);
+    }
+
+    public FilteringCursorWrapper(
+            Cursor cursor, String[] acceptMimes, String[] rejectMimes, long rejectBefore) {
         mCursor = cursor;
 
         final int count = cursor.getCount();
@@ -47,9 +52,14 @@
         while (cursor.moveToNext()) {
             final String mimeType = cursor.getString(
                     cursor.getColumnIndex(Document.COLUMN_MIME_TYPE));
+            final long lastModified = cursor.getLong(
+                    cursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED));
             if (rejectMimes != null && MimePredicate.mimeMatches(rejectMimes, mimeType)) {
                 continue;
             }
+            if (lastModified < rejectBefore) {
+                continue;
+            }
             if (MimePredicate.mimeMatches(acceptMimes, mimeType)) {
                 mPosition[mCount++] = cursor.getPosition();
             }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index e390456..9a4fb7d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -26,9 +26,11 @@
 import android.database.Cursor;
 import android.database.MergeCursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
+import android.text.format.DateUtils;
 import android.util.Log;
 
 import com.android.documentsui.DocumentsActivity.State;
@@ -54,17 +56,23 @@
 public class RecentLoader extends AsyncTaskLoader<DirectoryResult> {
     private static final boolean LOGD = true;
 
-    public static final int MAX_OUTSTANDING_RECENTS = 2;
+    // TODO: adjust for svelte devices
+    // TODO: add support for oneway queries to avoid wedging loader
+    private static final int MAX_OUTSTANDING_RECENTS = 2;
 
     /**
      * Time to wait for first pass to complete before returning partial results.
      */
-    public static final int MAX_FIRST_PASS_WAIT_MILLIS = 500;
+    private static final int MAX_FIRST_PASS_WAIT_MILLIS = 500;
 
-    /**
-     * Maximum documents from a single root.
-     */
-    public static final int MAX_DOCS_FROM_ROOT = 64;
+    /** Maximum documents from a single root. */
+    private static final int MAX_DOCS_FROM_ROOT = 64;
+
+    /** Ignore documents older than this age. */
+    private static final long REJECT_OLDER_THAN = 45 * DateUtils.DAY_IN_MILLIS;
+
+    /** MIME types that should always be excluded from recents. */
+    private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR };
 
     private static final ExecutorService sExecutor = buildExecutor();
 
@@ -173,6 +181,8 @@
             }
         }
 
+        final long rejectBefore = System.currentTimeMillis() - REJECT_OLDER_THAN;
+
         // Collect all finished tasks
         List<Cursor> cursors = Lists.newArrayList();
         for (RecentTask task : mTasks.values()) {
@@ -180,7 +190,7 @@
                 try {
                     final Cursor cursor = task.get();
                     final FilteringCursorWrapper filtered = new FilteringCursorWrapper(
-                            cursor, mState.acceptMimes, new String[] { Document.MIME_TYPE_DIR }) {
+                            cursor, mState.acceptMimes, RECENT_REJECT_MIMES, rejectBefore) {
                         @Override
                         public void close() {
                             // Ignored, since we manage cursor lifecycle internally
@@ -203,11 +213,22 @@
         final DirectoryResult result = new DirectoryResult();
         result.sortOrder = SORT_ORDER_LAST_MODIFIED;
 
-        if (cursors.size() > 0) {
-            final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
-            final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder);
-            result.cursor = sorted;
+        // Hint to UI if we're still loading
+        final Bundle extras = new Bundle();
+        if (cursors.size() != mTasks.size()) {
+            extras.putBoolean(DocumentsContract.EXTRA_LOADING, true);
         }
+
+        final MergeCursor merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
+        final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder) {
+            @Override
+            public Bundle getExtras() {
+                return extras;
+            }
+        };
+
+        result.cursor = sorted;
+
         return result;
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index c975382..3954173 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -66,6 +66,7 @@
  */
 public class RecentsCreateFragment extends Fragment {
 
+    private View mEmptyView;
     private ListView mListView;
 
     private DocumentStackAdapter mAdapter;
@@ -87,6 +88,8 @@
 
         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
 
+        mEmptyView = view.findViewById(android.R.id.empty);
+
         mListView = (ListView) view.findViewById(R.id.list);
         mListView.setOnItemClickListener(mItemListener);
 
@@ -189,6 +192,13 @@
 
         public void swapStacks(List<DocumentStack> stacks) {
             mStacks = stacks;
+
+            if (isEmpty()) {
+                mEmptyView.setVisibility(View.VISIBLE);
+            } else {
+                mEmptyView.setVisibility(View.GONE);
+            }
+
             notifyDataSetChanged();
         }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
index 7386cae..4313fa7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -33,7 +33,7 @@
 public class RecentsProvider extends ContentProvider {
     private static final String TAG = "RecentsProvider";
 
-    public static final long MAX_HISTORY_IN_MILLIS = DateUtils.DAY_IN_MILLIS * 45;
+    public static final long MAX_HISTORY_IN_MILLIS = 45 * DateUtils.DAY_IN_MILLIS;
 
     private static final String AUTHORITY = "com.android.documentsui.recents";
 
@@ -56,6 +56,7 @@
     public static final String TABLE_RESUME = "resume";
 
     public static class RecentColumns {
+        public static final String KEY = "key";
         public static final String STACK = "stack";
         public static final String TIMESTAMP = "timestamp";
     }
@@ -99,16 +100,18 @@
         private static final int VERSION_INIT = 1;
         private static final int VERSION_AS_BLOB = 3;
         private static final int VERSION_ADD_EXTERNAL = 4;
+        private static final int VERSION_ADD_RECENT_KEY = 5;
 
         public DatabaseHelper(Context context) {
-            super(context, DB_NAME, null, VERSION_ADD_EXTERNAL);
+            super(context, DB_NAME, null, VERSION_ADD_RECENT_KEY);
         }
 
         @Override
         public void onCreate(SQLiteDatabase db) {
 
             db.execSQL("CREATE TABLE " + TABLE_RECENT + " (" +
-                    RecentColumns.STACK + " BLOB PRIMARY KEY ON CONFLICT REPLACE," +
+                    RecentColumns.KEY + " TEXT PRIMARY KEY ON CONFLICT REPLACE," +
+                    RecentColumns.STACK + " BLOB DEFAULT NULL," +
                     RecentColumns.TIMESTAMP + " INTEGER" +
                     ")");
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 15af8aa..e3908e9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -99,7 +99,8 @@
      */
     public void updateAsync() {
         // Special root for recents
-        mRecentsRoot.rootType = Root.ROOT_TYPE_SHORTCUT;
+        mRecentsRoot.authority = null;
+        mRecentsRoot.rootId = null;
         mRecentsRoot.icon = R.drawable.ic_root_recent;
         mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE;
         mRecentsRoot.title = mContext.getString(R.string.root_recent);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index d602622..2fb12bb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -26,7 +26,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
-import android.provider.DocumentsContract.Root;
 import android.text.TextUtils;
 import android.text.format.Formatter;
 import android.view.LayoutInflater;
@@ -37,16 +36,16 @@
 import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.ListView;
-import android.widget.Space;
 import android.widget.TextView;
 
 import com.android.documentsui.DocumentsActivity.State;
-import com.android.documentsui.SectionedListAdapter.SectionAdapter;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.RootInfo;
 import com.android.internal.util.Objects;
+import com.google.common.collect.Lists;
 
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
@@ -56,7 +55,7 @@
 public class RootsFragment extends Fragment {
 
     private ListView mList;
-    private SectionedRootsAdapter mAdapter;
+    private RootsAdapter mAdapter;
 
     private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
 
@@ -112,7 +111,7 @@
 
                 final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
 
-                mAdapter = new SectionedRootsAdapter(context, result, includeApps);
+                mAdapter = new RootsAdapter(context, result, includeApps);
                 mList.setAdapter(mAdapter);
 
                 onCurrentRootChanged();
@@ -154,136 +153,148 @@
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
-            final Object item = mAdapter.getItem(position);
-            if (item instanceof RootInfo) {
-                activity.onRootPicked((RootInfo) item, true);
-            } else if (item instanceof ResolveInfo) {
-                activity.onAppPicked((ResolveInfo) item);
+            final Item item = mAdapter.getItem(position);
+            if (item instanceof RootItem) {
+                activity.onRootPicked(((RootItem) item).root, true);
+            } else if (item instanceof AppItem) {
+                activity.onAppPicked(((AppItem) item).info);
             } else {
                 throw new IllegalStateException("Unknown root: " + item);
             }
         }
     };
 
-    private static class RootsAdapter extends ArrayAdapter<RootInfo> implements SectionAdapter {
-        public RootsAdapter(Context context) {
-            super(context, 0);
+    private static abstract class Item {
+        private final int mLayoutId;
+
+        public Item(int layoutId) {
+            mLayoutId = layoutId;
+        }
+
+        public View getView(View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(mLayoutId, parent, false);
+            }
+            bindView(convertView);
+            return convertView;
+        }
+
+        public abstract void bindView(View convertView);
+    }
+
+    private static class RootItem extends Item {
+        public final RootInfo root;
+
+        public RootItem(RootInfo root) {
+            super(R.layout.item_root);
+            this.root = root;
         }
 
         @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final Context context = parent.getContext();
-            if (convertView == null) {
-                convertView = LayoutInflater.from(context)
-                        .inflate(R.layout.item_root, parent, false);
-            }
-
+        public void bindView(View convertView) {
             final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
             final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
 
-            final RootInfo root = getItem(position);
+            final Context context = convertView.getContext();
             icon.setImageDrawable(root.loadIcon(context));
             title.setText(root.title);
 
-            // Device summary is always available space
-            final String summaryText;
-            if (root.rootType == Root.ROOT_TYPE_DEVICE && root.availableBytes >= 0) {
+            // Show available space if no summary
+            String summaryText = root.summary;
+            if (TextUtils.isEmpty(summaryText) && root.availableBytes >= 0) {
                 summaryText = context.getString(R.string.root_available_bytes,
                         Formatter.formatFileSize(context, root.availableBytes));
-            } else {
-                summaryText = root.summary;
             }
 
             summary.setText(summaryText);
             summary.setVisibility(TextUtils.isEmpty(summaryText) ? View.GONE : View.VISIBLE);
-
-            return convertView;
-        }
-
-        @Override
-        public View getHeaderView(View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = new Space(parent.getContext());
-            }
-            return convertView;
         }
     }
 
-    private static class AppsAdapter extends ArrayAdapter<ResolveInfo> implements SectionAdapter {
-        public AppsAdapter(Context context) {
-            super(context, 0);
+    private static class SpacerItem extends Item {
+        public SpacerItem() {
+            super(R.layout.item_root_spacer);
         }
 
         @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final Context context = parent.getContext();
-            final PackageManager pm = context.getPackageManager();
-            if (convertView == null) {
-                convertView = LayoutInflater.from(context)
-                        .inflate(R.layout.item_root, parent, false);
-            }
+        public void bindView(View convertView) {
+            // Nothing to bind
+        }
+    }
 
+    private static class AppItem extends Item {
+        public final ResolveInfo info;
+
+        public AppItem(ResolveInfo info) {
+            super(R.layout.item_root);
+            this.info = info;
+        }
+
+        @Override
+        public void bindView(View convertView) {
             final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
             final TextView summary = (TextView) convertView.findViewById(android.R.id.summary);
 
-            final ResolveInfo info = getItem(position);
+            final PackageManager pm = convertView.getContext().getPackageManager();
             icon.setImageDrawable(info.loadIcon(pm));
             title.setText(info.loadLabel(pm));
 
             // TODO: match existing summary behavior from disambig dialog
             summary.setVisibility(View.GONE);
-
-            return convertView;
-        }
-
-        @Override
-        public View getHeaderView(View convertView, ViewGroup parent) {
-            if (convertView == null) {
-                convertView = LayoutInflater.from(parent.getContext())
-                        .inflate(R.layout.item_root_header, parent, false);
-            }
-
-            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
-            title.setText(R.string.root_type_apps);
-
-            return convertView;
         }
     }
 
-    private static class SectionedRootsAdapter extends SectionedListAdapter {
-        private final RootsAdapter mRecent;
-        private final RootsAdapter mServices;
-        private final RootsAdapter mShortcuts;
-        private final RootsAdapter mDevices;
-        private final AppsAdapter mApps;
+    private static class RootsAdapter extends ArrayAdapter<Item> {
+        public RootsAdapter(Context context, Collection<RootInfo> roots, Intent includeApps) {
+            super(context, 0);
 
-        public SectionedRootsAdapter(
-                Context context, Collection<RootInfo> roots, Intent includeApps) {
-            mRecent = new RootsAdapter(context);
-            mServices = new RootsAdapter(context);
-            mShortcuts = new RootsAdapter(context);
-            mDevices = new RootsAdapter(context);
-            mApps = new AppsAdapter(context);
+            RootItem recents = null;
+            RootItem images = null;
+            RootItem videos = null;
+            RootItem audio = null;
+            RootItem downloads = null;
+
+            final List<RootInfo> clouds = Lists.newArrayList();
+            final List<RootInfo> locals = Lists.newArrayList();
 
             for (RootInfo root : roots) {
-                if (root.authority == null) {
-                    mRecent.add(root);
-                    continue;
+                if (root.isRecents()) {
+                    recents = new RootItem(root);
+                } else if (root.isExternalStorage()) {
+                    locals.add(root);
+                } else if (root.isDownloads()) {
+                    downloads = new RootItem(root);
+                } else if (root.isImages()) {
+                    images = new RootItem(root);
+                } else if (root.isVideos()) {
+                    videos = new RootItem(root);
+                } else if (root.isAudio()) {
+                    audio = new RootItem(root);
+                } else {
+                    clouds.add(root);
                 }
+            }
 
-                switch (root.rootType) {
-                    case Root.ROOT_TYPE_SERVICE:
-                        mServices.add(root);
-                        break;
-                    case Root.ROOT_TYPE_SHORTCUT:
-                        mShortcuts.add(root);
-                        break;
-                    case Root.ROOT_TYPE_DEVICE:
-                        mDevices.add(root);
-                        break;
-                }
+            final RootComparator comp = new RootComparator();
+            Collections.sort(clouds, comp);
+            Collections.sort(locals, comp);
+
+            if (recents != null) add(recents);
+
+            for (RootInfo cloud : clouds) {
+                add(new RootItem(cloud));
+            }
+
+            if (images != null) add(images);
+            if (videos != null) add(videos);
+            if (audio != null) add(audio);
+            if (downloads != null) add(downloads);
+
+            for (RootInfo local : locals) {
+                add(new RootItem(local));
             }
 
             if (includeApps != null) {
@@ -291,34 +302,53 @@
                 final List<ResolveInfo> infos = pm.queryIntentActivities(
                         includeApps, PackageManager.MATCH_DEFAULT_ONLY);
 
+                final List<AppItem> apps = Lists.newArrayList();
+
                 // Omit ourselves from the list
                 for (ResolveInfo info : infos) {
                     if (!context.getPackageName().equals(info.activityInfo.packageName)) {
-                        mApps.add(info);
+                        apps.add(new AppItem(info));
+                    }
+                }
+
+                if (apps.size() > 0) {
+                    add(new SpacerItem());
+                    for (Item item : apps) {
+                        add(item);
                     }
                 }
             }
+        }
 
-            final RootComparator comp = new RootComparator();
-            mServices.sort(comp);
-            mShortcuts.sort(comp);
-            mDevices.sort(comp);
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final Item item = getItem(position);
+            return item.getView(convertView, parent);
+        }
 
-            if (mRecent.getCount() > 0) {
-                addSection(mRecent);
+        @Override
+        public boolean areAllItemsEnabled() {
+            return false;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return getItemViewType(position) != 1;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            final Item item = getItem(position);
+            if (item instanceof RootItem || item instanceof AppItem) {
+                return 0;
+            } else {
+                return 1;
             }
-            if (mServices.getCount() > 0) {
-                addSection(mServices);
-            }
-            if (mShortcuts.getCount() > 0) {
-                addSection(mShortcuts);
-            }
-            if (mDevices.getCount() > 0) {
-                addSection(mDevices);
-            }
-            if (mApps.getCount() > 0) {
-                addSection(mApps);
-            }
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 2;
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
index 57fc7e4..1a47308 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java
@@ -213,8 +213,13 @@
                 if (DocumentsContract.isDocumentUri(this, uri)) {
                     result += "; DOC_ID";
                 }
-                getContentResolver()
-                        .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                try {
+                    getContentResolver().takePersistableUriPermission(
+                            uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                } catch (SecurityException e) {
+                    result += "; FAILED TO TAKE";
+                    Log.e(TAG, "Failed to take", e);
+                }
                 InputStream is = null;
                 try {
                     is = getContentResolver().openInputStream(uri);
@@ -222,7 +227,7 @@
                     result += "; read length=" + length;
                 } catch (Exception e) {
                     result += "; ERROR";
-                    Log.w(TAG, "Failed to read " + uri, e);
+                    Log.e(TAG, "Failed to read " + uri, e);
                 } finally {
                     IoUtils.closeQuietly(is);
                 }
@@ -235,15 +240,20 @@
                 if (DocumentsContract.isDocumentUri(this, uri)) {
                     result += "; DOC_ID";
                 }
-                getContentResolver()
-                        .takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                try {
+                    getContentResolver().takePersistableUriPermission(
+                            uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                } catch (SecurityException e) {
+                    result += "; FAILED TO TAKE";
+                    Log.e(TAG, "Failed to take", e);
+                }
                 OutputStream os = null;
                 try {
                     os = getContentResolver().openOutputStream(uri);
                     os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes());
                 } catch (Exception e) {
                     result += "; ERROR";
-                    Log.w(TAG, "Failed to write " + uri, e);
+                    Log.e(TAG, "Failed to write " + uri, e);
                 } finally {
                     IoUtils.closeQuietly(os);
                 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
index 0a378c0..28bab6c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentStack.java
@@ -71,6 +71,25 @@
         }
     }
 
+    /**
+     * Build key that uniquely identifies this stack. It omits most of the raw
+     * details included in {@link #write(DataOutputStream)}, since they change
+     * too regularly to be used as a key.
+     */
+    public String buildKey() {
+        final StringBuilder builder = new StringBuilder();
+        if (root != null) {
+            builder.append(root.authority).append('#');
+            builder.append(root.rootId).append('#');
+        } else {
+            builder.append("[null]").append('#');
+        }
+        for (DocumentInfo doc : this) {
+            builder.append(doc.documentId).append('#');
+        }
+        return builder.toString();
+    }
+
     @Override
     public void reset() {
         clear();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 014901a..e220c9e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -42,10 +42,10 @@
  */
 public class RootInfo implements Durable, Parcelable {
     private static final int VERSION_INIT = 1;
+    private static final int VERSION_DROP_TYPE = 2;
 
     public String authority;
     public String rootId;
-    public int rootType;
     public int flags;
     public int icon;
     public String title;
@@ -67,7 +67,6 @@
     public void reset() {
         authority = null;
         rootId = null;
-        rootType = 0;
         flags = 0;
         icon = 0;
         title = null;
@@ -85,10 +84,9 @@
     public void read(DataInputStream in) throws IOException {
         final int version = in.readInt();
         switch (version) {
-            case VERSION_INIT:
+            case VERSION_DROP_TYPE:
                 authority = DurableUtils.readNullableString(in);
                 rootId = DurableUtils.readNullableString(in);
-                rootType = in.readInt();
                 flags = in.readInt();
                 icon = in.readInt();
                 title = DurableUtils.readNullableString(in);
@@ -105,10 +103,9 @@
 
     @Override
     public void write(DataOutputStream out) throws IOException {
-        out.writeInt(VERSION_INIT);
+        out.writeInt(VERSION_DROP_TYPE);
         DurableUtils.writeNullableString(out, authority);
         DurableUtils.writeNullableString(out, rootId);
-        out.writeInt(rootType);
         out.writeInt(flags);
         out.writeInt(icon);
         DurableUtils.writeNullableString(out, title);
@@ -146,7 +143,6 @@
         final RootInfo root = new RootInfo();
         root.authority = authority;
         root.rootId = getCursorString(cursor, Root.COLUMN_ROOT_ID);
-        root.rootType = getCursorInt(cursor, Root.COLUMN_ROOT_TYPE);
         root.flags = getCursorInt(cursor, Root.COLUMN_FLAGS);
         root.icon = getCursorInt(cursor, Root.COLUMN_ICON);
         root.title = getCursorString(cursor, Root.COLUMN_TITLE);
@@ -162,25 +158,44 @@
         derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
 
         // TODO: remove these special case icons
-        if ("com.android.externalstorage.documents".equals(authority)) {
-            if ("documents".equals(rootId)) {
-                derivedIcon = R.drawable.ic_doc_text;
-            } else {
-                derivedIcon = R.drawable.ic_root_sdcard;
-            }
-        }
-        if ("com.android.providers.downloads.documents".equals(authority)) {
+        if (isExternalStorage()) {
+            derivedIcon = R.drawable.ic_root_sdcard;
+        } else if (isDownloads()) {
             derivedIcon = R.drawable.ic_root_download;
+        } else if (isImages()) {
+            derivedIcon = R.drawable.ic_doc_image;
+        } else if (isVideos()) {
+            derivedIcon = R.drawable.ic_doc_video;
+        } else if (isAudio()) {
+            derivedIcon = R.drawable.ic_doc_audio;
         }
-        if ("com.android.providers.media.documents".equals(authority)) {
-            if ("images_root".equals(rootId)) {
-                derivedIcon = R.drawable.ic_doc_image;
-            } else if ("videos_root".equals(rootId)) {
-                derivedIcon = R.drawable.ic_doc_video;
-            } else if ("audio_root".equals(rootId)) {
-                derivedIcon = R.drawable.ic_doc_audio;
-            }
-        }
+    }
+
+    public boolean isRecents() {
+        return authority == null && rootId == null;
+    }
+
+    public boolean isExternalStorage() {
+        return "com.android.externalstorage.documents".equals(authority);
+    }
+
+    public boolean isDownloads() {
+        return "com.android.providers.downloads.documents".equals(authority);
+    }
+
+    public boolean isImages() {
+        return "com.android.providers.media.documents".equals(authority)
+                && "images_root".equals(rootId);
+    }
+
+    public boolean isVideos() {
+        return "com.android.providers.media.documents".equals(authority)
+                && "videos_root".equals(rootId);
+    }
+
+    public boolean isAudio() {
+        return "com.android.providers.media.documents".equals(authority)
+                && "audio_root".equals(rootId);
     }
 
     @Override
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index ed28da5..9328b33 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -47,9 +47,8 @@
     // docId format: root:path/to/file
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
-            Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
-            Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
-            Root.COLUMN_AVAILABLE_BYTES,
+            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
+            Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,
     };
 
     private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
@@ -59,7 +58,6 @@
 
     private static class RootInfo {
         public String rootId;
-        public int rootType;
         public int flags;
         public String title;
         public String docId;
@@ -84,7 +82,6 @@
 
             final RootInfo root = new RootInfo();
             root.rootId = rootId;
-            root.rootType = Root.ROOT_TYPE_DEVICE;
             root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
                     | Root.FLAG_SUPPORTS_SEARCH;
             root.title = getContext().getString(R.string.root_internal_storage);
@@ -198,7 +195,6 @@
 
             final RowBuilder row = result.newRow();
             row.add(Root.COLUMN_ROOT_ID, root.rootId);
-            row.add(Root.COLUMN_ROOT_TYPE, root.rootType);
             row.add(Root.COLUMN_FLAGS, root.flags);
             row.add(Root.COLUMN_TITLE, root.title);
             row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
index e6fbb1b..5a15cd2 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/TestDocumentsProvider.java
@@ -65,7 +65,7 @@
     private static final String MY_DOC_NULL = "myNull";
 
     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
-            Root.COLUMN_ROOT_ID, Root.COLUMN_ROOT_TYPE, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
+            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
             Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
             Root.COLUMN_AVAILABLE_BYTES,
     };
@@ -114,7 +114,6 @@
         final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
         final RowBuilder row = result.newRow();
         row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID);
-        row.add(Root.COLUMN_ROOT_TYPE, Root.ROOT_TYPE_SERVICE);
         row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS);
         row.add(Root.COLUMN_TITLE, "_Test title which is really long");
         row.add(Root.COLUMN_SUMMARY,
diff --git a/packages/Keyguard/res/layout/keyguard_transport_control_view.xml b/packages/Keyguard/res/layout/keyguard_transport_control_view.xml
index 7e36f9f..801999a 100644
--- a/packages/Keyguard/res/layout/keyguard_transport_control_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_transport_control_view.xml
@@ -22,22 +22,16 @@
     android:gravity="center_horizontal"
     android:id="@+id/keyguard_transport_control">
 
-    <!-- FrameLayout used as scrim to show between album art and buttons -->
-    <FrameLayout
+    <!-- Use ImageView for its cropping features; otherwise could be android:background -->
+    <ImageView
+        android:id="@+id/albumart"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:foreground="@drawable/ic_lockscreen_player_background"
-        android:contentDescription="@string/keygaurd_accessibility_media_controls">
-        <!-- Use ImageView for its cropping features; otherwise could be android:background -->
-        <ImageView
-            android:id="@+id/albumart"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="fill"
-            android:scaleType="centerCrop"
-            android:adjustViewBounds="false"
-        />
-    </FrameLayout>
+        android:layout_gravity="fill"
+        android:scaleType="centerCrop"
+        android:adjustViewBounds="false"
+        android:contentDescription="@string/keygaurd_accessibility_media_controls" />
+
 
     <LinearLayout
         android:orientation="vertical"
diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml
index 16a3f3f..a31f708 100644
--- a/packages/Keyguard/res/values/styles.xml
+++ b/packages/Keyguard/res/values/styles.xml
@@ -22,15 +22,16 @@
     <style name="Widget.Button.NumPadKey"
             parent="@android:style/Widget.Button">
         <item name="android:singleLine">true</item>
-        <item name="android:padding">6dip</item>
         <item name="android:gravity">left|center_vertical</item>
         <item name="android:background">?android:attr/selectableItemBackground</item>
         <item name="android:textSize">34dp</item>
         <item name="android:fontFamily">sans-serif</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">#ffffff</item>
-        <item name="android:paddingBottom">10dp</item>
+        <item name="android:paddingTop">6dp</item>
+        <item name="android:paddingBottom">8dp</item>
         <item name="android:paddingLeft">20dp</item>
+        <item name="android:paddingRight">6dp</item>
     </style>
     <style name="TextAppearance.NumPadKey"
             parent="@android:style/TextAppearance">
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 07d4d1b..4da6171 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -428,10 +428,11 @@
     }
 
     private boolean widgetsDisabled() {
+        boolean disabledByLowRamDevice = ActivityManager.isLowRamDeviceStatic();
         boolean disabledByDpm =
                 (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
         boolean disabledByUser = !mLockPatternUtils.getWidgetsEnabled();
-        return disabledByDpm || disabledByUser;
+        return disabledByLowRamDevice || disabledByDpm || disabledByUser;
     }
 
     private boolean cameraDisabledByDpm() {
@@ -1483,6 +1484,7 @@
             if (DEBUGXPORT) Log.v(TAG, "remove transport");
             mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
             mTransportControl = null;
+            KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null);
         }
     }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
index 3208aff..2a5f979 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java
@@ -36,6 +36,7 @@
 import android.text.TextUtils;
 import android.text.style.ForegroundColorSpan;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
@@ -98,11 +99,9 @@
 
             case MSG_SET_ARTWORK:
                 if (mClientGeneration == msg.arg1) {
-                    if (mMetadata.bitmap != null) {
-                        mMetadata.bitmap.recycle();
-                    }
                     mMetadata.bitmap = (Bitmap) msg.obj;
-                    mAlbumArt.setImageBitmap(mMetadata.bitmap);
+                    KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(
+                            mMetadata.bitmap);
                 }
                 break;
 
@@ -223,7 +222,8 @@
     @Override
     protected void onSizeChanged (int w, int h, int oldw, int oldh) {
         if (mAttached) {
-            int dim = Math.min(512, Math.max(w, h));
+            final DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
+            int dim = Math.max(dm.widthPixels, dm.heightPixels);
             if (DEBUG) Log.v(TAG, "TCV uses bitmap size=" + dim);
             mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim);
         }
@@ -300,7 +300,8 @@
                     Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         }
 
-        mAlbumArt.setImageBitmap(mMetadata.bitmap);
+        KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(
+                mMetadata.bitmap);
         final int flags = mTransportControlFlags;
         setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS);
         setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f4bbf9a..734f517 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -249,11 +249,11 @@
             if (Intent.ACTION_TIME_TICK.equals(action)
                     || Intent.ACTION_TIME_CHANGED.equals(action)
                     || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_TIME_UPDATE));
+                mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
             } else if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
                 mTelephonyPlmn = getTelephonyPlmnFrom(intent);
                 mTelephonySpn = getTelephonySpnFrom(intent);
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_CARRIER_INFO_UPDATE));
+                mHandler.sendEmptyMessage(MSG_CARRIER_INFO_UPDATE);
             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
                 final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
                 final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
@@ -277,12 +277,12 @@
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
             } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
                     .equals(action)) {
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED));
+                mHandler.sendEmptyMessage(MSG_DPM_STATE_CHANGED);
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED,
                        intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
             } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_BOOT_COMPLETED));
+                dispatchBootCompleted();
             }
         }
     };
@@ -407,6 +407,20 @@
         return sInstance;
     }
 
+    /**
+     * IMPORTANT: Must be called from UI thread.
+     */
+    public void dispatchSetBackground(Bitmap bmp) {
+        if (DEBUG) Log.d(TAG, "dispatchSetBackground");
+        final int count = mCallbacks.size();
+        for (int i = 0; i < count; i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onSetBackground(bmp);
+            }
+        }
+    }
+
     protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) {
         mDisplayClientState.clientGeneration = clientGeneration;
         mDisplayClientState.clearing = clearing;
@@ -520,7 +534,7 @@
                 super.onChange(selfChange);
                 mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
                 if (mDeviceProvisioned) {
-                    mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED));
+                    mHandler.sendEmptyMessage(MSG_DEVICE_PROVISIONED);
                 }
                 if (DEBUG) Log.d(TAG, "DEVICE_PROVISIONED state = " + mDeviceProvisioned);
             }
@@ -536,7 +550,7 @@
         if (provisioned != mDeviceProvisioned) {
             mDeviceProvisioned = provisioned;
             if (mDeviceProvisioned) {
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED));
+                mHandler.sendEmptyMessage(MSG_DEVICE_PROVISIONED);
             }
         }
     }
@@ -582,6 +596,18 @@
     }
 
     /**
+     * This is exposed since {@link Intent#ACTION_BOOT_COMPLETED} is not sticky. If
+     * keyguard crashes sometime after boot, then it will never receive this
+     * broadcast and hence not handle the event. This method is ultimately called by
+     * PhoneWindowManager in this case.
+     */
+    protected void dispatchBootCompleted() {
+        if (!mBootCompleted) {
+            mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
+        }
+    }
+
+    /**
      * Handle {@link #MSG_BOOT_COMPLETED}
      */
     protected void handleBootCompleted() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index b0511e5..e6dddab 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -17,6 +17,7 @@
 
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
+import android.graphics.Bitmap;
 import android.media.AudioManager;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -135,4 +136,8 @@
      * Called when the emergency call button is pressed.
      */
     void onEmergencyCallAction() { }
+
+    public void onSetBackground(Bitmap bitmap) {
+        // THIS SPACE FOR RENT
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index b96ef88..2084a16 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -16,6 +16,9 @@
 
 package com.android.keyguard;
 
+import android.app.PendingIntent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import com.android.internal.policy.IKeyguardShowCallback;
 import com.android.internal.widget.LockPatternUtils;
 
@@ -77,6 +80,14 @@
     private boolean mScreenOn = false;
     private LockPatternUtils mLockPatternUtils;
 
+    private KeyguardUpdateMonitorCallback mBackgroundChanger = new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onSetBackground(Bitmap bmp) {
+            mKeyguardHost.setCustomBackground(bmp != null ?
+                    new BitmapDrawable(mContext.getResources(), bmp) : null);
+        }
+    };
+
     public interface ShowListener {
         void onShown(IBinder windowToken);
     };
@@ -140,11 +151,25 @@
     class ViewManagerHost extends FrameLayout {
         private static final int BACKGROUND_COLOR = 0x70000000;
 
+        private Drawable mCustomBackground;
+
         // This is a faster way to draw the background on devices without hardware acceleration
         private final Drawable mBackgroundDrawable = new Drawable() {
             @Override
             public void draw(Canvas canvas) {
-                canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC);
+                if (mCustomBackground != null) {
+                    final Rect bounds = mCustomBackground.getBounds();
+                    final int vWidth = getWidth();
+                    final int vHeight = getHeight();
+
+                    final int restore = canvas.save();
+                    canvas.translate(-(bounds.width() - vWidth) / 2,
+                            -(bounds.height() - vHeight) / 2);
+                    mCustomBackground.draw(canvas);
+                    canvas.restoreToCount(restore);
+                } else {
+                    canvas.drawColor(BACKGROUND_COLOR, PorterDuff.Mode.SRC);
+                }
             }
 
             @Override
@@ -166,6 +191,40 @@
             setBackground(mBackgroundDrawable);
         }
 
+        public void setCustomBackground(Drawable d) {
+            mCustomBackground = d;
+            if (d != null) {
+                d.setColorFilter(BACKGROUND_COLOR, PorterDuff.Mode.SRC_OVER);
+            }
+            computeCustomBackgroundBounds();
+            invalidate();
+        }
+
+        private void computeCustomBackgroundBounds() {
+            if (mCustomBackground == null) return; // Nothing to do
+            if (!isLaidOut()) return; // We'll do this later
+
+            final int bgWidth = mCustomBackground.getIntrinsicWidth();
+            final int bgHeight = mCustomBackground.getIntrinsicHeight();
+            final int vWidth = getWidth();
+            final int vHeight = getHeight();
+
+            final float bgAspect = (float) bgWidth / bgHeight;
+            final float vAspect = (float) vWidth / vHeight;
+
+            if (bgAspect > vAspect) {
+                mCustomBackground.setBounds(0, 0, (int) (vHeight * bgAspect), vHeight);
+            } else {
+                mCustomBackground.setBounds(0, 0, vWidth, (int) (vWidth / bgAspect));
+            }
+        }
+
+        @Override
+        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+            super.onSizeChanged(w, h, oldw, oldh);
+            computeCustomBackgroundBounds();
+        }
+
         @Override
         protected void onConfigurationChanged(Configuration newConfig) {
             super.onConfigurationChanged(newConfig);
@@ -239,9 +298,12 @@
             lp.setTitle("Keyguard");
             mWindowLayoutParams = lp;
             mViewManager.addView(mKeyguardHost, lp);
+
+            KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mBackgroundChanger);
         }
 
         if (force || mKeyguardView == null) {
+            mKeyguardHost.setCustomBackground(null);
             mKeyguardHost.removeAllViews();
             inflateKeyguardView(options);
             mKeyguardView.requestFocus();
@@ -428,6 +490,8 @@
                     public void run() {
                         synchronized (KeyguardViewManager.this) {
                             lastView.cleanUp();
+                            // Let go of any large bitmaps.
+                            mKeyguardHost.setCustomBackground(null);
                             mKeyguardHost.removeView(lastView);
                         }
                     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index 0606d83..ec3eb157 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -16,6 +16,7 @@
 
 package com.android.keyguard;
 
+import android.graphics.Bitmap;
 import com.android.internal.policy.IKeyguardExitCallback;
 import com.android.internal.policy.IKeyguardShowCallback;
 import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
@@ -528,6 +529,9 @@
             mSystemReady = true;
             mUpdateMonitor.registerCallback(mUpdateCallback);
 
+            // Send boot completed message if it hasn't already been sent.
+            mUpdateMonitor.dispatchBootCompleted();
+
             // Suppress biometric unlock right after boot until things have settled if it is the
             // selected security method, otherwise unsuppress it.  It must be unsuppressed if it is
             // not the selected security method for the following reason:  if the user starts
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 9fe7e00..5ee8d8c 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -109,6 +109,9 @@
     <!-- Label for an unknown reason for failed or blocked print job. [CHAR LIMIT=25] -->
     <string name="reason_unknown">unknown</string>
 
+    <!-- Label for a printer that is not available. [CHAR LIMIT=25] -->
+    <string name="printer_unavailable"><xliff:g id="print_job_name" example="Canon-123GHT">%1$s</xliff:g> &#8211; unavailable</string>
+
     <!-- Arrays -->
 
     <!-- Color mode labels. -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
index 3a1a3c4..65af830 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
@@ -19,13 +19,16 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Loader;
+import android.content.pm.ServiceInfo;
 import android.os.AsyncTask;
 import android.print.PrintManager;
 import android.print.PrinterDiscoverySession;
 import android.print.PrinterDiscoverySession.OnPrintersChangeListener;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
+import android.printservice.PrintServiceInfo;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
@@ -46,6 +49,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import libcore.io.IoUtils;
 
@@ -384,6 +388,30 @@
                             + FusedPrintersProvider.this.hashCode());
                 }
 
+                // Ignore printer records whose target services are not installed.
+                PrintManager printManager = (PrintManager) getContext()
+                        .getSystemService(Context.PRINT_SERVICE);
+                List<PrintServiceInfo> services = printManager
+                        .getInstalledPrintServices();
+
+                Set<ComponentName> installedComponents = new ArraySet<ComponentName>();
+                final int installedServiceCount = services.size();
+                for (int i = 0; i < installedServiceCount; i++) {
+                    ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
+                    ComponentName componentName = new ComponentName(
+                            serviceInfo.packageName, serviceInfo.name);
+                    installedComponents.add(componentName);
+                }
+
+                final int printerCount = printers.size();
+                for (int i = printerCount - 1; i >= 0; i--) {
+                    ComponentName printerServiceName = printers.get(i).getId().getServiceName();
+                    if (!installedComponents.contains(printerServiceName.getPackageName())) {
+                        printers.remove(i);
+                    }
+                }
+
+                // Store the filtered list.
                 mHistoricalPrinters = printers;
 
                 // Compute the favorite printers.
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index b6ef7b1..44362d4 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -819,8 +819,6 @@
 
         private PrinterInfo mCurrentPrinter;
 
-        private boolean mRequestedCurrentPrinterRefresh;
-
         private final OnItemSelectedListener mOnItemSelectedListener =
                 new AdapterView.OnItemSelectedListener() {
             @Override
@@ -840,7 +838,7 @@
                         return;
                     }
 
-                    mRequestedCurrentPrinterRefresh = false;
+                    mCapabilitiesTimeout.remove();
 
                     mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter
                             .getItem(position);
@@ -855,8 +853,7 @@
 
                     PrinterCapabilitiesInfo capabilities = mCurrentPrinter.getCapabilities();
                     if (capabilities == null) {
-                        // TODO: We need a timeout for the update.
-                        mRequestedCurrentPrinterRefresh = true;
+                        mCapabilitiesTimeout.post();
                         updateUi();
                         refreshCurrentPrinter();
                     } else {
@@ -1129,6 +1126,9 @@
             }
         };
 
+        private final WaitForPrinterCapabilitiesTimeout mCapabilitiesTimeout =
+                new WaitForPrinterCapabilitiesTimeout();
+
         private int mEditorState;
 
         private boolean mIgnoreNextDestinationChange;
@@ -1174,16 +1174,16 @@
                                 if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE
                                         && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE
                                         && printer.getCapabilities() == null
-                                        && !mRequestedCurrentPrinterRefresh) {
-                                    mRequestedCurrentPrinterRefresh = true;
+                                        && !mCapabilitiesTimeout.isPosted()) {
+                                    mCapabilitiesTimeout.post();
                                     refreshCurrentPrinter();
                                     return;
                                 }
 
                                 // We just refreshed the current printer.
                                 if (printer.getCapabilities() != null
-                                        && mRequestedCurrentPrinterRefresh) {
-                                    mRequestedCurrentPrinterRefresh = false;
+                                        && mCapabilitiesTimeout.isPosted()) {
+                                    mCapabilitiesTimeout.remove();
                                     updatePrintAttributes(printer.getCapabilities());
                                     updateUi();
                                     mController.update();
@@ -1972,6 +1972,43 @@
             }
         }
 
+        private final class WaitForPrinterCapabilitiesTimeout implements Runnable {
+            private static final long GET_CAPABILITIES_TIMEOUT_MILLIS = 10000; // 10sec
+
+            private boolean mIsPosted;
+
+            public void post() {
+                if (!mIsPosted) {
+                    mDestinationSpinner.postDelayed(this,
+                            GET_CAPABILITIES_TIMEOUT_MILLIS);
+                    mIsPosted = true;
+                }
+            }
+
+            public void remove() {
+                if (mIsPosted) {
+                    mIsPosted = false;
+                    mDestinationSpinner.removeCallbacks(this);
+                }
+            }
+
+            public boolean isPosted() {
+                return mIsPosted;
+            }
+
+            @Override
+            public void run() {
+                mIsPosted = false;
+                if (mDestinationSpinner.getSelectedItemPosition() >= 0) {
+                    View itemView = mDestinationSpinner.getSelectedView();
+                    TextView titleView = (TextView) itemView.findViewById(R.id.title);
+                    String title = getString(R.string.printer_unavailable,
+                            mCurrentPrinter.getName());
+                    titleView.setText(title);
+                }
+            }
+        }
+
         private final class DestinationAdapter extends BaseAdapter
                 implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>{
             private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 62b35fe..87181f7 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -43,6 +43,7 @@
 import android.print.PrinterId;
 import android.print.PrinterInfo;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
@@ -59,10 +60,12 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -82,6 +85,8 @@
 
     private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
 
+    private static final String PRINT_JOB_FILE_PREFIX = "print_job_";
+
     private static final String PRINT_FILE_EXTENSION = "pdf";
 
     private static final Object sLock = new Object();
@@ -168,9 +173,9 @@
                         PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT
                         | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
 
-                Message message = mHandlerCaller.obtainMessageIIO(
+                Message message = mHandlerCaller.obtainMessageO(
                         HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
-                        printJob.getAppId(), 0, printJob.getId());
+                        printJob);
                 mHandlerCaller.executeOrSendMessage(message);
 
                 message = mHandlerCaller.obtainMessageOO(
@@ -179,9 +184,6 @@
                 mHandlerCaller.executeOrSendMessage(message);
 
                 printJob.setCreationTime(System.currentTimeMillis());
-                synchronized (mLock) {
-                    mPersistanceManager.writeStateLocked();
-                }
             }
 
             @Override
@@ -225,12 +227,40 @@
             }
 
             @Override
-            public void forgetPrintJobs(List<PrintJobId> printJobIds) {
-                PrintSpoolerService.this.forgetPrintJobs(printJobIds);
+            protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+                PrintSpoolerService.this.dump(fd, writer, args);
             }
         };
     }
 
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        synchronized (mLock) {
+            String prefix = args[0];
+            String tab = "  ";
+
+            pw.append(prefix).append("print jobs:").println();
+            final int printJobCount = mPrintJobs.size();
+            for (int i = 0; i < printJobCount; i++) {
+                PrintJobInfo printJob = mPrintJobs.get(i);
+                pw.append(prefix).append(tab).append(printJob.toString());
+                pw.println();
+            }
+
+            pw.append(prefix).append("print job files:").println();
+            File[] files = getFilesDir().listFiles();
+            if (files != null) {
+                final int fileCount = files.length;
+                for (int i = 0; i < fileCount; i++) {
+                    File file = files[i];
+                    if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
+                        pw.append(prefix).append(tab).append(file.getName()).println();
+                    }
+                }
+            }
+        }
+    }
+
     private void sendOnPrintJobQueued(PrintJobInfo printJob) {
         Message message = mHandlerCaller.obtainMessageO(
                 HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
@@ -324,10 +354,9 @@
 
                 case MSG_ON_PRINT_JOB_STATE_CHANGED: {
                     if (mClient != null) {
-                        PrintJobId printJobId = (PrintJobId) message.obj;
-                        final int appId = message.arg1;
+                        PrintJobInfo printJob = (PrintJobInfo) message.obj;
                         try {
-                            mClient.onPrintJobStateChanged(printJobId, appId);
+                            mClient.onPrintJobStateChanged(printJob);
                         } catch (RemoteException re) {
                             Slog.e(LOG_TAG, "Error notify for print job state change.", re);
                         }
@@ -391,17 +420,46 @@
     public void createPrintJob(PrintJobInfo printJob) {
         synchronized (mLock) {
             addPrintJobLocked(printJob);
+            setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
         }
     }
 
     private void handleReadPrintJobsLocked() {
+        // Make a map with the files for a print job since we may have
+        // to delete some. One example of getting orphan files if the
+        // spooler crashes while constructing a print job. We do not
+        // persist partially populated print jobs under construction to
+        // avoid special handling for various attributes missing.
+        ArrayMap<PrintJobId, File> fileForJobMap = null;
+        File[] files = getFilesDir().listFiles();
+        if (files != null) {
+            final int fileCount = files.length;
+            for (int i = 0; i < fileCount; i++) {
+                File file = files[i];
+                if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
+                    if (fileForJobMap == null) {
+                        fileForJobMap = new ArrayMap<PrintJobId, File>();
+                    }
+                    String printJobIdString = file.getName().substring(0,
+                            PRINT_JOB_FILE_PREFIX.length());
+                    PrintJobId printJobId = PrintJobId.unflattenFromString(
+                            printJobIdString);
+                    fileForJobMap.put(printJobId, file);
+                }
+            }
+        }
+
         final int printJobCount = mPrintJobs.size();
         for (int i = 0; i < printJobCount; i++) {
             PrintJobInfo printJob = mPrintJobs.get(i);
 
+            // We want to have only the orphan files at the end.
+            if (fileForJobMap != null) {
+                fileForJobMap.remove(printJob.getId());
+            }
+
             // Update the notification.
             mNotificationController.onPrintJobStateChanged(printJob);
-
             switch (printJob.getState()) {
                 case PrintJobInfo.STATE_QUEUED:
                 case PrintJobInfo.STATE_STARTED:
@@ -415,6 +473,15 @@
                 } break;
             }
         }
+
+        // Delete the orphan files.
+        if (fileForJobMap != null) {
+            final int orphanFileCount = fileForJobMap.size();
+            for (int i = 0; i < orphanFileCount; i++) {
+                File file = fileForJobMap.valueAt(i);
+                file.delete();
+            }
+        }
     }
 
     public void checkAllPrintJobsHandled() {
@@ -465,7 +532,7 @@
     }
 
     public File generateFileForPrintJob(PrintJobId printJobId) {
-        return new File(getFilesDir(), "print_job_"
+        return new File(getFilesDir(), PRINT_JOB_FILE_PREFIX
                 + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
     }
 
@@ -476,31 +543,6 @@
         }
     }
 
-    private void forgetPrintJobs(List<PrintJobId> printJobIds) {
-        synchronized (mLock) {
-            boolean printJobsRemoved = false;
-            final int removedPrintJobCount = printJobIds.size();
-            for (int i = 0; i < removedPrintJobCount; i++) {
-                PrintJobId removedPrintJobId = printJobIds.get(i);
-                final int printJobCount = mPrintJobs.size();
-                for (int j = printJobCount - 1; j >= 0; j--) {
-                    PrintJobInfo printJob = mPrintJobs.get(j);
-                    if (removedPrintJobId.equals(printJob.getId())) {
-                        mPrintJobs.remove(j);
-                        printJobsRemoved = true;
-                        if (DEBUG_PRINT_JOB_LIFECYCLE) {
-                            Slog.i(LOG_TAG, "[FORGOT] " + printJob.getId().flattenToString());
-                        }
-                        removePrintJobFileLocked(printJob.getId());
-                    }
-                }
-            }
-            if (printJobsRemoved) {
-                mPersistanceManager.writeStateLocked();
-            }
-        }
-    }
-
     private void removeObsoletePrintJobs() {
         synchronized (mLock) {
             final int printJobCount = mPrintJobs.size();
@@ -523,7 +565,7 @@
         if (file.exists()) {
             file.delete();
             if (DEBUG_PRINT_JOB_LIFECYCLE) {
-                Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId.flattenToString());
+                Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
             }
         }
     }
@@ -552,10 +594,7 @@
                 switch (state) {
                     case PrintJobInfo.STATE_COMPLETED:
                     case PrintJobInfo.STATE_CANCELED:
-                        // Just remove the file but keep the print job info since
-                        // the app that created it may be holding onto the PrintJob
-                        // instance and query it for its most recent state. We will
-                        // remove the info for this job when told so by the system.
+                        mPrintJobs.remove(printJob);
                         removePrintJobFileLocked(printJob.getId());
                         // $fall-through$
 
@@ -582,9 +621,9 @@
                     notifyOnAllPrintJobsHandled();
                 }
 
-                Message message = mHandlerCaller.obtainMessageIIO(
+                Message message = mHandlerCaller.obtainMessageO(
                         HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
-                        printJob.getAppId(), 0, printJob.getId());
+                        printJob);
                 mHandlerCaller.executeOrSendMessage(message);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index c32f741..c7f0e17 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarPanel;
+import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
 public class SearchPanelView extends FrameLayout implements
@@ -88,11 +89,7 @@
 
         if (isKeyguardShowing) {
             // Have keyguard show the bouncer and launch the activity if the user succeeds.
-            try {
-                mWm.showAssistant();
-            } catch (RemoteException e) {
-                // too bad, so sad...
-            }
+            KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
             onAnimationStarted();
         } else {
             // Otherwise, keyguard isn't showing so launch it from here.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 49777d4..932fe20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -123,7 +123,7 @@
 
     protected int mCurrentUserId = 0;
 
-    protected int mLayoutDirection;
+    protected int mLayoutDirection = -1; // invalid
     private Locale mLocale;
     protected boolean mUseHeadsUp = false;
 
@@ -299,8 +299,6 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         mContext.registerReceiver(mBroadcastReceiver, filter);
-
-        mLocale = mContext.getResources().getConfiguration().locale;
     }
 
     public void userSwitched(int newUserId) {
@@ -320,11 +318,17 @@
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
-        final Locale newLocale = mContext.getResources().getConfiguration().locale;
-        if (! newLocale.equals(mLocale)) {
-            mLocale = newLocale;
-            mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
-            refreshLayout(mLayoutDirection);
+        final Locale locale = mContext.getResources().getConfiguration().locale;
+        final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
+        if (! locale.equals(mLocale) || ld != mLayoutDirection) {
+            if (DEBUG) {
+                Log.v(TAG, String.format(
+                        "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
+                        locale, ld));
+            }
+            mLocale = locale;
+            mLayoutDirection = ld;
+            refreshLayout(ld);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
index ecf7b35..16fe1aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SystemBars.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import android.content.res.Configuration;
 import android.provider.Settings;
 import android.util.Log;
 
@@ -70,6 +71,13 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        if (mStatusBar != null) {
+            mStatusBar.onConfigurationChanged(newConfig);
+        }
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mStatusBar != null) {
             mStatusBar.dump(fd, pw, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
index 1221a55..5c55f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
@@ -23,7 +23,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.util.Log;
+import android.util.Slog;
 import android.view.MotionEvent;
 
 import com.android.internal.policy.IKeyguardExitCallback;
@@ -41,7 +41,9 @@
     static final String KEYGUARD_PACKAGE = "com.android.keyguard";
     static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService";
 
-    IKeyguardService mService;
+    private static KeyguardTouchDelegate sInstance;
+
+    private volatile IKeyguardService mService;
 
     protected static final boolean DEBUG = false;
     protected static final String TAG = "KeyguardTouchDelegate";
@@ -49,83 +51,121 @@
     private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            Log.v(TAG, "Connected to keyguard");
+            Slog.v(TAG, "Connected to keyguard");
             mService = IKeyguardService.Stub.asInterface(service);
 
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            Log.v(TAG, "Disconnected from keyguard");
+            Slog.v(TAG, "Disconnected from keyguard");
             mService = null;
+            sInstance = null; // force reconnection if this goes away
         }
 
     };
 
-    public KeyguardTouchDelegate(Context context) {
+    private KeyguardTouchDelegate(Context context) {
         Intent intent = new Intent();
         intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS);
         if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                 Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
-            if (DEBUG) Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS);
+            if (DEBUG) Slog.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS);
         } else {
-            if (DEBUG) Log.v(TAG, "*** Keyguard started");
+            if (DEBUG) Slog.v(TAG, "*** Keyguard started");
         }
     }
 
+    public static KeyguardTouchDelegate getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new KeyguardTouchDelegate(context);
+        }
+        return sInstance;
+    }
+
     public boolean isSecure() {
-        boolean secure = false;
-        if (mService != null) {
+        final IKeyguardService service = mService;
+        if (service != null) {
             try {
-                secure = mService.isSecure();
+                return service.isSecure();
             } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException calling keyguard.isSecure()!", e);
+                Slog.e(TAG, "RemoteException calling keyguard.isSecure()!", e);
             }
         } else {
-            Log.w(TAG, "isSecure(): NO SERVICE!");
+            Slog.w(TAG, "isSecure(): NO SERVICE!");
         }
-        return secure;
+        return false;
     }
 
     public boolean dispatch(MotionEvent event) {
-        if (mService != null) {
+        final IKeyguardService service = mService;
+        if (service != null) {
             try {
-                mService.dispatch(event);
+                service.dispatch(event);
+                return true;
             } catch (RemoteException e) {
                 // What to do?
-                Log.e(TAG, "RemoteException sending event to keyguard!", e);
-                return false;
+                Slog.e(TAG, "RemoteException sending event to keyguard!", e);
             }
-            return true;
         } else {
-            Log.w(TAG, "dispatch(event): NO SERVICE!");
+            Slog.w(TAG, "dispatch(event): NO SERVICE!");
+        }
+        return false;
+    }
+
+    public boolean isInputRestricted() {
+        final IKeyguardService service = mService;
+        if (service != null) {
+            try {
+                return service.isInputRestricted();
+            } catch (RemoteException e) {
+                Slog.w(TAG , "Remote Exception", e);
+            }
+        } else {
+            Slog.w(TAG, "isInputRestricted(): NO SERVICE!");
+        }
+        return false;
+    }
+
+    public boolean isShowingAndNotHidden() {
+        final IKeyguardService service = mService;
+        if (service != null) {
+            try {
+                return service.isShowingAndNotHidden();
+            } catch (RemoteException e) {
+                Slog.w(TAG , "Remote Exception", e);
+            }
+        } else {
+            Slog.w(TAG, "isShowingAndNotHidden(): NO SERVICE!");
         }
         return false;
     }
 
     public void showAssistant() {
-        if (mService != null) {
+        final IKeyguardService service = mService;
+        if (service != null) {
             try {
-                mService.showAssistant();
+                service.showAssistant();
             } catch (RemoteException e) {
                 // What to do?
-                Log.e(TAG, "RemoteException launching assistant!", e);
+                Slog.e(TAG, "RemoteException launching assistant!", e);
             }
         } else {
-            Log.w(TAG, "dispatch(event): NO SERVICE!");
+            Slog.w(TAG, "showAssistant(event): NO SERVICE!");
         }
     }
 
     public void launchCamera() {
-        if (mService != null) {
+        final IKeyguardService service = mService;
+        if (service != null) {
             try {
-                mService.launchCamera();
+                service.launchCamera();
             } catch (RemoteException e) {
                 // What to do?
-                Log.e(TAG, "RemoteException launching camera!", e);
+                Slog.e(TAG, "RemoteException launching camera!", e);
             }
         } else {
-            Log.w(TAG, "dispatch(event): NO SERVICE!");
+            Slog.w(TAG, "dispatch(event): NO SERVICE!");
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 596fac6..04885f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -88,7 +88,6 @@
 
     // used to disable the camera icon in navbar when disabled by DPM
     private boolean mCameraDisabledByDpm;
-    KeyguardTouchDelegate mKeyguardTouchDelegate;
 
     private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
         @Override
@@ -112,7 +111,7 @@
                     }
                     break;
             }
-            return mKeyguardTouchDelegate.dispatch(event);
+            return KeyguardTouchDelegate.getInstance(getContext()).dispatch(event);
         }
     };
 
@@ -155,8 +154,6 @@
 
         mBarTransitions = new NavigationBarTransitions(this);
 
-        mKeyguardTouchDelegate = new KeyguardTouchDelegate(mContext);
-
         mCameraDisabledByDpm = isCameraDisabledByDpm();
         watchForDevicePolicyChanges();
     }
@@ -341,7 +338,7 @@
                 final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
                 final  boolean disabledBecauseKeyguardSecure =
                         (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
-                        && mKeyguardTouchDelegate.isSecure();
+                        && KeyguardTouchDelegate.getInstance(getContext()).isSecure();
                 return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
             } catch (RemoteException e) {
                 Log.e(TAG, "Can't get userId", e);
@@ -426,9 +423,9 @@
 
     protected void launchForAccessibilityClick(View v) {
         if (v == getCameraButton()) {
-            mKeyguardTouchDelegate.launchCamera();
+            KeyguardTouchDelegate.getInstance(getContext()).launchCamera();
         } else if (v == getSearchLight()) {
-            mKeyguardTouchDelegate.showAssistant();
+            KeyguardTouchDelegate.getInstance(getContext()).showAssistant();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index fbc94b1..c47d0eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -38,6 +38,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Canvas;
@@ -633,7 +634,6 @@
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(Intent.ACTION_SCREEN_ON);
@@ -2433,17 +2433,6 @@
                 notifyNavigationBarScreenOn(false);
                 notifyHeadsUpScreenOn(false);
             }
-            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
-                if (DEBUG) {
-                    Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
-                }
-                mDisplay.getSize(mCurrentDisplaySize);
-
-                updateResources();
-                repositionNavigationBar();
-                updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-                updateShowSearchHoldoff();
-            }
             else if (Intent.ACTION_SCREEN_ON.equals(action)) {
                 mScreenOn = true;
                 // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
@@ -2466,6 +2455,22 @@
         }
     };
 
+    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig); // calls refreshLayout
+
+        if (DEBUG) {
+            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
+        }
+        mDisplay.getSize(mCurrentDisplaySize);
+
+        updateResources();
+        repositionNavigationBar();
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+        updateShowSearchHoldoff();
+    }
+
     @Override
     public void userSwitched(int newUserId) {
         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9f9b6d6..a5fd1d7 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -5127,11 +5127,6 @@
     }
 
     @Override
-    public void showAssistant() {
-        mKeyguardDelegate.showAssistant();
-    }
-
-    @Override
     public boolean canMagnifyWindow(int windowType) {
         switch (windowType) {
             case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
index 5e299ee..83be1a8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
@@ -181,11 +181,7 @@
     }
 
     public void showAssistant() {
-        try {
-            mService.showAssistant();
-        } catch (RemoteException e) {
-            Slog.w(TAG , "Remote Exception", e);
-        }
+        // Not used by PhoneWindowManager
     }
 
     public void dispatch(MotionEvent event) {
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 2145b76..3a3dfd5 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -47,6 +47,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.RegisteredServicesCache;
 import android.content.pm.RegisteredServicesCacheListener;
+import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
@@ -580,15 +581,18 @@
         try {
             new Session(fromAccounts, null, account.type, false,
                     false /* stripAuthTokenFromResult */) {
+                @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
                             + ", " + account.type;
                 }
 
+                @Override
                 public void run() throws RemoteException {
                     mAuthenticator.getAccountCredentialsForCloning(this, account);
                 }
 
+                @Override
                 public void onResult(Bundle result) {
                     if (result != null) {
                         if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
@@ -613,11 +617,13 @@
         try {
             new Session(targetUser, null, account.type, false,
                     false /* stripAuthTokenFromResult */) {
+                @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
                             + ", " + account.type;
                 }
 
+                @Override
                 public void run() throws RemoteException {
                     // Confirm that the owner's account still exists before this step.
                     UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER);
@@ -632,6 +638,7 @@
                     }
                 }
 
+                @Override
                 public void onResult(Bundle result) {
                     if (result != null) {
                         if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
@@ -646,6 +653,7 @@
                     }
                 }
 
+                @Override
                 public void onError(int errorCode, String errorMessage) {
                     super.onError(errorCode,  errorMessage);
                     // TODO: Show error notification to user
@@ -774,6 +782,7 @@
             mAccount = account;
         }
 
+        @Override
         public void run() throws RemoteException {
             try {
                 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
@@ -782,6 +791,7 @@
             }
         }
 
+        @Override
         public void onResult(Bundle result) {
             IAccountManagerResponse response = getResponseAndClose();
             if (response != null) {
@@ -807,6 +817,7 @@
             }
         }
 
+        @Override
         protected String toDebugString(long now) {
             return super.toDebugString(now) + ", hasFeatures"
                     + ", " + mAccount
@@ -863,15 +874,18 @@
             mAccount = account;
         }
 
+        @Override
         protected String toDebugString(long now) {
             return super.toDebugString(now) + ", removeAccount"
                     + ", account " + mAccount;
         }
 
+        @Override
         public void run() throws RemoteException {
             mAuthenticator.getAccountRemovalAllowed(this, mAccount);
         }
 
+        @Override
         public void onResult(Bundle result) {
             if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
                     && !result.containsKey(AccountManager.KEY_INTENT)) {
@@ -1212,16 +1226,19 @@
         try {
             new Session(accounts, response, accountType, false,
                     false /* stripAuthTokenFromResult */) {
+                @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAuthTokenLabel"
                             + ", " + accountType
                             + ", authTokenType " + authTokenType;
                 }
 
+                @Override
                 public void run() throws RemoteException {
                     mAuthenticator.getAuthTokenLabel(this, authTokenType);
                 }
 
+                @Override
                 public void onResult(Bundle result) {
                     if (result != null) {
                         String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
@@ -1299,6 +1316,7 @@
 
             new Session(accounts, response, account.type, expectActivityLaunch,
                     false /* stripAuthTokenFromResult */) {
+                @Override
                 protected String toDebugString(long now) {
                     if (loginOptions != null) loginOptions.keySet();
                     return super.toDebugString(now) + ", getAuthToken"
@@ -1308,6 +1326,7 @@
                             + ", notifyOnAuthFailure " + notifyOnAuthFailure;
                 }
 
+                @Override
                 public void run() throws RemoteException {
                     // If the caller doesn't have permission then create and return the
                     // "grant permission" intent instead of the "getAuthToken" intent.
@@ -1318,6 +1337,7 @@
                     }
                 }
 
+                @Override
                 public void onResult(Bundle result) {
                     if (result != null) {
                         if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
@@ -1482,11 +1502,13 @@
         try {
             new Session(accounts, response, accountType, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */) {
+                @Override
                 public void run() throws RemoteException {
                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
                             options);
                 }
 
+                @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", addAccount"
                             + ", accountType " + accountType
@@ -1530,9 +1552,11 @@
         try {
             new Session(accounts, response, account.type, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */) {
+                @Override
                 public void run() throws RemoteException {
                     mAuthenticator.confirmCredentials(this, account, options);
                 }
+                @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", confirmCredentials"
                             + ", " + account;
@@ -1563,9 +1587,11 @@
         try {
             new Session(accounts, response, account.type, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */) {
+                @Override
                 public void run() throws RemoteException {
                     mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
                 }
+                @Override
                 protected String toDebugString(long now) {
                     if (loginOptions != null) loginOptions.keySet();
                     return super.toDebugString(now) + ", updateCredentials"
@@ -1596,9 +1622,11 @@
         try {
             new Session(accounts, response, accountType, expectActivityLaunch,
                     true /* stripAuthTokenFromResult */) {
+                @Override
                 public void run() throws RemoteException {
                     mAuthenticator.editProperties(this, mAccountType);
                 }
+                @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", editProperties"
                             + ", accountType " + accountType;
@@ -1624,6 +1652,7 @@
             mFeatures = features;
         }
 
+        @Override
         public void run() throws RemoteException {
             synchronized (mAccounts.cacheLock) {
                 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
@@ -1661,6 +1690,7 @@
             }
         }
 
+        @Override
         public void onResult(Bundle result) {
             mNumResults++;
             if (result == null) {
@@ -1699,6 +1729,7 @@
         }
 
 
+        @Override
         protected String toDebugString(long now) {
             return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
                     + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
@@ -2111,9 +2142,31 @@
             }
         }
 
+        @Override
         public void onResult(Bundle result) {
             mNumResults++;
-            if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
+            Intent intent = null;
+            if (result != null
+                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
+                /*
+                 * The Authenticator API allows third party authenticators to
+                 * supply arbitrary intents to other apps that they can run,
+                 * this can be very bad when those apps are in the system like
+                 * the System Settings.
+                 */
+                PackageManager pm = mContext.getPackageManager();
+                ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+                int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
+                int authenticatorUid = Binder.getCallingUid();
+                if (PackageManager.SIGNATURE_MATCH !=
+                        pm.checkSignatures(authenticatorUid, targetUid)) {
+                    throw new SecurityException(
+                            "Activity to be started with KEY_INTENT must " +
+                            "share Authenticator's signatures");
+                }
+            }
+            if (result != null
+                    && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
                 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
                 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
                 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
@@ -2223,6 +2276,7 @@
             super(looper);
         }
 
+        @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MESSAGE_TIMED_OUT:
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a14d729..ddb6d1a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1689,7 +1689,7 @@
                                     && proc.pid == pid) {
                                 num++;
                                 proc.lastPssTime = SystemClock.uptimeMillis();
-                                proc.baseProcessTracker.addPss(pss, tmp[0], true);
+                                proc.baseProcessTracker.addPss(pss, tmp[0], true, proc.pkgList);
                                 if (DEBUG_PSS) Slog.d(TAG, "PSS of " + proc.toShortString()
                                         + ": " + pss + " lastPss=" + proc.lastPss
                                         + " state=" + ProcessList.makeProcStateString(procState));
@@ -2705,18 +2705,6 @@
         return intent;
     }
 
-    String getHomePackageName() {
-        Intent intent = getHomeIntent();
-        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, mCurrentUserId);
-        if (aInfo != null) {
-            final String homePackageName = aInfo.applicationInfo.packageName;
-            if (!ResolverActivity.class.getName().equals(homePackageName)) {
-                return homePackageName;
-            }
-        }
-        return null;
-    }
-
     boolean startHomeActivityLocked(int userId) {
         if (mHeadless) {
             // Added because none of the other calls to ensureBootCompleted seem to fire
@@ -4278,7 +4266,7 @@
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
                         proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
-                                infos[i].getTotalUss(), false);
+                                infos[i].getTotalUss(), false, proc.pkgList);
                     }
                 }
             }
@@ -4305,7 +4293,7 @@
                 synchronized (this) {
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
-                        proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false);
+                        proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false, proc.pkgList);
                     }
                 }
             }
@@ -6786,6 +6774,10 @@
             // Kill the running processes.
             for (int i=0; i<procs.size(); i++) {
                 ProcessRecord pr = procs.get(i);
+                if (pr == mHomeProcess) {
+                    // Don't kill the home process along with tasks from the same package.
+                    continue;
+                }
                 if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
                     killUnneededProcessLocked(pr, "remove task");
                 } else {
@@ -11748,7 +11740,7 @@
                 synchronized (this) {
                     if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
                         // Record this for posterity if the process has been stable.
-                        r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true);
+                        r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true, r.pkgList);
                     }
                 }
 
@@ -11909,7 +11901,7 @@
                 if (memInfo.getZramTotalSizeKb() != 0) {
                     if (!isCompact) {
                         pw.print("     ZRAM: "); pw.print(memInfo.getZramTotalSizeKb());
-                                pw.print(" kB used for ");
+                                pw.print(" kB physical used for ");
                                 pw.print(memInfo.getSwapTotalSizeKb()
                                         - memInfo.getSwapFreeSizeKb());
                                 pw.print(" kB in swap (");
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 4359895..2c0b83b 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -58,6 +58,7 @@
 final class ActivityRecord {
     static final String TAG = ActivityManagerService.TAG;
     static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
+    final public static String RECENTS_PACKAGE_NAME = "com.android.systemui.recent";
 
     final ActivityManagerService service; // owner
     final IApplicationToken.Stub appToken; // window manager token
@@ -443,25 +444,18 @@
             noDisplay = ent != null && ent.array.getBoolean(
                     com.android.internal.R.styleable.Window_windowNoDisplay, false);
 
-            // If we know the system has determined the component, then
-            // we can consider this to be a home activity...
-            String homePackageName = supervisor.getHomePackageName();
-            if (homePackageName != null && homePackageName.equals(packageName)) {
-                mActivityType = HOME_ACTIVITY_TYPE;
-            } else if ((!_componentSpecified || _launchedFromUid == Process.myUid()
+            if ((!_componentSpecified || _launchedFromUid == Process.myUid()
                     || _launchedFromUid == 0) &&
                     Intent.ACTION_MAIN.equals(_intent.getAction()) &&
                     _intent.hasCategory(Intent.CATEGORY_HOME) &&
                     _intent.getCategories().size() == 1 &&
                     _intent.getData() == null &&
                     _intent.getType() == null &&
-                    (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+                    (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
+                    isNotResolverActivity()) {
                 // This sure looks like a home activity!
                 mActivityType = HOME_ACTIVITY_TYPE;
-                if (isNotResolverActivity()) {
-                    supervisor.setHomePackageName(userId, packageName);
-                }
-            } else if (realActivity.getClassName().contains("com.android.systemui.recent")) {
+            } else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) {
                 mActivityType = RECENTS_ACTIVITY_TYPE;
             } else {
                 mActivityType = APPLICATION_ACTIVITY_TYPE;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 18ff819..4d66946 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -334,20 +334,16 @@
         mCurrentUser = service.mCurrentUserId;
     }
 
-    private boolean okToShow(ActivityRecord r) {
+    boolean okToShow(ActivityRecord r) {
         return r.userId == mCurrentUser
                 || (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0;
     }
 
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = mTaskHistory.get(taskNdx);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                ActivityRecord r = activities.get(activityNdx);
-                if (!r.finishing && r != notTop && okToShow(r)) {
-                    return r;
-                }
+            ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked(notTop);
+            if (r != null) {
+                return r;
             }
         }
         return null;
@@ -1087,10 +1083,24 @@
                         // At this point, nothing else needs to be shown
                         if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r);
                         behindFullscreen = true;
-                    } else if (task.mActivities.indexOf(r) == 0 && task.mOnTopOfHome) {
-                        if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
-                        showHomeBehindStack = true;
-                        behindFullscreen = true;
+                    } else if (task.mOnTopOfHome) {
+                        // Work our way down from r to bottom of task and see if there are any
+                        // visible activities below r.
+                        int rIndex = task.mActivities.indexOf(r);
+                        for ( --rIndex; rIndex >= 0; --rIndex) {
+                            final ActivityRecord blocker = task.mActivities.get(rIndex);
+                            if (!blocker.finishing && blocker.visible) {
+                                if (DEBUG_VISBILITY) Slog.v(TAG, "Home visibility for " +
+                                        r + " blocked by " + blocker);
+                                break;
+                            }
+                        }
+                        if (rIndex < 0) {
+                            // Got to task bottom without finding a visible activity, show home.
+                            if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
+                            showHomeBehindStack = true;
+                            behindFullscreen = true;
+                        }
                     }
                 } else {
                     if (DEBUG_VISBILITY) Slog.v(
@@ -3405,11 +3415,16 @@
         }
     }
 
-    void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
+    /**
+     * Reset local parameters because an app's activity died.
+     * @param app The app of the activity that died.
+     * @return true if home should be launched next.
+     */
+    boolean handleAppDiedLocked(ProcessRecord app) {
         if (!containsApp(app)) {
-            return;
+            return false;
         }
-        // TODO: handle the case where an app spans multiple stacks.
+
         if (mPausingActivity != null && mPausingActivity.app == app) {
             if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG,
                     "App died while pausing: " + mPausingActivity);
@@ -3419,28 +3434,32 @@
             mLastPausedActivity = null;
             mLastNoHistoryActivity = null;
         }
-        final ActivityRecord top = topRunningActivityLocked(null);
-        final boolean launchHomeTaskNext =
-                top != null && top.app == app && top.task.mOnTopOfHome;
 
-        // Remove this application's activities from active lists.
-        boolean hasVisibleActivities = removeHistoryRecordsForAppLocked(app);
-
-        if (!restarting) {
-            ActivityStack stack = mStackSupervisor.getFocusedStack();
-            if (stack == null || launchHomeTaskNext) {
-                mStackSupervisor.resumeHomeActivity(null);
-            } else if (!mStackSupervisor.resumeTopActivitiesLocked(stack, null, null)) {
-                // If there was nothing to resume, and we are not already
-                // restarting this process, but there is a visible activity that
-                // is hosted by the process...  then make sure all visible
-                // activities are running, taking care of restarting this
-                // process.
-                if (hasVisibleActivities) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
-                }
+        // Determine if the top task is exiting and should return to home. Do this before it gets
+        // removed in removeHistoryRecordsForAppsLocked.
+        boolean launchHomeNext = false;
+        int top = mTaskHistory.size() - 1;
+        while (top >= 0) {
+            final TaskRecord topTask = mTaskHistory.get(top);
+            if (topTask.mActivities.isEmpty()) {
+                // Not possible, but just in case.
+                --top;
+                continue;
             }
+            ActivityRecord r = topTask.topRunningActivityLocked(null);
+            if (r != null) {
+                // r will be launched next.
+                break;
+            }
+            // There is an activity in topTask that is finishing. If topTask belongs to the app
+            // return to home depending on the task flag.
+            launchHomeNext = topTask.mOnTopOfHome;
+            break;
         }
+
+        removeHistoryRecordsForAppLocked(app);
+
+        return launchHomeNext;
     }
 
     void handleAppCrashLocked(ProcessRecord app) {
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 1ee13ec..f718706 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -203,12 +203,6 @@
      */
     final PowerManager.WakeLock mGoingToSleep;
 
-    /**
-     * The name of the current home activity for each user.
-     * TODO: Remove entries when user is deleted.
-     */
-    final SparseArray<String> mHomePackageNames = new SparseArray<String>();
-
     public ActivityStackSupervisor(ActivityManagerService service, Context context,
             Looper looper) {
         mService = service;
@@ -1932,10 +1926,28 @@
     }
 
     void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
-        // Just in case.
+        boolean launchHomeTaskNext = false;
+        final ActivityStack focusedStack = getFocusedStack();
         final int numStacks = mStacks.size();
         for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            mStacks.get(stackNdx).handleAppDiedLocked(app, restarting);
+            final ActivityStack stack = mStacks.get(stackNdx);
+            // Only update launchHomeTaskNext for the focused stack.
+            launchHomeTaskNext |= (stack == focusedStack && stack.handleAppDiedLocked(app));
+        }
+
+        if (!restarting) {
+            if (launchHomeTaskNext) {
+                resumeHomeActivity(null);
+            } else {
+                if (!resumeTopActivitiesLocked(focusedStack, null, null)) {
+                    // If there was nothing to resume, and we are not already
+                    // restarting this process, but there is a visible activity that
+                    // is hosted by the process...  then make sure all visible
+                    // activities are running, taking care of restarting this
+                    // process.
+                    ensureActivitiesVisibleLocked(null, 0);
+                }
+            }
         }
     }
 
@@ -2267,11 +2279,6 @@
     boolean switchUserLocked(int userId, UserStartedState uss) {
         mCurrentUser = userId;
 
-        final String homePackageName = mService.getHomePackageName();
-        if (homePackageName != null) {
-            setHomePackageName(mCurrentUser, homePackageName);
-        }
-
         mStartingUsers.add(uss);
         boolean haveActivities = false;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
@@ -2373,12 +2380,6 @@
         pw.print(prefix); pw.print("mStackState="); pw.println(stackStateToString(mStackState));
         pw.print(prefix); pw.println("mSleepTimeout: " + mSleepTimeout);
         pw.print(prefix); pw.println("mCurTaskId: " + mCurTaskId);
-        pw.print(prefix); pw.print("mHomePackageNames:");
-                for (int i = 0; i < mHomePackageNames.size(); ++i) {
-                    pw.print(" ("); pw.print(mHomePackageNames.keyAt(i)); pw.print(",");
-                    pw.print(mHomePackageNames.valueAt(i)); pw.print(")");
-                }
-                pw.println();
     }
 
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -2635,14 +2636,4 @@
             }
         }
     }
-
-    String getHomePackageName() {
-        return mHomePackageNames.get(mCurrentUser);
-    }
-
-    void setHomePackageName(int userId, String homePackageName) {
-        if (DEBUG_SWITCH) Slog.d(TAG, "setHomePackageName: user=" + userId + " package="
-                + homePackageName);
-        mHomePackageNames.put(userId, homePackageName);
-    }
 }
diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java
index be08973..2c49bb9 100644
--- a/services/java/com/android/server/am/ProcessStatsService.java
+++ b/services/java/com/android/server/am/ProcessStatsService.java
@@ -61,7 +61,6 @@
     static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames.
     static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in.
     static long WRITE_PERIOD = 30*60*1000;      // Write file every 30 minutes or so.
-    static long COMMIT_PERIOD = 3*60*60*1000;  // Commit current stats every 3 hours.
 
     final ActivityManagerService mAm;
     final File mBaseDir;
@@ -160,7 +159,7 @@
     public boolean shouldWriteNowLocked(long now) {
         if (now > (mLastWriteTime+WRITE_PERIOD)) {
             if (SystemClock.elapsedRealtime()
-                    > (mProcessStats.mTimePeriodStartRealtime+COMMIT_PERIOD)) {
+                    > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) {
                 mCommitPending = true;
             }
             return true;
@@ -358,7 +357,7 @@
             boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
             boolean sepProcStates, int[] procStates, long now, String reqPackage) {
         ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked(
-                screenStates, memStates, procStates, now, reqPackage);
+                screenStates, memStates, procStates, procStates, now, reqPackage);
         if (procs.size() > 0) {
             if (header != null) {
                 pw.println(header);
@@ -457,7 +456,7 @@
             if (curTime < minTime) {
                 // Need to add in older stats to reach desired time.
                 ArrayList<String> files = getCommittedFiles(0, false, true);
-                if (files.size() > 0) {
+                if (files != null && files.size() > 0) {
                     current.setDataPosition(0);
                     ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
                     current.recycle();
@@ -520,7 +519,7 @@
     static private void dumpHelp(PrintWriter pw) {
         pw.println("Process stats (procstats) dump options:");
         pw.println("    [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
-        pw.println("    [--details] [--full-details] [--current] [--one-day]");
+        pw.println("    [--details] [--full-details] [--current] [--hours]");
         pw.println("    [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
         pw.println("  --checkin: perform a checkin: print and delete old committed states.");
         pw.println("  --c: print only state in checkin format.");
@@ -532,7 +531,7 @@
         pw.println("  --details: dump all execution details, not just summary.");
         pw.println("  --full-details: dump only detail information, for all saved state.");
         pw.println("  --current: only dump current state.");
-        pw.println("  --one-day: dump stats aggregated across about one day.");
+        pw.println("  --hours: aggregate over about N last hours.");
         pw.println("  --commit: commit current stats to disk and reset to start new stats.");
         pw.println("  --reset: reset current stats, without committing.");
         pw.println("  --clear: clear all stats; does both --reset and deletes old stats.");
@@ -562,7 +561,7 @@
         boolean dumpDetails = false;
         boolean dumpFullDetails = false;
         boolean dumpAll = false;
-        boolean oneDay = false;
+        int aggregateHours = 0;
         String reqPackage = null;
         boolean csvSepScreenStats = false;
         int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON};
@@ -632,8 +631,20 @@
                     dumpDetails = true;
                 } else if ("--full-details".equals(arg)) {
                     dumpFullDetails = true;
-                } else if ("--one-day".equals(arg)) {
-                    oneDay = true;
+                } else if ("--hours".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Error: argument required for --hours");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    try {
+                        aggregateHours = Integer.parseInt(args[i]);
+                    } catch (NumberFormatException e) {
+                        pw.println("Error: --hours argument not an int -- " + args[i]);
+                        dumpHelp(pw);
+                        return;
+                    }
                 } else if ("--current".equals(arg)) {
                     currentOnly = true;
                 } else if ("--commit".equals(arg)) {
@@ -750,8 +761,9 @@
                 */
             }
             return;
-        } else if (oneDay) {
-            ParcelFileDescriptor pfd = getStatsOverTime(24*60*60*1000);
+        } else if (aggregateHours != 0) {
+            ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
+                    - (ProcessStats.COMMIT_PERIOD/2));
             if (pfd == null) {
                 pw.println("Unable to build stats!");
                 return;
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index f0bba4f..385253e 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -139,6 +139,16 @@
         return null;
     }
 
+    ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+        for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+            ActivityRecord r = mActivities.get(activityNdx);
+            if (!r.finishing && r != notTop && stack.okToShow(r)) {
+                return r;
+            }
+        }
+        return null;
+    }
+
     /**
      * Reorder the history stack so that the activity at the given index is
      * brought to the front.
@@ -418,6 +428,7 @@
             pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
                     pw.print(" rootWasReset="); pw.print(rootWasReset);
                     pw.print(" userId="); pw.print(userId);
+                    pw.print(" mTaskType="); pw.print(mTaskType);
                     pw.print(" numFullscreen="); pw.print(numFullscreen);
                     pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome);
         }
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
index d4583b5..b8e1b04 100644
--- a/services/java/com/android/server/print/PrintManagerService.java
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -194,6 +194,21 @@
     }
 
     @Override
+    public List<PrintServiceInfo> getInstalledPrintServices(int userId) {
+        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+        final UserState userState;
+        synchronized (mLock) {
+            userState = getOrCreateUserStateLocked(resolvedUserId);
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return userState.getInstalledPrintServices();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
             int userId) {
         final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java
index f98a805..798cea3 100644
--- a/services/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/java/com/android/server/print/RemotePrintSpooler.java
@@ -33,7 +33,6 @@
 import android.print.IPrintSpoolerClient;
 import android.print.PrintJobId;
 import android.print.PrintJobInfo;
-import android.print.PrintManager;
 import android.util.Slog;
 import android.util.TimedRemoteCaller;
 
@@ -91,7 +90,7 @@
     public static interface PrintSpoolerCallbacks {
         public void onPrintJobQueued(PrintJobInfo printJob);
         public void onAllPrintJobsForServiceHandled(ComponentName printService);
-        public void onPrintJobStateChanged(PrintJobId printJobId, int appId);
+        public void onPrintJobStateChanged(PrintJobInfo printJob);
     }
 
     public RemotePrintSpooler(Context context, int userId,
@@ -280,30 +279,6 @@
         }
     }
 
-    public final void forgetPrintJobs(List<PrintJobId> printJobIds) {
-        throwIfCalledOnMainThread();
-        synchronized (mLock) {
-            throwIfDestroyedLocked();
-            mCanUnbind = false;
-        }
-        try {
-            getRemoteInstanceLazy().forgetPrintJobs(printJobIds);
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error forgeting print jobs", re);
-        } catch (TimeoutException te) {
-            Slog.e(LOG_TAG, "Error forgeting print jobs", te);
-        } finally {
-            if (DEBUG) {
-                Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
-                        + "] forgetPrintJobs()");
-            }
-            synchronized (mLock) {
-                mCanUnbind = true;
-                mLock.notifyAll();
-            }
-        }
-    }
-
     public final void destroy() {
         throwIfCalledOnMainThread();
         if (DEBUG) {
@@ -323,18 +298,15 @@
                     .append(String.valueOf(mDestroyed)).println();
             pw.append(prefix).append("bound=")
                     .append((mRemoteInstance != null) ? "true" : "false").println();
-            pw.append(prefix).append("print jobs:").println();
-            if (mRemoteInstance != null) {
-                List<PrintJobInfo> printJobs = getPrintJobInfos(null,
-                        PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY);
-                if (printJobs != null) {
-                    final int printJobCount = printJobs.size();
-                    for (int i = 0; i < printJobCount; i++) {
-                        PrintJobInfo printJob = printJobs.get(i);
-                        pw.append(prefix).append(prefix).append(printJob.toString());
-                        pw.println();
-                    }
-                }
+
+            pw.flush();
+
+            try {
+                getRemoteInstanceLazy().asBinder().dump(fd, new String[]{prefix});
+            } catch (TimeoutException te) {
+                /* ignore */
+            } catch (RemoteException re) {
+                /* ignore */
             }
         }
     }
@@ -346,8 +318,8 @@
         }
     }
 
-    private void onPrintJobStateChanged(PrintJobId printJobId, int appId) {
-        mCallbacks.onPrintJobStateChanged(printJobId, appId);
+    private void onPrintJobStateChanged(PrintJobInfo printJob) {
+        mCallbacks.onPrintJobStateChanged(printJob);
     }
 
     private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
@@ -625,12 +597,12 @@
         }
 
         @Override
-        public void onPrintJobStateChanged(PrintJobId printJobId, int appId) {
+        public void onPrintJobStateChanged(PrintJobInfo printJob) {
             RemotePrintSpooler spooler = mWeakSpooler.get();
             if (spooler != null) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    spooler.onPrintJobStateChanged(printJobId, appId);
+                    spooler.onPrintJobStateChanged(printJob);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
index e5f5842..bc70fe3 100644
--- a/services/java/com/android/server/print/UserState.java
+++ b/services/java/com/android/server/print/UserState.java
@@ -33,7 +33,6 @@
 import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.UserManager;
 import android.print.IPrintClient;
 import android.print.IPrintDocumentAdapter;
 import android.print.IPrintJobStateChangeListener;
@@ -52,6 +51,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.R;
 import com.android.internal.os.BackgroundThread;
@@ -62,6 +62,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -93,8 +94,8 @@
     private final Set<ComponentName> mEnabledServices =
             new ArraySet<ComponentName>();
 
-    private final CreatedPrintJobTracker mCreatedPrintJobTracker =
-            new CreatedPrintJobTracker();
+    private final PrintJobForAppCache mPrintJobForAppCache =
+            new PrintJobForAppCache();
 
     private final Object mLock;
 
@@ -155,23 +156,22 @@
     public PrintJobInfo print(String printJobName, final IPrintClient client,
             final IPrintDocumentAdapter documentAdapter, PrintAttributes attributes,
             int appId) {
-        PrintJobId printJobId = new PrintJobId();
-
-        // Track this job so we can forget it when the creator dies.
-        if (!mCreatedPrintJobTracker.onPrintJobCreatedLocked(client.asBinder(), printJobId)) {
-            // Not adding a print job means the client is dead - done.
-            return null;
-        }
-
         // Create print job place holder.
         final PrintJobInfo printJob = new PrintJobInfo();
-        printJob.setId(printJobId);
+        printJob.setId(new PrintJobId());
         printJob.setAppId(appId);
         printJob.setLabel(printJobName);
         printJob.setAttributes(attributes);
         printJob.setState(PrintJobInfo.STATE_CREATED);
         printJob.setCopies(1);
 
+        // Track this job so we can forget it when the creator dies.
+        if (!mPrintJobForAppCache.onPrintJobCreated(client.asBinder(), appId,
+                printJob)) {
+            // Not adding a print job means the client is dead - done.
+            return null;
+        }
+
         // Spin the spooler to add the job and show the config UI.
         new AsyncTask<Void, Void, Void>() {
             @Override
@@ -185,10 +185,42 @@
     }
 
     public List<PrintJobInfo> getPrintJobInfos(int appId) {
-        return mSpooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY, appId);
+        List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId);
+        // Note that the print spooler is not storing print jobs that
+        // are in a terminal state as it is non-trivial to properly update
+        // the spooler state for when to forget print jobs in terminal state.
+        // Therefore, we fuse the cached print jobs for running apps (some
+        // jobs are in a terminal state) with the ones that the print
+        // spooler knows about (some jobs are being processed).
+        ArrayMap<PrintJobId, PrintJobInfo> result =
+                new ArrayMap<PrintJobId, PrintJobInfo>();
+
+        // Add the cached print jobs for running apps.
+        final int cachedPrintJobCount = cachedPrintJobs.size();
+        for (int i = 0; i < cachedPrintJobCount; i++) {
+            PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i);
+            result.put(cachedPrintJob.getId(), cachedPrintJob);
+        }
+
+        // Add everything else the spooler knows about.
+        List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(null,
+                PrintJobInfo.STATE_ANY, appId);
+        if (printJobs != null) {
+            final int printJobCount = printJobs.size();
+            for (int i = 0; i < printJobCount; i++) {
+                PrintJobInfo printJob = printJobs.get(i);
+                result.put(printJob.getId(), printJob);
+            }
+        }
+
+        return new ArrayList<PrintJobInfo>(result.values());
     }
 
     public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
+        PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId);
+        if (printJob != null) {
+            return printJob;
+        }
         return mSpooler.getPrintJobInfo(printJobId, appId);
     }
 
@@ -242,6 +274,12 @@
         }
     }
 
+    public List<PrintServiceInfo> getInstalledPrintServices() {
+        synchronized (mLock) {
+            return mInstalledServices;
+        }
+    }
+
     public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
         synchronized (mLock) {
             throwIfDestroyedLocked();
@@ -398,9 +436,10 @@
     }
 
     @Override
-    public void onPrintJobStateChanged(PrintJobId printJobId, int appId) {
+    public void onPrintJobStateChanged(PrintJobInfo printJob) {
+        mPrintJobForAppCache.onPrintJobStateChanged(printJob);
         mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED,
-                appId, 0, printJobId).sendToTarget();
+                printJob.getAppId(), 0, printJob.getId()).sendToTarget();
     }
 
     @Override
@@ -525,6 +564,9 @@
             pw.println();
         }
 
+        pw.append(prefix).append(tab).append("cached print jobs:").println();
+        mPrintJobForAppCache.dump(pw, prefix + tab + tab);
+
         pw.append(prefix).append(tab).append("discovery mediator:").println();
         if (mPrinterDiscoverySession != null) {
             mPrinterDiscoverySession.dump(pw, prefix + tab + tab);
@@ -1424,34 +1466,19 @@
         }
     }
 
-    private final class CreatedPrintJobTracker {
-        private final ArrayMap<IBinder, List<PrintJobId>> mCreatedPrintJobs =
-                new ArrayMap<IBinder, List<PrintJobId>>();
+    private final class PrintJobForAppCache {
+        private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp =
+                new SparseArray<List<PrintJobInfo>>();
 
-        public boolean onPrintJobCreatedLocked(final IBinder creator, PrintJobId printJobId) {
+        public boolean onPrintJobCreated(final IBinder creator, final int appId,
+                PrintJobInfo printJob) {
             try {
                 creator.linkToDeath(new DeathRecipient() {
                     @Override
                     public void binderDied() {
                         creator.unlinkToDeath(this, 0);
-                        UserManager userManager = (UserManager) mContext.getSystemService(
-                                Context.USER_SERVICE);
-                        // If the death is a result of the user being removed, then
-                        // do nothing since the spooler data for this user will be
-                        // wiped and we cannot bind to the spooler at this point.
-                        if (userManager.getUserInfo(mUserId) == null) {
-                            return;
-                        }
-                        List<PrintJobId> printJobIds = null;
                         synchronized (mLock) {
-                            printJobIds = mCreatedPrintJobs.remove(creator);
-                            if (printJobIds == null) {
-                                return;
-                            }
-                            printJobIds = new ArrayList<PrintJobId>(printJobIds);
-                        }
-                        if (printJobIds != null) {
-                            mSpooler.forgetPrintJobs(printJobIds);
+                            mPrintJobsForRunningApp.remove(appId);
                         }
                     }
                 }, 0);
@@ -1460,14 +1487,93 @@
                 return false;
             }
             synchronized (mLock) {
-                List<PrintJobId> printJobIds = mCreatedPrintJobs.get(creator);
-                if (printJobIds == null) {
-                    printJobIds = new ArrayList<PrintJobId>();
-                    mCreatedPrintJobs.put(creator, printJobIds);
+                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId);
+                if (printJobsForApp == null) {
+                    printJobsForApp = new ArrayList<PrintJobInfo>();
+                    mPrintJobsForRunningApp.put(appId, printJobsForApp);
                 }
-                printJobIds.add(printJobId);
+                printJobsForApp.add(printJob);
             }
             return true;
         }
+
+        public void onPrintJobStateChanged(PrintJobInfo printJob) {
+            synchronized (mLock) {
+                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(
+                        printJob.getAppId());
+                if (printJobsForApp == null) {
+                    return;
+                }
+                final int printJobCount = printJobsForApp.size();
+                for (int i = 0; i < printJobCount; i++) {
+                    PrintJobInfo oldPrintJob = printJobsForApp.get(i);
+                    if (oldPrintJob.getId().equals(printJob.getId())) {
+                        printJobsForApp.set(i, printJob);
+                    }
+                }
+            }
+        }
+
+        public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) {
+            synchronized (mLock) {
+                List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId);
+                if (printJobsForApp == null) {
+                    return null;
+                }
+                final int printJobCount = printJobsForApp.size();
+                for (int i = 0; i < printJobCount; i++) {
+                    PrintJobInfo printJob = printJobsForApp.get(i);
+                    if (printJob.getId().equals(printJobId)) {
+                        return printJob;
+                    }
+                }
+            }
+            return null;
+        }
+
+        public List<PrintJobInfo> getPrintJobs(int appId) {
+            synchronized (mLock) {
+                List<PrintJobInfo> printJobs = null;
+                if (appId == PrintManager.APP_ID_ANY) {
+                    final int bucketCount = mPrintJobsForRunningApp.size();
+                    for (int i = 0; i < bucketCount; i++) {
+                        List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
+                        if (printJobs == null) {
+                            printJobs = new ArrayList<PrintJobInfo>();
+                        }
+                        printJobs.addAll(bucket);
+                    }
+                } else {
+                    List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId);
+                    if (bucket != null) {
+                        if (printJobs == null) {
+                            printJobs = new ArrayList<PrintJobInfo>();
+                        }
+                        printJobs.addAll(bucket);
+                    }
+                }
+                if (printJobs != null) {
+                    return printJobs;
+                }
+                return Collections.emptyList();
+            }
+        }
+
+        public void dump(PrintWriter pw, String prefix) {
+            synchronized (mLock) {
+                String tab = "  ";
+                final int bucketCount = mPrintJobsForRunningApp.size();
+                for (int i = 0; i < bucketCount; i++) {
+                    final int appId = mPrintJobsForRunningApp.keyAt(i);
+                    pw.append(prefix).append("appId=" + appId).append(':').println();
+                    List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i);
+                    final int printJobCount = bucket.size();
+                    for (int j = 0; j < printJobCount; j++) {
+                        PrintJobInfo printJob = bucket.get(j);
+                        pw.append(prefix).append(tab).append(printJob.toString()).println();
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index beeb899..2798104 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -162,6 +162,7 @@
     void moveStack(TaskStack stack, boolean toTop) {
         mStackHistory.remove(stack);
         mStackHistory.add(toTop ? mStackHistory.size() : 0, stack);
+        mService.moveStackWindowsLocked(stack);
     }
 
     public boolean isPrivate() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index e330f8b..80c50cc 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4715,7 +4715,7 @@
         }
     }
 
-    private void moveStackWindowsLocked(TaskStack stack) {
+    void moveStackWindowsLocked(TaskStack stack) {
         DisplayContent displayContent = stack.getDisplayContent();
 
         // First remove all of the windows from the list.
@@ -4782,7 +4782,6 @@
                 }
                 stack.moveTaskToTop(task);
                 displayContent.moveStack(stack, true);
-                moveStackWindowsLocked(stack);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -10142,16 +10141,6 @@
         return mSafeMode;
     }
 
-    @Override
-    public void showAssistant() {
-        // TODO: What permission?
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
-                != PackageManager.PERMISSION_GRANTED) {
-            return;
-        }
-        mPolicy.showAssistant();
-    }
-
     void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
         pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
         mPolicy.dump("    ", pw, args);
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 1e3b058..632efe0 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -346,6 +346,8 @@
     LABEL_ATTR = 0x01010001,
     ICON_ATTR = 0x01010002,
     NAME_ATTR = 0x01010003,
+    PERMISSION_ATTR = 0x01010006,
+    RESOURCE_ATTR = 0x01010025,
     DEBUGGABLE_ATTR = 0x0101000f,
     VERSION_CODE_ATTR = 0x0101021b,
     VERSION_NAME_ATTR = 0x0101021c,
@@ -372,6 +374,7 @@
     COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
     LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
     PUBLIC_KEY_ATTR = 0x010103a6,
+    CATEGORY_ATTR = 0x010103e8,
 };
 
 const char *getComponentName(String8 &pkgName, String8 &componentName) {
@@ -424,6 +427,61 @@
     printf("\n");
 }
 
+Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost,
+        String8 *outError = NULL)
+{
+    Asset* aidAsset = assets.openNonAsset(xmlPath, Asset::ACCESS_BUFFER);
+    if (aidAsset == NULL) {
+        if (outError != NULL) *outError = "xml resource does not exist";
+        return Vector<String8>();
+    }
+
+    const String8 serviceTagName(offHost ? "offhost-apdu-service" : "host-apdu-service");
+
+    bool withinApduService = false;
+    Vector<String8> categories;
+
+    String8 error;
+    ResXMLTree tree;
+    tree.setTo(aidAsset->getBuffer(true), aidAsset->getLength());
+
+    size_t len;
+    int depth = 0;
+    ResXMLTree::event_code_t code;
+    while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+        if (code == ResXMLTree::END_TAG) {
+            depth--;
+            String8 tag(tree.getElementName(&len));
+
+            if (depth == 0 && tag == serviceTagName) {
+                withinApduService = false;
+            }
+
+        } else if (code == ResXMLTree::START_TAG) {
+            depth++;
+            String8 tag(tree.getElementName(&len));
+
+            if (depth == 1) {
+                if (tag == serviceTagName) {
+                    withinApduService = true;
+                }
+            } else if (depth == 2 && withinApduService) {
+                if (tag == "aid-group") {
+                    String8 category = getAttribute(tree, CATEGORY_ATTR, &error);
+                    if (error != "") {
+                        if (outError != NULL) *outError = error;
+                        return Vector<String8>();
+                    }
+
+                    categories.add(category);
+                }
+            }
+        }
+    }
+    aidAsset->close();
+    return categories;
+}
+
 /*
  * Handle the "dump" command, to extract select data from an archive.
  */
@@ -631,12 +689,31 @@
             bool hasOtherServices = false;
             bool hasWallpaperService = false;
             bool hasImeService = false;
+            bool hasAccessibilityService = false;
+            bool hasPrintService = false;
             bool hasWidgetReceivers = false;
+            bool hasDeviceAdminReceiver = false;
             bool hasIntentFilter = false;
+            bool hasPaymentService = false;
             bool actMainActivity = false;
             bool actWidgetReceivers = false;
+            bool actDeviceAdminEnabled = false;
             bool actImeService = false;
             bool actWallpaperService = false;
+            bool actAccessibilityService = false;
+            bool actPrintService = false;
+            bool actHostApduService = false;
+            bool actOffHostApduService = false;
+            bool hasMetaHostPaymentCategory = false;
+            bool hasMetaOffHostPaymentCategory = false;
+
+            // These permissions are required by services implementing services
+            // the system binds to (IME, Accessibility, PrintServices, etc.)
+            bool hasBindDeviceAdminPermission = false;
+            bool hasBindInputMethodPermission = false;
+            bool hasBindAccessibilityServicePermission = false;
+            bool hasBindPrintServicePermission = false;
+            bool hasBindNfcServicePermission = false;
 
             // These two implement the implicit permissions that are granted
             // to pre-1.6 applications.
@@ -747,6 +824,13 @@
                             hasOtherActivities |= withinActivity;
                             hasOtherReceivers |= withinReceiver;
                             hasOtherServices |= withinService;
+                        } else {
+                            if (withinService) {
+                                hasPaymentService |= (actHostApduService && hasMetaHostPaymentCategory &&
+                                        hasBindNfcServicePermission);
+                                hasPaymentService |= (actOffHostApduService && hasMetaOffHostPaymentCategory &&
+                                        hasBindNfcServicePermission);
+                            }
                         }
                         withinActivity = false;
                         withinService = false;
@@ -760,11 +844,18 @@
                                 hasOtherActivities |= !actMainActivity;
                             } else if (withinReceiver) {
                                 hasWidgetReceivers |= actWidgetReceivers;
-                                hasOtherReceivers |= !actWidgetReceivers;
+                                hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
+                                        hasBindDeviceAdminPermission);
+                                hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
                             } else if (withinService) {
                                 hasImeService |= actImeService;
                                 hasWallpaperService |= actWallpaperService;
-                                hasOtherServices |= (!actImeService && !actWallpaperService);
+                                hasAccessibilityService |= (actAccessibilityService &&
+                                        hasBindAccessibilityServicePermission);
+                                hasPrintService |= (actPrintService && hasBindPrintServicePermission);
+                                hasOtherServices |= (!actImeService && !actWallpaperService &&
+                                        !actAccessibilityService && !actPrintService &&
+                                        !actHostApduService && !actOffHostApduService);
                             }
                         }
                         withinIntentFilter = false;
@@ -1109,6 +1200,13 @@
                     withinReceiver = false;
                     withinService = false;
                     hasIntentFilter = false;
+                    hasMetaHostPaymentCategory = false;
+                    hasMetaOffHostPaymentCategory = false;
+                    hasBindDeviceAdminPermission = false;
+                    hasBindInputMethodPermission = false;
+                    hasBindAccessibilityServicePermission = false;
+                    hasBindPrintServicePermission = false;
+                    hasBindNfcServicePermission = false;
                     if (withinApplication) {
                         if(tag == "activity") {
                             withinActivity = true;
@@ -1166,6 +1264,16 @@
                                         " %s\n", error.string());
                                 goto bail;
                             }
+
+                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+                            if (error == "") {
+                                if (permission == "android.permission.BIND_DEVICE_ADMIN") {
+                                    hasBindDeviceAdminPermission = true;
+                                }
+                            } else {
+                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
+                                        " receiver '%s': %s\n", receiverName.string(), error.string());
+                            }
                         } else if (tag == "service") {
                             withinService = true;
                             serviceName = getAttribute(tree, NAME_ATTR, &error);
@@ -1175,6 +1283,22 @@
                                         " service: %s\n", error.string());
                                 goto bail;
                             }
+
+                            String8 permission = getAttribute(tree, PERMISSION_ATTR, &error);
+                            if (error == "") {
+                                if (permission == "android.permission.BIND_INPUT_METHOD") {
+                                    hasBindInputMethodPermission = true;
+                                } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
+                                    hasBindAccessibilityServicePermission = true;
+                                } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
+                                    hasBindPrintServicePermission = true;
+                                } else if (permission == "android.permission.BIND_NFC_SERVICE") {
+                                    hasBindNfcServicePermission = true;
+                                }
+                            } else {
+                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
+                                        " service '%s': %s\n", serviceName.string(), error.string());
+                            }
                         }
                     } else if (withinSupportsInput && tag == "input-type") {
                         String8 name = getAttribute(tree, NAME_ATTR, &error);
@@ -1186,10 +1310,60 @@
                             goto bail;
                         }
                     }
-                } else if ((depth == 4) && (tag == "intent-filter")) {
-                    hasIntentFilter = true;
-                    withinIntentFilter = true;
-                    actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
+                } else if (depth == 4) {
+                    if (tag == "intent-filter") {
+                        hasIntentFilter = true;
+                        withinIntentFilter = true;
+                        actMainActivity = false;
+                        actWidgetReceivers = false;
+                        actImeService = false;
+                        actWallpaperService = false;
+                        actAccessibilityService = false;
+                        actPrintService = false;
+                        actDeviceAdminEnabled = false;
+                        actHostApduService = false;
+                        actOffHostApduService = false;
+                    } else if (withinService && tag == "meta-data") {
+                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        if (error != "") {
+                            fprintf(stderr, "ERROR getting 'android:name' attribute for"
+                                    " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
+                            goto bail;
+                        }
+
+                        if (name == "android.nfc.cardemulation.host_apdu_service" ||
+                                name == "android.nfc.cardemulation.off_host_apdu_service") {
+                            bool offHost = true;
+                            if (name == "android.nfc.cardemulation.host_apdu_service") {
+                                offHost = false;
+                            }
+
+                            String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error);
+                            if (error != "") {
+                                fprintf(stderr, "ERROR getting 'android:resource' attribute for"
+                                        " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
+                                goto bail;
+                            }
+
+                            Vector<String8> categories = getNfcAidCategories(assets, xmlPath,
+                                    offHost, &error);
+                            if (error != "") {
+                                fprintf(stderr, "ERROR getting AID category for service '%s'\n",
+                                        serviceName.string());
+                                goto bail;
+                            }
+
+                            const size_t catLen = categories.size();
+                            for (size_t i = 0; i < catLen; i++) {
+                                bool paymentCategory = (categories[i] == "payment");
+                                if (offHost) {
+                                    hasMetaOffHostPaymentCategory |= paymentCategory;
+                                } else {
+                                    hasMetaHostPaymentCategory |= paymentCategory;
+                                }
+                            }
+                        }
+                    }
                 } else if ((depth == 5) && withinIntentFilter){
                     String8 action;
                     if (tag == "action") {
@@ -1206,12 +1380,22 @@
                         } else if (withinReceiver) {
                             if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
                                 actWidgetReceivers = true;
+                            } else if (action == "android.app.action.DEVICE_ADMIN_ENABLED") {
+                                actDeviceAdminEnabled = true;
                             }
                         } else if (withinService) {
                             if (action == "android.view.InputMethod") {
                                 actImeService = true;
                             } else if (action == "android.service.wallpaper.WallpaperService") {
                                 actWallpaperService = true;
+                            } else if (action == "android.accessibilityservice.AccessibilityService") {
+                                actAccessibilityService = true;
+                            } else if (action == "android.printservice.PrintService") {
+                                actPrintService = true;
+                            } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
+                                actHostApduService = true;
+                            } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
+                                actOffHostApduService = true;
                             }
                         }
                         if (action == "android.intent.action.SEARCH") {
@@ -1411,12 +1595,24 @@
             if (hasWidgetReceivers) {
                 printf("app-widget\n");
             }
+            if (hasDeviceAdminReceiver) {
+                printf("device-admin\n");
+            }
             if (hasImeService) {
                 printf("ime\n");
             }
             if (hasWallpaperService) {
                 printf("wallpaper\n");
             }
+            if (hasAccessibilityService) {
+                printf("accessibility\n");
+            }
+            if (hasPrintService) {
+                printf("print\n");
+            }
+            if (hasPaymentService) {
+                printf("payment\n");
+            }
             if (hasOtherActivities) {
                 printf("other-activities\n");
             }
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index f0c3a75..fd7a645 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -458,11 +458,6 @@
     }
 
     @Override
-    public void showAssistant() {
-
-    }
-
-    @Override
     public IBinder getFocusedWindowToken() {
         // TODO Auto-generated method stub
         return null;
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 76bd636..798bc2e 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1456,9 +1456,6 @@
     public void setCountryCode(String countryCode, boolean persist) {
         if (persist) {
             mPersistedCountryCode = countryCode;
-            Settings.Global.putString(mContext.getContentResolver(),
-                    Settings.Global.WIFI_COUNTRY_CODE,
-                    countryCode);
         }
         sendMessage(CMD_SET_COUNTRY_CODE, countryCode);
         mWifiP2pChannel.sendMessage(WifiP2pService.SET_COUNTRY_CODE, countryCode);
@@ -1691,8 +1688,8 @@
      * Set the country code from the system setting value, if any.
      */
     private void setCountryCode() {
-        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.WIFI_COUNTRY_CODE);
+        String countryCode = mContext.getResources().getString(
+                                    R.string.config_wifi_default_country_code);
         if (countryCode != null && !countryCode.isEmpty()) {
             setCountryCode(countryCode, false);
         } else {
@@ -2417,13 +2414,6 @@
                 case CMD_BOOT_COMPLETED:
                     String countryCode = mPersistedCountryCode;
                     if (TextUtils.isEmpty(countryCode) == false) {
-                        Settings.Global.putString(mContext.getContentResolver(),
-                                Settings.Global.WIFI_COUNTRY_CODE,
-                                countryCode);
-                        // it may be that the state transition that should send this info
-                        // to the driver happened between mPersistedCountryCode getting set
-                        // and now, so simply persisting it here would mean we have sent
-                        // nothing to the driver.  Send the cmd so it might be set now.
                         sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, countryCode);
                     }
                     break;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 625ffb8..50e8e3d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -2545,12 +2545,6 @@
         mServiceTransactionId = 0;
         mServiceDiscReqId = null;
 
-        String countryCode = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.WIFI_COUNTRY_CODE);
-        if (countryCode != null && !countryCode.isEmpty()) {
-            mP2pStateMachine.sendMessage(SET_COUNTRY_CODE, countryCode);
-        }
-
         updatePersistentNetworks(RELOAD);
     }