Merge "More cleanup prior to unhiding" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 7e2b0ed..dca7481 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3455,7 +3455,7 @@
     method public boolean navigateUpToFromChild(android.app.Activity, android.content.Intent);
     method public void onActionModeFinished(android.view.ActionMode);
     method public void onActionModeStarted(android.view.ActionMode);
-    method protected void onActivityReenter(int, android.content.Intent);
+    method public void onActivityReenter(int, android.content.Intent);
     method protected void onActivityResult(int, int, android.content.Intent);
     method public void onAttachFragment(android.app.Fragment);
     method public void onAttachedToWindow();
@@ -3466,7 +3466,7 @@
     method public boolean onContextItemSelected(android.view.MenuItem);
     method public void onContextMenuClosed(android.view.Menu);
     method protected void onCreate(android.os.Bundle);
-    method protected void onCreate(android.os.Bundle, android.os.PersistableBundle);
+    method public void onCreate(android.os.Bundle, android.os.PersistableBundle);
     method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
     method public java.lang.CharSequence onCreateDescription();
     method protected deprecated android.app.Dialog onCreateDialog(int);
@@ -3498,7 +3498,7 @@
     method public void onPanelClosed(int, android.view.Menu);
     method protected void onPause();
     method protected void onPostCreate(android.os.Bundle);
-    method protected void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
+    method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
     method protected void onPostResume();
     method protected deprecated void onPrepareDialog(int, android.app.Dialog);
     method protected deprecated void onPrepareDialog(int, android.app.Dialog, android.os.Bundle);
@@ -3508,11 +3508,11 @@
     method public void onProvideAssistData(android.os.Bundle);
     method protected void onRestart();
     method protected void onRestoreInstanceState(android.os.Bundle);
-    method protected void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
+    method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
     method protected void onResume();
     method public deprecated java.lang.Object onRetainNonConfigurationInstance();
     method protected void onSaveInstanceState(android.os.Bundle);
-    method protected void onSaveInstanceState(android.os.Bundle, android.os.PersistableBundle);
+    method public void onSaveInstanceState(android.os.Bundle, android.os.PersistableBundle);
     method public boolean onSearchRequested();
     method protected void onStart();
     method protected void onStop();
@@ -14015,7 +14015,7 @@
     ctor public SettingInjectorService(java.lang.String);
     method public final android.os.IBinder onBind(android.content.Intent);
     method protected abstract boolean onGetEnabled();
-    method protected deprecated java.lang.String onGetSummary();
+    method protected abstract deprecated java.lang.String onGetSummary();
     method public final void onStart(android.content.Intent, int);
     method public final int onStartCommand(android.content.Intent, int, int);
     field public static final java.lang.String ACTION_INJECTED_SETTING_CHANGED = "android.location.InjectedSettingChanged";
@@ -21563,7 +21563,7 @@
     field public static final int JELLY_BEAN_MR2 = 18; // 0x12
     field public static final int KITKAT = 19; // 0x13
     field public static final int KITKAT_WATCH = 20; // 0x14
-    field public static final int L = 10000; // 0x2710
+    field public static final int L = 21; // 0x15
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -23686,7 +23686,7 @@
     field public static final java.lang.String DATE = "date";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "date DESC";
     field public static final java.lang.String DURATION = "duration";
-    field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.call_type_filter";
+    field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER";
     field public static final java.lang.String FEATURES = "features";
     field public static final int FEATURES_NONE = 0; // 0x0
     field public static final int FEATURES_VIDEO = 1; // 0x1
@@ -24050,11 +24050,11 @@
 
   public static final class ContactsContract.CommonDataKinds.Callable implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     ctor public ContactsContract.CommonDataKinds.Callable();
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final android.net.Uri CONTENT_URI;
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
   }
 
   protected static abstract interface ContactsContract.CommonDataKinds.CommonColumns implements android.provider.ContactsContract.CommonDataKinds.BaseTypes {
@@ -24065,11 +24065,11 @@
 
   public static final class ContactsContract.CommonDataKinds.Contactables implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     ctor public ContactsContract.CommonDataKinds.Contactables();
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final android.net.Uri CONTENT_URI;
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
   }
 
@@ -24077,15 +24077,15 @@
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
     field public static final java.lang.String ADDRESS = "data1";
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email_v2";
     field public static final android.net.Uri CONTENT_LOOKUP_URI;
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/email_v2";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DISPLAY_NAME = "data4";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final int TYPE_HOME = 1; // 0x1
     field public static final int TYPE_MOBILE = 4; // 0x4
     field public static final int TYPE_OTHER = 3; // 0x3
@@ -24095,10 +24095,10 @@
   public static final class ContactsContract.CommonDataKinds.Event implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static int getTypeResource(java.lang.Integer);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_event";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String START_DATE = "data1";
     field public static final int TYPE_ANNIVERSARY = 1; // 0x1
     field public static final int TYPE_BIRTHDAY = 3; // 0x3
@@ -24106,19 +24106,19 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.GroupMembership implements android.provider.ContactsContract.DataColumnsWithJoins {
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/group_membership";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String GROUP_ROW_ID = "data1";
     field public static final java.lang.String GROUP_SOURCE_ID = "group_sourceid";
   }
 
   public static final class ContactsContract.CommonDataKinds.Identity implements android.provider.ContactsContract.DataColumnsWithJoins {
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/identity";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String IDENTITY = "data1";
     field public static final java.lang.String NAMESPACE = "data2";
   }
@@ -24128,11 +24128,11 @@
     method public static final int getProtocolLabelResource(int);
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
     field public static final java.lang.String CUSTOM_PROTOCOL = "data6";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String PROTOCOL = "data5";
     field public static final int PROTOCOL_AIM = 0; // 0x0
     field public static final int PROTOCOL_CUSTOM = -1; // 0xffffffff
@@ -24150,10 +24150,10 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Nickname implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/nickname";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String NAME = "data1";
     field public static final int TYPE_DEFAULT = 1; // 0x1
     field public static final int TYPE_INITIALS = 5; // 0x5
@@ -24164,22 +24164,22 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Note implements android.provider.ContactsContract.DataColumnsWithJoins {
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/note";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String NOTE = "data1";
   }
 
   public static final class ContactsContract.CommonDataKinds.Organization implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String COMPANY = "data1";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
     field public static final java.lang.String DEPARTMENT = "data5";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String JOB_DESCRIPTION = "data6";
     field public static final java.lang.String OFFICE_LOCATION = "data9";
     field public static final java.lang.String PHONETIC_NAME = "data8";
@@ -24192,13 +24192,13 @@
   public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/phone_v2";
     field public static final android.net.Uri CONTENT_URI;
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String NORMALIZED_NUMBER = "data4";
     field public static final java.lang.String NUMBER = "data1";
     field public static final java.lang.String SEARCH_DISPLAY_NAME_KEY = "search_display_name";
@@ -24226,10 +24226,10 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Photo implements android.provider.ContactsContract.DataColumnsWithJoins {
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String PHOTO = "data15";
     field public static final java.lang.String PHOTO_FILE_ID = "data14";
   }
@@ -24237,10 +24237,10 @@
   public static final class ContactsContract.CommonDataKinds.Relation implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/relation";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String NAME = "data1";
     field public static final int TYPE_ASSISTANT = 1; // 0x1
     field public static final int TYPE_BROTHER = 2; // 0x2
@@ -24261,10 +24261,10 @@
   public static final class ContactsContract.CommonDataKinds.SipAddress implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sip_address";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String SIP_ADDRESS = "data1";
     field public static final int TYPE_HOME = 1; // 0x1
     field public static final int TYPE_OTHER = 3; // 0x3
@@ -24272,11 +24272,11 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.StructuredName implements android.provider.ContactsContract.DataColumnsWithJoins {
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/name";
     field public static final java.lang.String DISPLAY_NAME = "data1";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String FAMILY_NAME = "data3";
     field public static final java.lang.String FULL_NAME_STYLE = "data10";
     field public static final java.lang.String GIVEN_NAME = "data2";
@@ -24291,14 +24291,14 @@
   public static final class ContactsContract.CommonDataKinds.StructuredPostal implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CITY = "data7";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address_v2";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/postal-address_v2";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String COUNTRY = "data10";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String FORMATTED_ADDRESS = "data1";
     field public static final java.lang.String NEIGHBORHOOD = "data6";
     field public static final java.lang.String POBOX = "data5";
@@ -24311,10 +24311,10 @@
   }
 
   public static final class ContactsContract.CommonDataKinds.Website implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/website";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final int TYPE_BLOG = 2; // 0x2
     field public static final int TYPE_FTP = 6; // 0x6
     field public static final int TYPE_HOME = 4; // 0x4
@@ -24362,7 +24362,6 @@
     method public static deprecated void markAsContacted(android.content.ContentResolver, long);
     method public static java.io.InputStream openContactPhotoInputStream(android.content.ContentResolver, android.net.Uri, boolean);
     method public static java.io.InputStream openContactPhotoInputStream(android.content.ContentResolver, android.net.Uri);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final android.net.Uri CONTENT_FILTER_URI;
     field public static final android.net.Uri CONTENT_FREQUENT_URI;
     field public static final android.net.Uri CONTENT_GROUP_URI;
@@ -24375,8 +24374,9 @@
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String CONTENT_VCARD_TYPE = "text/x-vcard";
     field public static final android.net.Uri CONTENT_VCARD_URI;
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
   }
 
   public static final class ContactsContract.Contacts.AggregationSuggestions implements android.provider.BaseColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns {
@@ -24421,11 +24421,11 @@
 
   public static final class ContactsContract.Data implements android.provider.ContactsContract.DataColumnsWithJoins {
     method public static android.net.Uri getContactLookupUri(android.content.ContentResolver, android.net.Uri);
-    field public static final java.lang.String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/data";
     field public static final android.net.Uri CONTENT_URI;
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
-    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX = "android.provider.extra.ADDRESS_BOOK_INDEX";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
+    field public static final java.lang.String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
     field public static final java.lang.String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
   }
 
@@ -24669,7 +24669,7 @@
     ctor public ContactsContract.QuickContact();
     method public static void showQuickContact(android.content.Context, android.view.View, android.net.Uri, int, java.lang.String[]);
     method public static void showQuickContact(android.content.Context, android.graphics.Rect, android.net.Uri, int, java.lang.String[]);
-    field public static final java.lang.String ACTION_QUICK_CONTACT = "com.android.contacts.action.QUICK_CONTACT";
+    field public static final java.lang.String ACTION_QUICK_CONTACT = "android.provider.action.QUICK_CONTACT";
     field public static final java.lang.String EXTRA_EXCLUDE_MIMES = "android.provider.extra.EXCLUDE_MIMES";
     field public static final int MODE_LARGE = 3; // 0x3
     field public static final int MODE_MEDIUM = 2; // 0x2
@@ -28870,35 +28870,21 @@
   }
 
   public final class SmsManager {
-    method public android.net.Uri addMultimediaMessageDraft(android.net.Uri);
-    method public android.net.Uri addTextMessageDraft(java.lang.String, java.lang.String);
-    method public boolean archiveStoredConversation(long, boolean);
-    method public boolean deleteStoredConversation(long);
-    method public boolean deleteStoredMessage(android.net.Uri);
     method public java.util.ArrayList<java.lang.String> divideMessage(java.lang.String);
-    method public void downloadMultimediaMessage(java.lang.String, android.net.Uri, android.content.ContentValues, android.app.PendingIntent);
-    method public boolean getAutoPersisting();
+    method public void downloadMultimediaMessage(java.lang.String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
     method public android.os.Bundle getCarrierConfigValues();
     method public static android.telephony.SmsManager getDefault();
     method public static android.telephony.SmsManager getSmsManagerUsingSubId(long);
     method public long getSubId();
-    method public android.net.Uri importMultimediaMessage(android.net.Uri, java.lang.String, long, boolean, boolean);
-    method public android.net.Uri importTextMessage(java.lang.String, int, java.lang.String, long, boolean, boolean);
     method public void injectSmsPdu(byte[], java.lang.String, android.app.PendingIntent);
     method public void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
-    method public void sendMultimediaMessage(android.net.Uri, java.lang.String, android.content.ContentValues, android.app.PendingIntent);
+    method public void sendMultimediaMessage(android.net.Uri, java.lang.String, android.os.Bundle, android.app.PendingIntent);
     method public void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
-    method public void sendStoredMultimediaMessage(android.net.Uri, android.content.ContentValues, android.app.PendingIntent);
-    method public void sendStoredMultipartTextMessage(android.net.Uri, java.lang.String, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
-    method public void sendStoredTextMessage(android.net.Uri, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
     method public void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
-    method public void setAutoPersisting(boolean);
     method public void updateMmsDownloadStatus(int, byte[]);
     method public void updateMmsSendStatus(int, boolean);
     method public void updateSmsSendStatus(int, boolean);
-    method public boolean updateStoredMessageStatus(android.net.Uri, android.content.ContentValues);
-    field public static final java.lang.String MESSAGE_STATUS_READ = "read";
-    field public static final java.lang.String MESSAGE_STATUS_SEEN = "seen";
+    field public static final java.lang.String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA";
     field public static final java.lang.String MMS_CONFIG_ALIAS_ENABLED = "aliasEnabled";
     field public static final java.lang.String MMS_CONFIG_ALIAS_MAX_CHARS = "aliasMaxChars";
     field public static final java.lang.String MMS_CONFIG_ALIAS_MIN_CHARS = "aliasMinChars";
@@ -28933,13 +28919,10 @@
     field public static final int MMS_ERROR_IO_ERROR = 5; // 0x5
     field public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3; // 0x3
     field public static final int MMS_ERROR_UNSPECIFIED = 1; // 0x1
-    field public static final java.lang.String MMS_EXTRA_DATA = "data";
     field public static final int RESULT_ERROR_GENERIC_FAILURE = 1; // 0x1
     field public static final int RESULT_ERROR_NO_SERVICE = 4; // 0x4
     field public static final int RESULT_ERROR_NULL_PDU = 3; // 0x3
     field public static final int RESULT_ERROR_RADIO_OFF = 2; // 0x2
-    field public static final int SMS_TYPE_INCOMING = 0; // 0x0
-    field public static final int SMS_TYPE_OUTGOING = 1; // 0x1
     field public static final int STATUS_ON_ICC_FREE = 0; // 0x0
     field public static final int STATUS_ON_ICC_READ = 1; // 0x1
     field public static final int STATUS_ON_ICC_SENT = 5; // 0x5
@@ -34350,7 +34333,7 @@
   }
 
   public final class ViewAnimationUtils {
-    method public static final android.animation.Animator createCircularReveal(android.view.View, int, int, float, float);
+    method public static android.animation.Animator createCircularReveal(android.view.View, int, int, float, float);
   }
 
   public class ViewConfiguration {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 29ef484..8f0266c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -955,7 +955,7 @@
      * @see #onRestoreInstanceState
      * @see #onPostCreate
      */
-    protected void onCreate(@Nullable Bundle savedInstanceState,
+    public void onCreate(@Nullable Bundle savedInstanceState,
             @Nullable PersistableBundle persistentState) {
         onCreate(savedInstanceState);
     }
@@ -1040,7 +1040,7 @@
      * @see #onResume
      * @see #onSaveInstanceState
      */
-    protected void onRestoreInstanceState(Bundle savedInstanceState,
+    public void onRestoreInstanceState(Bundle savedInstanceState,
             PersistableBundle persistentState) {
         if (savedInstanceState != null) {
             onRestoreInstanceState(savedInstanceState);
@@ -1130,7 +1130,7 @@
      *
      * @see #onCreate
      */
-    protected void onPostCreate(@Nullable Bundle savedInstanceState,
+    public void onPostCreate(@Nullable Bundle savedInstanceState,
             @Nullable PersistableBundle persistentState) {
         onPostCreate(savedInstanceState);
     }
@@ -1380,7 +1380,7 @@
      * @see #onRestoreInstanceState(Bundle, PersistableBundle)
      * @see #onPause
      */
-    protected void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
         onSaveInstanceState(outState);
     }
 
@@ -4769,7 +4769,7 @@
      * @param data An Intent, which can return result data to the caller
      *               (various data can be attached to Intent "extras").
      */
-    protected void onActivityReenter(int resultCode, Intent data) {
+    public void onActivityReenter(int resultCode, Intent data) {
     }
 
     /**
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 4b5ced9..30f3576 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -55,10 +55,29 @@
     public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1;
 
     /**
+     * Message used by TV to receive volume status from Audio Receiver. It should check volume value
+     * that is retrieved from extra value with the key {@link #EXTRA_MESSAGE_EXTRAM_PARAM1}. If the
+     * value is in range of [0,100], it is current volume of Audio Receiver. And there is another
+     * value, {@link #AVR_VOLUME_MUTED}, which is used to inform volume mute.
+     */
+    public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2;
+
+    /**
      * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the ID of
      * the message to display on screen.
      */
     public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID";
+    /**
+     * Used as an extra field in the intent {@link #ACTION_OSD_MESSAGE}. Contains the extra value
+     * of the message.
+     */
+    public static final String EXTRA_MESSAGE_EXTRAM_PARAM1 =
+            "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1";
+
+    /**
+     * Volume value for mute state.
+     */
+    public static final int AVR_VOLUME_MUTED = 101;
 
     public static final int POWER_STATUS_UNKNOWN = -1;
     public static final int POWER_STATUS_ON = 0;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index b0e0b49..1e0dc53 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -541,7 +541,7 @@
          * Intent.</li>
          * </ul>
          */
-        public static final int L = CUR_DEVELOPMENT;
+        public static final int L = 21;
     }
     
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 0202f91..5fa1cc9 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -116,8 +116,8 @@
          * </pre>
          * </p>
          */
-        public static final String EXTRA_CALL_TYPE_FILTER
-                = "android.provider.extra.call_type_filter";
+        public static final String EXTRA_CALL_TYPE_FILTER =
+                "android.provider.extra.CALL_TYPE_FILTER";
 
         /**
          * Content uri used to access call log entries, including voicemail records. You must have
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 27473e3..18a9eb1 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1125,7 +1125,7 @@
          * import android.provider.ContactsContract.Contacts;
          *
          * Uri uri = Contacts.CONTENT_URI.buildUpon()
-         *          .appendQueryParameter(Contacts.ADDRESS_BOOK_INDEX_EXTRAS, "true")
+         *          .appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true")
          *          .build();
          * Cursor cursor = getContentResolver().query(uri,
          *          new String[] {Contacts.DISPLAY_NAME},
@@ -1140,21 +1140,24 @@
          * </pre>
          * </p>
          */
-        public static final String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras";
+        public static final String EXTRA_ADDRESS_BOOK_INDEX =
+                "android.provider.extra.ADDRESS_BOOK_INDEX";
 
         /**
          * The array of address book index titles, which are returned in the
          * same order as the data in the cursor.
          * <p>TYPE: String[]</p>
          */
-        public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles";
+        public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES =
+                "android.provider.extra.ADDRESS_BOOK_INDEX_TITLES";
 
         /**
          * The array of group counts for the corresponding group.  Contains the same number
          * of elements as the EXTRA_ADDRESS_BOOK_INDEX_TITLES array.
          * <p>TYPE: int[]</p>
          */
-        public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts";
+        public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS =
+                "android.provider.extra.ADDRESS_BOOK_INDEX_COUNTS";
     }
 
     /**
@@ -8111,7 +8114,7 @@
          * for the provided {@link Contacts} entry.
          */
         public static final String ACTION_QUICK_CONTACT =
-                "com.android.contacts.action.QUICK_CONTACT";
+                "android.provider.action.QUICK_CONTACT";
 
         /**
          * Extra used to specify pivot dialog location in screen coordinates.
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 679e2cc..3222dbf 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -116,7 +116,8 @@
 
     /**
      * Move the cursor to the buffer offset physically above the current
-     * offset, or return false if the cursor is already on the top line.
+     * offset, to the beginning if it is on the top line but not at the
+     * start, or return false if the cursor is already on the top line.
      */
     public static boolean moveUp(Spannable text, Layout layout) {
         int start = getSelectionStart(text);
@@ -149,6 +150,9 @@
 
                 setSelection(text, move);
                 return true;
+            } else if (end != 0) {
+                setSelection(text, 0);
+                return true;
             }
         }
 
@@ -157,7 +161,9 @@
 
     /**
      * Move the cursor to the buffer offset physically below the current
-     * offset, or return false if the cursor is already on the bottom line.
+     * offset, to the end of the buffer if it is on the bottom line but
+     * not at the end, or return false if the cursor is already at the
+     * end of the buffer.
      */
     public static boolean moveDown(Spannable text, Layout layout) {
         int start = getSelectionStart(text);
@@ -190,6 +196,9 @@
 
                 setSelection(text, move);
                 return true;
+            } else if (end != text.length()) {
+                setSelection(text, text.length());
+                return true;
             }
         }
 
diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java
index 96abf51..6ca4a9e 100644
--- a/core/java/android/widget/ActionMenuView.java
+++ b/core/java/android/widget/ActionMenuView.java
@@ -29,6 +29,7 @@
 import com.android.internal.view.menu.ActionMenuItemView;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuItemImpl;
+import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.view.menu.MenuView;
 
 /**
@@ -53,6 +54,8 @@
 
     private boolean mReserveOverflow;
     private ActionMenuPresenter mPresenter;
+    private MenuPresenter.Callback mActionMenuPresenterCallback;
+    private MenuBuilder.Callback mMenuBuilderCallback;
     private boolean mFormatItems;
     private int mFormatItemsWidth;
     private int mMinCellSize;
@@ -608,7 +611,8 @@
             mMenu = new MenuBuilder(context);
             mMenu.setCallback(new MenuBuilderCallback());
             mPresenter = new ActionMenuPresenter(context);
-            mPresenter.setCallback(new ActionMenuPresenterCallback());
+            mPresenter.setCallback(mActionMenuPresenterCallback != null
+                    ? mActionMenuPresenterCallback : new ActionMenuPresenterCallback());
             mMenu.addMenuPresenter(mPresenter, mPopupContext);
             mPresenter.setMenuView(this);
         }
@@ -617,6 +621,15 @@
     }
 
     /**
+     * Must be called before the first call to getMenu()
+     * @hide
+     */
+    public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
+        mActionMenuPresenterCallback = pcb;
+        mMenuBuilderCallback = mcb;
+    }
+
+    /**
      * Returns the current menu or null if one has not yet been configured.
      * @hide Internal use only for action bar integration
      */
@@ -719,6 +732,9 @@
 
         @Override
         public void onMenuModeChange(MenuBuilder menu) {
+            if (mMenuBuilderCallback != null) {
+                mMenuBuilderCallback.onMenuModeChange(menu);
+            }
         }
     }
 
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 57b8dcb..033b99a 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -52,12 +52,15 @@
     private static final String TAG = "EdgeEffect";
 
     // Time it will take the effect to fully recede in ms
-    private static final int RECEDE_TIME = 1000;
+    private static final int RECEDE_TIME = 600;
 
     // Time it will take before a pulled glow begins receding in ms
     private static final int PULL_TIME = 167;
 
-    private static final float MAX_ALPHA = 1.f;
+    // Time it will take in ms for a pulled glow to decay to partial strength before release
+    private static final int PULL_DECAY_TIME = 2000;
+
+    private static final float MAX_ALPHA = 0.5f;
 
     private static final float MAX_GLOW_SCALE = 2.f;
 
@@ -93,12 +96,9 @@
     private static final int STATE_RECEDE = 3;
     private static final int STATE_PULL_DECAY = 4;
 
-    // How much dragging should effect the height of the glow image.
-    // Number determined by user testing.
-    private static final int PULL_DISTANCE_GLOW_FACTOR = 7;
-    private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f;
+    private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
 
-    private static final int VELOCITY_GLOW_FACTOR = 12;
+    private static final int VELOCITY_GLOW_FACTOR = 6;
 
     private int mState = STATE_IDLE;
 
@@ -107,7 +107,7 @@
     private final Rect mBounds = new Rect();
     private final Paint mPaint = new Paint();
     private float mRadius;
-    private float mBaseGlowHeight;
+    private float mBaseGlowScale;
     private float mDisplacement = 0.5f;
     private float mTargetDisplacement = 0.5f;
 
@@ -138,8 +138,12 @@
         final float r = width * 0.75f / SIN;
         final float y = COS * r;
         final float h = r - y;
+        final float or = height * 0.75f / SIN;
+        final float oy = COS * or;
+        final float oh = or - oy;
+
         mRadius = r;
-        mBaseGlowHeight = h;
+        mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
 
         mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
     }
@@ -319,13 +323,14 @@
         final float centerX = mBounds.centerX();
         final float centerY = mBounds.height() - mRadius;
 
-        canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0);
+        canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, centerX, 0);
 
         final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
         float translateX = mBounds.width() * displacement / 2;
 
         canvas.clipRect(mBounds);
         canvas.translate(translateX, 0);
+        mPaint.setAlpha((int) (0xff * mGlowAlpha));
         canvas.drawCircle(centerX, centerY, mRadius, mPaint);
         canvas.restoreToCount(count);
 
@@ -372,7 +377,16 @@
                     mGlowScaleYFinish = 0.f;
                     break;
                 case STATE_PULL:
-                    // Hold in this state until explicitly released.
+                    mState = STATE_PULL_DECAY;
+                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
+                    mDuration = PULL_DECAY_TIME;
+
+                    mGlowAlphaStart = mGlowAlpha;
+                    mGlowScaleYStart = mGlowScaleY;
+
+                    // After pull, the glow should fade to nothing.
+                    mGlowAlphaFinish = 0.f;
+                    mGlowScaleYFinish = 0.f;
                     break;
                 case STATE_PULL_DECAY:
                     mState = STATE_RECEDE;
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 3ba03b8..be28199 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -37,6 +37,7 @@
 import android.view.ViewGroup;
 
 import com.android.internal.R;
+import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuItemImpl;
 import com.android.internal.view.menu.MenuPresenter;
@@ -153,6 +154,8 @@
     private ToolbarWidgetWrapper mWrapper;
     private ActionMenuPresenter mOuterActionMenuPresenter;
     private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
+    private MenuPresenter.Callback mActionMenuPresenterCallback;
+    private MenuBuilder.Callback mMenuBuilderCallback;
 
     private boolean mCollapsible;
 
@@ -825,6 +828,7 @@
             mMenuView = new ActionMenuView(getContext());
             mMenuView.setPopupTheme(mPopupTheme);
             mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener);
+            mMenuView.setMenuCallbacks(mActionMenuPresenterCallback, mMenuBuilderCallback);
             final LayoutParams lp = generateDefaultLayoutParams();
             lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
             mMenuView.setLayoutParams(lp);
@@ -1678,6 +1682,15 @@
     }
 
     /**
+     * Must be called before the menu is accessed
+     * @hide
+     */
+    public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
+        mActionMenuPresenterCallback = pcb;
+        mMenuBuilderCallback = mcb;
+    }
+
+    /**
      * Interface responsible for receiving menu item click events if the items themselves
      * do not have individual item click listeners.
      */
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index 6f1c7ec..99c87ea 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -35,6 +35,7 @@
 import android.widget.Toolbar;
 import com.android.internal.R;
 import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPresenter;
 import com.android.internal.widget.DecorToolbar;
 import com.android.internal.widget.ToolbarWidgetWrapper;
 
@@ -45,6 +46,7 @@
     private DecorToolbar mDecorToolbar;
     private boolean mToolbarMenuPrepared;
     private Window.Callback mWindowCallback;
+    private boolean mMenuCallbackSet;
 
     private CharSequence mHomeDescription;
 
@@ -453,6 +455,10 @@
     }
 
     void populateOptionsMenu() {
+        if (!mMenuCallbackSet) {
+            mToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(), new MenuBuilderCallback());
+            mMenuCallbackSet = true;
+        }
         final Menu menu = mToolbar.getMenu();
         final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null;
         if (mb != null) {
@@ -514,4 +520,51 @@
             return result;
         }
     }
+
+    private final class ActionMenuPresenterCallback implements MenuPresenter.Callback {
+        private boolean mClosingActionMenu;
+
+        @Override
+        public boolean onOpenSubMenu(MenuBuilder subMenu) {
+            if (mWindowCallback != null) {
+                mWindowCallback.onMenuOpened(Window.FEATURE_ACTION_BAR, subMenu);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+            if (mClosingActionMenu) {
+                return;
+            }
+
+            mClosingActionMenu = true;
+            mToolbar.dismissPopupMenus();
+            if (mWindowCallback != null) {
+                mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu);
+            }
+            mClosingActionMenu = false;
+        }
+    }
+
+    private final class MenuBuilderCallback implements MenuBuilder.Callback {
+
+        @Override
+        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+            return false;
+        }
+
+        @Override
+        public void onMenuModeChange(MenuBuilder menu) {
+            if (mWindowCallback != null) {
+                if (mToolbar.isOverflowMenuShowing()) {
+                    mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu);
+                } else if (mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL,
+                        null, menu)) {
+                    mWindowCallback.onMenuOpened(Window.FEATURE_ACTION_BAR, menu);
+                }
+            }
+        }
+    }
 }
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index e6bc4db..25caae3 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -54,6 +54,7 @@
     convexMask = NULL;
     caches.resourceCache.incrementRefcount(this);
     rendererLightPosDirty = true;
+    wasBuildLayered = false;
     renderState.registerLayer(this);
 }
 
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 38c29c7..911b99880 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -306,6 +306,7 @@
     Rect dirtyRect;
     bool debugDrawUpdate;
     bool hasDrawnSinceUpdate;
+    bool wasBuildLayered;
 
 private:
     void requireRenderer();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 0f36c06..dbd273d 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -605,6 +605,12 @@
     AutoFence fence;
 }
 
+void OpenGLRenderer::markLayersAsBuildLayers() {
+    for (size_t i = 0; i < mLayerUpdates.size(); i++) {
+        mLayerUpdates[i]->wasBuildLayered = true;
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // State management
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index e9ca5d9..e295b1a 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -150,6 +150,7 @@
     void cancelLayerUpdate(Layer* layer);
     void clearLayerUpdates();
     void flushLayerUpdates();
+    void markLayersAsBuildLayers();
 
     virtual int saveLayer(float left, float top, float right, float bottom,
             const SkPaint* paint, int flags) {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 8fb1f64..a10e70f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -58,6 +58,19 @@
     fflush(file);
 }
 
+void RenderNode::debugDumpLayers(const char* prefix) {
+    if (mLayer) {
+        ALOGD("%sNode %p (%s) has layer %p (fbo = %u, wasBuildLayered = %s)",
+                prefix, this, getName(), mLayer, mLayer->getFbo(),
+                mLayer->wasBuildLayered ? "true" : "false");
+    }
+    if (mDisplayListData) {
+        for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
+            mDisplayListData->children()[i]->mRenderNode->debugDumpLayers(prefix);
+        }
+    }
+}
+
 RenderNode::RenderNode()
         : mDirtyPropertyFields(0)
         , mNeedsDisplayListDataSync(false)
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index d897997..f329283 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -102,6 +102,7 @@
     };
 
     ANDROID_API static void outputLogBuffer(int fd);
+    void debugDumpLayers(const char* prefix);
 
     ANDROID_API void setStagingDisplayList(DisplayListData* newData);
 
diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp
index 9948b44..a7c5e85 100644
--- a/libs/hwui/RenderState.cpp
+++ b/libs/hwui/RenderState.cpp
@@ -15,6 +15,8 @@
  */
 #include "RenderState.h"
 
+#include "renderthread/CanvasContext.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -38,6 +40,17 @@
 void RenderState::onGLContextDestroyed() {
     if (CC_UNLIKELY(!mActiveLayers.empty())) {
         mCaches->dumpMemoryUsage();
+        for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin();
+                cit != mRegisteredContexts.end(); cit++) {
+            renderthread::CanvasContext* context = *cit;
+            ALOGD("Context: %p (root = %p)", context, context->mRootRenderNode.get());
+            ALOGD("  Prefeteched layers: %zu", context->mPrefetechedLayers.size());
+            for (std::set<RenderNode*>::iterator pit = context->mPrefetechedLayers.begin();
+                    pit != context->mPrefetechedLayers.end(); pit++) {
+                (*pit)->debugDumpLayers("    ");
+            }
+            context->mRootRenderNode->debugDumpLayers("  ");
+        }
         LOG_ALWAYS_FATAL("layers have survived gl context destruction");
     }
 }
diff --git a/libs/hwui/RenderState.h b/libs/hwui/RenderState.h
index 3915fb5..c7ab197 100644
--- a/libs/hwui/RenderState.h
+++ b/libs/hwui/RenderState.h
@@ -29,6 +29,7 @@
 namespace uirenderer {
 
 namespace renderthread {
+class CanvasContext;
 class RenderThread;
 }
 
@@ -57,6 +58,14 @@
         mActiveLayers.erase(layer);
     }
 
+    void registerCanvasContext(renderthread::CanvasContext* context) {
+        mRegisteredContexts.insert(context);
+    }
+
+    void unregisterCanvasContext(renderthread::CanvasContext* context) {
+        mRegisteredContexts.erase(context);
+    }
+
 private:
     friend class renderthread::RenderThread;
     friend class Caches;
@@ -69,6 +78,7 @@
 
     Caches* mCaches;
     std::set<const Layer*> mActiveLayers;
+    std::set<renderthread::CanvasContext*> mRegisteredContexts;
 
     GLsizei mViewportWidth;
     GLsizei mViewportHeight;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 428e426..4129a89 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -48,11 +48,13 @@
         , mHaveNewSurface(false)
         , mRootRenderNode(rootRenderNode) {
     mAnimationContext = contextFactory->createAnimationContext(mRenderThread.timeLord());
+    mRenderThread.renderState().registerCanvasContext(this);
 }
 
 CanvasContext::~CanvasContext() {
     destroy();
     delete mAnimationContext;
+    mRenderThread.renderState().unregisterCanvasContext(this);
 }
 
 void CanvasContext::destroy() {
@@ -299,6 +301,7 @@
     // purposes when the frame is actually drawn
     node->setPropertyFieldsDirty(RenderNode::GENERIC);
 
+    mCanvas->markLayersAsBuildLayers();
     mCanvas->flushLayerUpdates();
 
     node->incStrong(0);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 5984fd0..2460f6b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -42,6 +42,7 @@
 class OpenGLRenderer;
 class Rect;
 class Layer;
+class RenderState;
 
 namespace renderthread {
 
@@ -95,6 +96,9 @@
 
 private:
     friend class RegisterFrameCallbackTask;
+    // TODO: Replace with something better for layer & other GL object
+    // lifecycle tracking
+    friend class android::uirenderer::RenderState;
 
     void setSurface(ANativeWindow* window);
     void swapBuffers();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 0b91e9d..c461f3a 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -40,6 +40,7 @@
 
 namespace renderthread {
 
+class CanvasContext;
 class DispatchFrameCallbacks;
 class EglManager;
 class RenderProxy;
diff --git a/location/java/android/location/SettingInjectorService.java b/location/java/android/location/SettingInjectorService.java
index 98c7864..fcd2cde 100644
--- a/location/java/android/location/SettingInjectorService.java
+++ b/location/java/android/location/SettingInjectorService.java
@@ -196,10 +196,7 @@
      * @deprecated not called any more
      */
     @Deprecated
-    protected String onGetSummary() {
-        // Do not delete until no callers have @Override annotations for this method
-        return null;
-    }
+    protected abstract String onGetSummary();
 
     /**
      * Returns the {@link android.preference.Preference#isEnabled()} value. Should not perform
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index af5c13d..ac0ca0a 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -708,7 +708,6 @@
                      // Send an event to the end of the drag gesture.
                      sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
                  }
-                 mCurrentState = STATE_TOUCH_EXPLORING;
             } break;
             case MotionEvent.ACTION_UP: {
                 mAms.onTouchInteractionEnd();
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 2ef806f..d05de69 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -440,12 +440,10 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Permission Denial: can't dump from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-        }
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
+                                                "Permission Denial: can't dump from from pid="
+                                                + Binder.getCallingPid()
+                                                + ", uid=" + Binder.getCallingUid());
 
         synchronized (mLock) {
             int N = mProviders.size();
@@ -4038,4 +4036,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 65ad1ce..b452a38 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -193,7 +193,7 @@
     private final class BinderService extends IMms.Stub {
         @Override
         public void sendMessage(long subId, String callingPkg, Uri contentUri,
-                String locationUrl, ContentValues configOverrides, PendingIntent sentIntent)
+                String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
                         throws RemoteException {
             mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
             if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
@@ -206,7 +206,7 @@
 
         @Override
         public void downloadMessage(long subId, String callingPkg, String locationUrl,
-                Uri contentUri, ContentValues configOverrides,
+                Uri contentUri, Bundle configOverrides,
                 PendingIntent downloadedIntent) throws RemoteException {
             mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
                     "Download MMS message");
@@ -333,7 +333,7 @@
 
         @Override
         public void sendStoredMessage(long subId, String callingPkg, Uri messageUri,
-                ContentValues configOverrides, PendingIntent sentIntent) throws RemoteException {
+                Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
             mContext.enforceCallingPermission(Manifest.permission.SEND_SMS,
                     "Send stored MMS message");
             if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e83963d..81c379a 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -241,6 +241,9 @@
     /** Run all ActivityStacks through this */
     final ActivityStackSupervisor mStackSupervisor;
 
+    /** Used to keep resumeTopActivityLocked() from being entered recursively */
+    private boolean inResumeTopActivity;
+
     static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
     static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
     static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -1439,6 +1442,23 @@
     }
 
     final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
+        if (inResumeTopActivity) {
+            // Don't even start recursing.
+            return false;
+        }
+
+        boolean result = false;
+        try {
+            // Protect against recursion.
+            inResumeTopActivity = true;
+            result = resumeTopActivityInnerLocked(prev, options);
+        } finally {
+            inResumeTopActivity = false;
+        }
+        return result;
+    }
+
+    final boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
         if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
 
         ActivityRecord parent = mActivityContainer.mParentActivity;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index bb22b4d..c5a6dbd 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -31,8 +31,6 @@
 
 import libcore.util.EmptyArray;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -102,11 +100,6 @@
     // Stores the local CEC devices in the system. Device type is used for key.
     private final SparseArray<HdmiCecLocalDevice> mLocalDevices = new SparseArray<>();
 
-    @IoThreadOnly
-    private final HdmiLogger mIoThreadLogger = new HdmiLogger(TAG);
-    @ServiceThreadOnly
-    private final HdmiLogger mServiceThreadLogger = new HdmiLogger(TAG);
-
     // Private constructor.  Use HdmiCecController.create().
     private HdmiCecController(HdmiControlService service) {
         mService = service;
@@ -210,9 +203,8 @@
         }
 
         final int assignedAddress = logicalAddress;
-        mIoThreadLogger.debug(
-                String.format("New logical address for device [%d]: [preferred:%d, assigned:%d]",
-                        deviceType, preferredAddress, assignedAddress));
+        HdmiLogger.debug("New logical address for device [%d]: [preferred:%d, assigned:%d]",
+                        deviceType, preferredAddress, assignedAddress);
         if (callback != null) {
             runOnServiceThread(new Runnable() {
                 @Override
@@ -449,7 +441,7 @@
                         allocated.add(address);
                     }
                 }
-                mIoThreadLogger.debug("[P]:Allocated Address=" + allocated);
+                HdmiLogger.debug("[P]:Allocated Address=" + allocated);
                 if (callback != null) {
                     runOnServiceThread(new Runnable() {
                         @Override
@@ -551,7 +543,7 @@
         runOnIoThread(new Runnable() {
             @Override
             public void run() {
-                mIoThreadLogger.debug("[S]:" + cecMessage);
+                HdmiLogger.debug("[S]:" + cecMessage);
                 byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
                 int i = 0;
                 int errorCode = Constants.SEND_RESULT_SUCCESS;
@@ -586,7 +578,7 @@
     private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
         assertRunOnServiceThread();
         HdmiCecMessage command = HdmiCecMessageBuilder.of(srcAddress, dstAddress, body);
-        mServiceThreadLogger.debug("[R]:" + command);
+        HdmiLogger.debug("[R]:" + command);
         onReceiveCommand(command);
     }
 
@@ -596,8 +588,7 @@
     @ServiceThreadOnly
     private void handleHotplug(int port, boolean connected) {
         assertRunOnServiceThread();
-        mServiceThreadLogger.debug(
-                "Hotplug event:[port:" + port + " , connected:" + connected + "]");
+        HdmiLogger.debug("Hotplug event:[port:%d, connected:%b]", port, connected);
         mService.onHotplug(port, connected);
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 85f5be2..b2300a6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -43,9 +43,6 @@
  */
 abstract class HdmiCecFeatureAction {
     private static final String TAG = "HdmiCecFeatureAction";
-    // As all actions run in the same thread (service thread), it's fine to have single logger.
-    // TODO: create global logger for each threads and use them.
-    protected static final HdmiLogger DLOGGER = new HdmiLogger(TAG);
 
     // Timer handler message used for timeout event
     protected static final int MSG_TIMEOUT = 100;
@@ -264,10 +261,7 @@
     }
 
     protected final void sendUserControlPressedAndReleased(int targetAddress, int uiCommand) {
-        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
-                getSourceAddress(), targetAddress, uiCommand));
-        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
-                getSourceAddress(), targetAddress));
+        mSource.sendUserControlPressedAndReleased(targetAddress, uiCommand);
     }
 
     protected final void addOnFinishedCallback(HdmiCecFeatureAction action, Runnable runnable) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index bc6a299..04e38dc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -810,6 +810,13 @@
         Slog.w(TAG, "sendKeyEvent not implemented");
     }
 
+    void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
+        mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlPressed(
+                mAddress, targetAddress, cecKeycode));
+        mService.sendCecCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+                mAddress, targetAddress));
+    }
+
     /**
      * Dump internal status of HdmiCecLocalDevice object.
      */
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7ae2198..5994c76 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -120,8 +120,6 @@
     // other CEC devices since they might not have logical address.
     private final ArraySet<Integer> mCecSwitches = new ArraySet<Integer>();
 
-    private final HdmiLogger mSafeLogger = new HdmiLogger(TAG);
-
     HdmiCecLocalDeviceTv(HdmiControlService service) {
         super(service, HdmiDeviceInfo.DEVICE_TV);
         mPrevPortId = Constants.INVALID_PORT_ID;
@@ -700,8 +698,7 @@
 
     // # Seq 25
     void setSystemAudioMode(boolean on, boolean updateSetting) {
-        mSafeLogger.debug(String.format("System Audio Mode change[old:%b new:%b]",
-                mSystemAudioActivated, on));
+        HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
 
         if (updateSetting) {
             mService.writeBooleanSetting(Global.HDMI_SYSTEM_AUDIO_ENABLED, on);
@@ -832,6 +829,8 @@
                     AudioManager.STREAM_MUSIC);
             mService.setAudioStatus(mute,
                     VolumeControlAction.scaleToCustomVolume(volume, maxVolume));
+            displayOsd(HdmiControlManager.OSD_MESSAGE_AVR_VOLUME_CHANGED,
+                    mute ? HdmiControlManager.AVR_VOLUME_MUTED : volume);
         }
     }
 
@@ -855,12 +854,13 @@
             }
         }
 
-        // Remove existing volume action.
-        removeAction(VolumeControlAction.class);
-
-        HdmiDeviceInfo avr = getAvrDeviceInfo();
-        addAndStartAction(VolumeControlAction.ofVolumeChange(this, avr.getLogicalAddress(),
-                cecVolume, delta > 0));
+        List<VolumeControlAction> actions = getActions(VolumeControlAction.class);
+        if (actions.isEmpty()) {
+            addAndStartAction(new VolumeControlAction(this,
+                    getAvrDeviceInfo().getLogicalAddress(), delta > 0));
+        } else {
+            actions.get(0).handleVolumeChange(delta > 0);
+        }
     }
 
     @ServiceThreadOnly
@@ -872,8 +872,9 @@
 
         // Remove existing volume action.
         removeAction(VolumeControlAction.class);
-        HdmiDeviceInfo avr = getAvrDeviceInfo();
-        addAndStartAction(VolumeControlAction.ofMute(this, avr.getLogicalAddress(), mute));
+        sendUserControlPressedAndReleased(getAvrDeviceInfo().getLogicalAddress(),
+                mute ? HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION :
+                        HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
     }
 
     @Override
@@ -935,7 +936,7 @@
     protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
         assertRunOnServiceThread();
         if (!isMessageForSystemAudio(message)) {
-            mSafeLogger.warning("Invalid <Set System Audio Mode> message:" + message);
+            HdmiLogger.warning("Invalid <Set System Audio Mode> message:" + message);
             return false;
         }
         SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
@@ -949,7 +950,7 @@
     protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
         if (!isMessageForSystemAudio(message)) {
-            mSafeLogger.warning("Invalid <System Audio Mode Status> message:" + message);
+            HdmiLogger.warning("Invalid <System Audio Mode Status> message:" + message);
             return false;
         }
         setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message), true);
@@ -1445,6 +1446,12 @@
         mService.displayOsd(messageId);
     }
 
+    @ServiceThreadOnly
+    void displayOsd(int messageId, int extra) {
+        assertRunOnServiceThread();
+        mService.displayOsd(messageId, extra);
+    }
+
     // Seq #54 and #55
     @ServiceThreadOnly
     void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 8b345cf..0b3d9fb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -51,7 +51,6 @@
     }
 
     final SparseArray<ValidationInfo> mValidationInfo = new SparseArray<>();
-    private final HdmiLogger mSpamSafeLogger = new HdmiLogger(TAG);
 
     public HdmiCecMessageValidator(HdmiControlService service) {
         mService = service;
@@ -183,32 +182,32 @@
         int opcode = message.getOpcode();
         ValidationInfo info = mValidationInfo.get(opcode);
         if (info == null) {
-            mSpamSafeLogger.warning("No validation information for the message: " + message);
+            HdmiLogger.warning("No validation information for the message: " + message);
             return true;
         }
 
         // Check the source field.
         if (message.getSource() == Constants.ADDR_UNREGISTERED &&
                 (info.addressType & SRC_UNREGISTERED) == 0) {
-            mSpamSafeLogger.warning("Unexpected source: " + message);
+            HdmiLogger.warning("Unexpected source: " + message);
             return false;
         }
         // Check the destination field.
         if (message.getDestination() == Constants.ADDR_BROADCAST) {
             if ((info.addressType & DEST_BROADCAST) == 0) {
-                mSpamSafeLogger.warning("Unexpected broadcast message: " + message);
+                HdmiLogger.warning("Unexpected broadcast message: " + message);
                 return false;
             }
         } else {  // Direct addressing.
             if ((info.addressType & DEST_DIRECT) == 0) {
-                mSpamSafeLogger.warning("Unexpected direct message: " + message);
+                HdmiLogger.warning("Unexpected direct message: " + message);
                 return false;
             }
         }
 
         // Check the parameter type.
         if (!info.parameterValidator.isValid(message.getParams())) {
-            mSpamSafeLogger.warning("Unexpected parameters: " + message);
+            HdmiLogger.warning("Unexpected parameters: " + message);
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9314cf8..38c6fb3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -166,8 +166,6 @@
     // Type of logical devices hosted in the system. Stored in the unmodifiable list.
     private final List<Integer> mLocalDevices;
 
-    private final HdmiLogger mSpamSafeLogger = new HdmiLogger(TAG);
-
     // List of records for hotplug event listener to handle the the caller killed in action.
     @GuardedBy("mLock")
     private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
@@ -656,7 +654,7 @@
         if (mMessageValidator.isValid(command)) {
             mCecController.sendCommand(command, callback);
         } else {
-            mSpamSafeLogger.error("Invalid message type:" + command);
+            HdmiLogger.error("Invalid message type:" + command);
             if (callback != null) {
                 callback.onSendCompleted(Constants.SEND_RESULT_FAILURE);
             }
@@ -705,7 +703,7 @@
         }
 
         if (message.getDestination() != Constants.ADDR_BROADCAST) {
-            mSpamSafeLogger.warning("Unhandled cec command:" + message);
+            HdmiLogger.warning("Unhandled cec command:" + message);
         }
         return false;
     }
@@ -794,7 +792,6 @@
             // FLAG_HDMI_SYSTEM_AUDIO_VOLUME prevents audio manager from announcing
             // volume change notification back to hdmi control service.
             audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume,
-                    AudioManager.FLAG_SHOW_UI |
                     AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME);
         }
     }
@@ -2033,4 +2030,14 @@
         getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
                 HdmiControlService.PERMISSION);
     }
+
+    @ServiceThreadOnly
+    void displayOsd(int messageId, int extra) {
+        assertRunOnServiceThread();
+        Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
+        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
+        intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRAM_PARAM1, extra);
+        getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                HdmiControlService.PERMISSION);
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index c7add75..2562ffc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -26,44 +26,83 @@
 /**
  * A logger that prevents spammy log. For the same log message, it logs once every 20seconds.
  * This class is not thread-safe.
+ * <p>
+ * For convenience, use single character prefix for all messages.
+ * Here are common acronyms
+ * <ul>
+ *   <li>[T]: Timout
+ *   <li>[R]: Received message
+ *   <li>[S]: Sent message
+ *   <li>[P]: Device polling result
+ * </ul>
  */
 final class HdmiLogger {
+    private static final String TAG = "HDMI";
     // Logging duration for same error message.
     private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000;  // 20s
 
     private static final boolean DEBUG = false;
 
+    private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>();
+
     // Key (String): log message.
     // Value (Pair(Long, Integer)): a pair of last log time millis and the number of logMessage.
     // Cache for warning.
     private final HashMap<String, Pair<Long, Integer>> mWarningTimingCache = new HashMap<>();
     // Cache for error.
     private final HashMap<String, Pair<Long, Integer>> mErrorTimingCache = new HashMap<>();
-    private final String mTag;
 
-    HdmiLogger(String tag) {
-        mTag = "HDMI:" + tag;
+    private HdmiLogger() {
     }
 
-    void warning(String logMessage) {
+    static final void warning(String logMessage, Object... objs) {
+        getLogger().warningInternal(toLogString(logMessage, objs));
+    }
+
+    private void warningInternal(String logMessage) {
         String log = updateLog(mWarningTimingCache, logMessage);
         if (!log.isEmpty()) {
-            Slog.w(mTag, log);
+            Slog.w(TAG, log);
         }
     }
 
-    void error(String logMessage) {
+    static final void error(String logMessage, Object... objs) {
+        getLogger().errorInternal(toLogString(logMessage, objs));
+    }
+
+    private void errorInternal(String logMessage) {
         String log = updateLog(mErrorTimingCache, logMessage);
         if (!log.isEmpty()) {
-            Slog.e(mTag, log);
+            Slog.e(TAG, log);
         }
     }
 
-    void debug(String logMessage) {
+    static final void debug(String logMessage, Object... objs) {
+        getLogger().debugInternal(toLogString(logMessage, objs));
+    }
+
+    private void debugInternal(String logMessage) {
         if (!DEBUG) {
             return;
         }
-        Slog.d(mTag, logMessage);
+        Slog.d(TAG, logMessage);
+    }
+
+    private static final String toLogString(String logMessage, Object[] objs) {
+        if (objs.length > 0) {
+            return String.format(logMessage, objs);
+        } else {
+            return logMessage;
+        }
+    }
+
+    private static HdmiLogger getLogger() {
+        HdmiLogger logger = sLogger.get();
+        if (logger == null) {
+            logger = new HdmiLogger();
+            sLogger.set(logger);
+        }
+        return logger;
     }
 
     private static String updateLog(HashMap<String, Pair<Long, Integer>> cache, String logMessage) {
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 17c2d6c..3fb450f 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -85,6 +85,7 @@
         if (mState != state || state != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) {
             return;
         }
+        HdmiLogger.debug("[T]RequestArcAction.");
         disableArcTransmission();
         finish();
     }
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index f25363d..d9e1f24 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -35,15 +35,15 @@
 
     @Override
     boolean start() {
+        mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+        addTimer(mState, HdmiConfig.TIMEOUT_MS);
+
         HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(
                 getSourceAddress(), mAvrAddress);
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
-                if (error == Constants.SEND_RESULT_SUCCESS) {
-                    mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
-                    addTimer(mState, HdmiConfig.TIMEOUT_MS);
-                } else {
+                if (error != Constants.SEND_RESULT_SUCCESS) {
                     // If failed to send <Request ARC Initiation>, start "Disabled"
                     // ARC transmission action.
                     disableArcTransmission();
diff --git a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
index 1491c72..f5a0115 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcTerminationAction.java
@@ -35,15 +35,15 @@
 
     @Override
     boolean start() {
+        mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
+        addTimer(mState, HdmiConfig.TIMEOUT_MS);
+
         HdmiCecMessage command =
                 HdmiCecMessageBuilder.buildRequestArcTermination(getSourceAddress(), mAvrAddress);
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
-                if (error == Constants.SEND_RESULT_SUCCESS) {
-                    mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
-                    addTimer(mState, HdmiConfig.TIMEOUT_MS);
-                } else {
+                if (error != Constants.SEND_RESULT_SUCCESS) {
                     // If failed to send <Request ARC Termination>, start "Disabled" ARC
                     // transmission action.
                     disableArcTransmission();
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 30519f3..bffa854 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -53,6 +53,18 @@
     @Override
     boolean start() {
         if (mEnabled) {
+            // Enable ARC status immediately after sending <Report Arc Initiated>.
+            // If AVR responds with <Feature Abort>, disable ARC status again.
+            // This is different from spec that says that turns ARC status to
+            // "Enabled" if <Report ARC Initiated> is acknowledged and no
+            // <Feature Abort> is received.
+            // But implemented this way to save the time having to wait for
+            // <Feature Abort>.
+            setArcStatus(true);
+            // If succeeds to send <Report ARC Initiated>, wait general timeout
+            // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
+            mState = STATE_WAITING_TIMEOUT;
+            addTimer(mState, HdmiConfig.TIMEOUT_MS);
             sendReportArcInitiated();
         } else {
             setArcStatus(false);
@@ -67,23 +79,11 @@
         sendCommand(command, new HdmiControlService.SendMessageCallback() {
             @Override
             public void onSendCompleted(int error) {
-                if (error == Constants.SEND_RESULT_SUCCESS) {
-                    // Enable ARC status immediately after sending <Report Arc Initiated>.
-                    // If AVR responds with <Feature Abort>, disable ARC status again.
-                    // This is different from spec that says that turns ARC status to
-                    // "Enabled" if <Report ARC Initiated> is acknowledged and no
-                    // <Feature Abort> is received.
-                    // But implemented this way to save the time having to wait for
-                    // <Feature Abort>.
-                    setArcStatus(true);
-                    // If succeeds to send <Report ARC Initiated>, wait general timeout
-                    // to check whether there is no <Feature Abort> for <Report ARC Initiated>.
-                    mState = STATE_WAITING_TIMEOUT;
-                    addTimer(mState, HdmiConfig.TIMEOUT_MS);
-                } else {
+                if (error != Constants.SEND_RESULT_SUCCESS) {
                     // If fails to send <Report ARC Initiated>, disable ARC and
                     // send <Report ARC Terminated> directly.
                     setArcStatus(false);
+                    HdmiLogger.debug("Failed to send <Report Arc Initiated>.");
                     finish();
                 }
             }
@@ -112,6 +112,7 @@
         if (opcode == Constants.MESSAGE_FEATURE_ABORT) {
             int originalOpcode = cmd.getParams()[0] & 0xFF;
             if (originalOpcode == Constants.MESSAGE_REPORT_ARC_INITIATED) {
+                HdmiLogger.debug("Feature aborted for <Report Arc Initiated>");
                 setArcStatus(false);
                 finish();
                 return true;
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index d15ffb0..0871194 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -98,7 +98,7 @@
             @Override
             public void onSendCompleted(int error) {
                 if (error != Constants.SEND_RESULT_SUCCESS) {
-                    DLOGGER.debug("Failed to send <System Audio Mode Request>:" + error);
+                    HdmiLogger.debug("Failed to send <System Audio Mode Request>:" + error);
                     setSystemAudioMode(false);
                     finishWithCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED);
                 }
@@ -111,7 +111,7 @@
     private void handleSendSystemAudioModeRequestTimeout() {
         if (!mTargetAudioStatus  // Don't retry for Off case.
                 || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
-            DLOGGER.debug("[T]:wait for <Set System Audio Mode>.");
+            HdmiLogger.debug("[T]:wait for <Set System Audio Mode>.");
             setSystemAudioMode(false);
             finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
             return;
@@ -130,7 +130,7 @@
                 if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
                         && (cmd.getParams()[0] & 0xFF)
                                 == Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST) {
-                    DLOGGER.debug("Failed to start system audio mode request.");
+                    HdmiLogger.debug("Failed to start system audio mode request.");
                     setSystemAudioMode(false);
                     finishWithCallback(HdmiControlManager.RESULT_EXCEPTION);
                     return true;
@@ -145,7 +145,7 @@
                     startAudioStatusAction();
                     return true;
                 } else {
-                    DLOGGER.debug("Unexpected system audio mode request:" + receivedStatus);
+                    HdmiLogger.debug("Unexpected system audio mode request:" + receivedStatus);
                     // Unexpected response, consider the request is newly initiated by AVR.
                     // To return 'false' will initiate new SystemAudioActionFromAvr by the control
                     // service.
diff --git a/services/core/java/com/android/server/hdmi/VolumeControlAction.java b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
index ddc267a..4338fc7 100644
--- a/services/core/java/com/android/server/hdmi/VolumeControlAction.java
+++ b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
@@ -17,66 +17,35 @@
 package com.android.server.hdmi;
 
 import static com.android.server.hdmi.Constants.IRT_MS;
+import static com.android.server.hdmi.Constants.MESSAGE_FEATURE_ABORT;
+import static com.android.server.hdmi.Constants.MESSAGE_REPORT_AUDIO_STATUS;
+import static com.android.server.hdmi.Constants.MESSAGE_USER_CONTROL_PRESSED;
 
-import com.android.internal.util.Preconditions;
+import android.media.AudioManager;
 
 /**
  * Feature action that transmits volume change to Audio Receiver.
  * <p>
- * This action is created when a user pressed volume up/down. However, Since Android only provides a
- * listener for delta of some volume change, we will set a target volume, and check reported volume
- * from Audio Receiver(AVR). If TV receives no &lt;Report Audio Status&gt; from AVR, this action
- * will be finished in {@link #IRT_MS} * {@link #VOLUME_CHANGE_TIMEOUT_MAX_COUNT} (ms).
+ * This action is created when a user pressed volume up/down. However, Android only provides a
+ * listener for delta of some volume change instead of individual key event. Also it's hard to know
+ * Audio Receiver's number of volume steps for a single volume control key. Because of this, it
+ * sends key-down event until IRT timeout happens, and it will send key-up event if no additional
+ * volume change happens; otherwise, it will send again key-down as press and hold feature does.
  */
 final class VolumeControlAction extends HdmiCecFeatureAction {
     private static final String TAG = "VolumeControlAction";
 
-    private static final int VOLUME_MUTE = 101;
-    private static final int VOLUME_RESTORE = 102;
+    // State that wait for next volume press.
+    private static final int STATE_WAIT_FOR_NEXT_VOLUME_PRESS = 1;
     private static final int MAX_VOLUME = 100;
-    private static final int MIN_VOLUME = 0;
 
-    // State where to wait for <Report Audio Status>
-    private static final int STATE_WAIT_FOR_REPORT_VOLUME_STATUS = 1;
-
-    // Maximum count of time out used to finish volume action.
-    private static final int VOLUME_CHANGE_TIMEOUT_MAX_COUNT = 2;
+    private static final int UNKNOWN_AVR_VOLUME = -1;
 
     private final int mAvrAddress;
-    private final int mTargetVolume;
-    private final boolean mIsVolumeUp;
-    private int mTimeoutCount;
-
-    /**
-     * Create a {@link VolumeControlAction} for mute/restore change
-     *
-     * @param source source device sending volume change
-     * @param avrAddress address of audio receiver
-     * @param mute whether to mute sound or not. {@code true} for mute on; {@code false} for mute
-     *            off, i.e restore volume
-     * @return newly created {@link VolumeControlAction}
-     */
-    public static VolumeControlAction ofMute(HdmiCecLocalDevice source, int avrAddress,
-            boolean mute) {
-        return new VolumeControlAction(source, avrAddress, mute ? VOLUME_MUTE : VOLUME_RESTORE,
-                false);
-    }
-
-    /**
-     * Create a {@link VolumeControlAction} for volume up/down change
-     *
-     * @param source source device sending volume change
-     * @param avrAddress address of audio receiver
-     * @param targetVolume target volume to be set to AVR. It should be in range of [0-100]
-     * @param isVolumeUp whether to volume up or not. {@code true} for volume up; {@code false} for
-     *            volume down
-     * @return newly created {@link VolumeControlAction}
-     */
-    public static VolumeControlAction ofVolumeChange(HdmiCecLocalDevice source, int avrAddress,
-            int targetVolume, boolean isVolumeUp) {
-        Preconditions.checkArgumentInRange(targetVolume, MIN_VOLUME, MAX_VOLUME, "volume");
-        return new VolumeControlAction(source, avrAddress, targetVolume, isVolumeUp);
-    }
+    private boolean mIsVolumeUp;
+    private long mLastKeyUpdateTime;
+    private int mLastAvrVolume;
+    private boolean mSentKeyPressed;
 
     /**
      * Scale a custom volume value to cec volume scale.
@@ -94,123 +63,141 @@
      *
      * @param cecVolume volume value in cec volume scale. It should be in a range of [0-100]
      * @param scale scale of custom volume (max volume)
-     * @return a volume value scaled to custom volume range
+     * @return a volume scaled to custom volume range
      */
     public static int scaleToCustomVolume(int cecVolume, int scale) {
         return (cecVolume * scale) / MAX_VOLUME;
     }
 
-    private VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, int targetVolume,
-            boolean isVolumeUp) {
+    VolumeControlAction(HdmiCecLocalDevice source, int avrAddress, boolean isVolumeUp) {
         super(source);
-
         mAvrAddress = avrAddress;
-        mTargetVolume = targetVolume;
         mIsVolumeUp = isVolumeUp;
+        mLastAvrVolume = UNKNOWN_AVR_VOLUME;
+        mSentKeyPressed = false;
+
+        updateLastKeyUpdateTime();
+    }
+
+    private void updateLastKeyUpdateTime() {
+        mLastKeyUpdateTime = System.currentTimeMillis();
     }
 
     @Override
     boolean start() {
-        if (isForMute()) {
-            sendMuteChange(mTargetVolume == VOLUME_MUTE);
-            finish();
-            return true;
-        }
-
-        startVolumeChange();
+        mState = STATE_WAIT_FOR_NEXT_VOLUME_PRESS;
+        sendVolumeKeyPressed();
+        resetTimer();
         return true;
     }
 
-
-    private boolean isForMute() {
-        return mTargetVolume == VOLUME_MUTE || mTargetVolume == VOLUME_RESTORE;
-    }
-
-    private void startVolumeChange() {
-        mTimeoutCount = 0;
-        sendVolumeChange(mIsVolumeUp);
-        mState = STATE_WAIT_FOR_REPORT_VOLUME_STATUS;
-        addTimer(mState, IRT_MS);
-    }
-
-    private void sendVolumeChange(boolean up) {
+    private void sendVolumeKeyPressed() {
         sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), mAvrAddress,
-                up ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
+                mIsVolumeUp ? HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
                         : HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN));
+        mSentKeyPressed = true;
     }
 
-    private void sendMuteChange(boolean mute) {
-        sendUserControlPressedAndReleased(mAvrAddress,
-                mute ? HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION :
-                        HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
+    private void resetTimer() {
+        mActionTimer.clearTimerMessage();
+        addTimer(STATE_WAIT_FOR_NEXT_VOLUME_PRESS, IRT_MS);
+    }
+
+    void handleVolumeChange(boolean isVolumeUp) {
+        if (mIsVolumeUp != isVolumeUp) {
+            HdmiLogger.debug("Volume Key Status Changed[old:%b new:%b]", mIsVolumeUp, isVolumeUp);
+            sendVolumeKeyReleased();
+            mIsVolumeUp = isVolumeUp;
+        }
+        updateLastKeyUpdateTime();
+    }
+
+    private void sendVolumeKeyReleased() {
+        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+                getSourceAddress(), mAvrAddress));
+        mSentKeyPressed = false;
     }
 
     @Override
     boolean processCommand(HdmiCecMessage cmd) {
-        if (mState != STATE_WAIT_FOR_REPORT_VOLUME_STATUS) {
+        if (mState != STATE_WAIT_FOR_NEXT_VOLUME_PRESS || cmd.getSource() != mAvrAddress) {
             return false;
         }
 
         switch (cmd.getOpcode()) {
-            case Constants.MESSAGE_REPORT_AUDIO_STATUS:
-                handleReportAudioStatus(cmd);
-                return true;
-            case Constants.MESSAGE_FEATURE_ABORT:
-                int originalOpcode = cmd.getParams()[0] & 0xFF;
-                if (originalOpcode == Constants.MESSAGE_USER_CONTROL_PRESSED
-                        || originalOpcode == Constants.MESSAGE_USER_CONTROL_RELEASED) {
-                    // TODO: handle feature abort.
-                    finish();
-                    return true;
-                }
-            default:  // fall through
+            case MESSAGE_REPORT_AUDIO_STATUS:
+                return handleReportAudioStatus(cmd);
+            case MESSAGE_FEATURE_ABORT:
+                return handleFeatureAbort(cmd);
+            default:
                 return false;
         }
     }
 
-    private void handleReportAudioStatus(HdmiCecMessage cmd) {
-        byte[] params = cmd.getParams();
+    private boolean handleReportAudioStatus(HdmiCecMessage cmd) {
+        byte params[] = cmd.getParams();
+        boolean mute = (params[0] & 0x80) == 0x80;
         int volume = params[0] & 0x7F;
-        // Update volume with new value.
-        // Note that it will affect system volume change.
-        tv().setAudioStatus(false, volume);
-        if (mIsVolumeUp) {
-            if (mTargetVolume <= volume) {
-                finishWithVolumeChangeRelease();
-                return;
-            }
-        } else {
-            if (mTargetVolume >= volume) {
-                finishWithVolumeChangeRelease();
-                return;
-            }
+        mLastAvrVolume = volume;
+        if (shouldUpdateAudioVolume(mute)) {
+            HdmiLogger.debug("Force volume change[mute:%b, volume=%d]", mute, volume);
+            tv().setAudioStatus(mute, volume);
         }
-
-        // Clear action status and send another volume change command.
-        clear();
-        startVolumeChange();
+        return true;
     }
 
-    private void finishWithVolumeChangeRelease() {
-        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
-                getSourceAddress(), mAvrAddress));
-        finish();
+    private boolean shouldUpdateAudioVolume(boolean mute) {
+        // Do nothing if in mute.
+        if (mute) {
+            return true;
+        }
+
+        // Update audio status if current volume position is edge of volume bar,
+        // i.e max or min volume.
+        AudioManager audioManager = tv().getService().getAudioManager();
+        int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+        if (mIsVolumeUp) {
+            int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+            return currentVolume == maxVolume;
+        } else {
+            return currentVolume == 0;
+        }
+    }
+
+    private boolean handleFeatureAbort(HdmiCecMessage cmd) {
+        int originalOpcode = cmd.getParams()[0] & 0xFF;
+        // Since it sends <User Control Released> only when it finishes this action,
+        // it takes care of <User Control Pressed> only here.
+        if (originalOpcode == MESSAGE_USER_CONTROL_PRESSED) {
+            finish();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void clear() {
+        super.clear();
+        if (mSentKeyPressed) {
+            sendVolumeKeyReleased();
+        }
+        if (mLastAvrVolume != UNKNOWN_AVR_VOLUME) {
+            tv().setAudioStatus(false, mLastAvrVolume);
+            mLastAvrVolume = UNKNOWN_AVR_VOLUME;
+        }
     }
 
     @Override
     void handleTimerEvent(int state) {
-        if (mState != STATE_WAIT_FOR_REPORT_VOLUME_STATUS) {
+        if (state != STATE_WAIT_FOR_NEXT_VOLUME_PRESS) {
             return;
         }
 
-        // If no report volume action after IRT * VOLUME_CHANGE_TIMEOUT_MAX_COUNT just stop volume
-        // action.
-        if (++mTimeoutCount == VOLUME_CHANGE_TIMEOUT_MAX_COUNT) {
-            finishWithVolumeChangeRelease();
-            return;
+        if (System.currentTimeMillis() - mLastKeyUpdateTime >= IRT_MS) {
+            finish();
+        } else {
+            sendVolumeKeyPressed();
+            resetTimer();
         }
-
-        sendVolumeChange(mIsVolumeUp);
-        addTimer(mState, IRT_MS);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 846efc0..f0f249e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -312,6 +312,12 @@
 
     final PackageHandler mHandler;
 
+    /**
+     * Messages for {@link #mHandler} that need to wait for system ready before
+     * being dispatched.
+     */
+    private ArrayList<Message> mPostSystemReadyMessages;
+
     final int mSdkVersion = Build.VERSION.SDK_INT;
 
     final Context mContext;
@@ -446,9 +452,9 @@
     /** Token for keys in mPendingVerification. */
     private int mPendingVerificationToken = 0;
 
-    boolean mSystemReady;
-    boolean mSafeMode;
-    boolean mHasSystemUidErrors;
+    volatile boolean mSystemReady;
+    volatile boolean mSafeMode;
+    volatile boolean mHasSystemUidErrors;
 
     ApplicationInfo mAndroidApplication;
     final ActivityInfo mResolveActivity = new ActivityInfo();
@@ -7685,16 +7691,18 @@
     }
 
     void schedulePackageCleaning(String packageName, int userId, boolean andCode) {
-        if (false) {
-            RuntimeException here = new RuntimeException("here");
-            here.fillInStackTrace();
-            Slog.d(TAG, "Schedule cleaning " + packageName + " user=" + userId
-                    + " andCode=" + andCode, here);
+        final Message msg = mHandler.obtainMessage(START_CLEANING_PACKAGE,
+                userId, andCode ? 1 : 0, packageName);
+        if (mSystemReady) {
+            msg.sendToTarget();
+        } else {
+            if (mPostSystemReadyMessages == null) {
+                mPostSystemReadyMessages = new ArrayList<>();
+            }
+            mPostSystemReadyMessages.add(msg);
         }
-        mHandler.sendMessage(mHandler.obtainMessage(START_CLEANING_PACKAGE,
-                userId, andCode ? 1 : 0, packageName));
     }
-    
+
     void startCleaningPackages() {
         // reader
         synchronized (mPackages) {
@@ -12038,6 +12046,14 @@
             }
         }
         sUserManager.systemReady();
+
+        // Kick off any messages waiting for system ready
+        if (mPostSystemReadyMessages != null) {
+            for (Message msg : mPostSystemReadyMessages) {
+                msg.sendToTarget();
+            }
+            mPostSystemReadyMessages = null;
+        }
     }
 
     @Override
@@ -13035,6 +13051,9 @@
         Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
         while (psit.hasNext()) {
             PackageSetting ps = psit.next();
+            if (ps.pkg == null) {
+                continue;
+            }
             final String packageName = ps.pkg.packageName;
             // Skip over if system app
             if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 64242ba..6785cb8 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -627,26 +627,40 @@
             return userState;
         }
 
-        private void handleUserStarted(int userId) {
-            UserState userState;
-            synchronized (mLock) {
-                userState = getOrCreateUserStateLocked(userId);
-                userState.updateIfNeededLocked();
-            }
-            // This is the first time we switch to this user after boot, so
-            // now is the time to remove obsolete print jobs since they
-            // are from the last boot and no application would query them.
-            userState.removeObsoletePrintJobs();
+        private void handleUserStarted(final int userId) {
+            // This code will touch the remote print spooler which
+            // must be called off the main thread, so post the work.
+            BackgroundThread.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    UserState userState;
+                    synchronized (mLock) {
+                        userState = getOrCreateUserStateLocked(userId);
+                        userState.updateIfNeededLocked();
+                    }
+                    // This is the first time we switch to this user after boot, so
+                    // now is the time to remove obsolete print jobs since they
+                    // are from the last boot and no application would query them.
+                    userState.removeObsoletePrintJobs();
+                }
+            });
         }
 
-        private void handleUserStopped(int userId) {
-            synchronized (mLock) {
-                UserState userState = mUserStates.get(userId);
-                if (userState != null) {
-                    userState.destroyLocked();
-                    mUserStates.remove(userId);
+        private void handleUserStopped(final int userId) {
+            // This code will touch the remote print spooler which
+            // must be called off the main thread, so post the work.
+            BackgroundThread.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (mLock) {
+                        UserState userState = mUserStates.get(userId);
+                        if (userState != null) {
+                            userState.destroyLocked();
+                            mUserStates.remove(userId);
+                        }
+                    }
                 }
-            }
+            });
         }
 
         private int resolveCallingProfileParentLocked(int userId) {
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 071b719..5486be1 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -287,16 +287,38 @@
     }
 
     /**
+     * Return the {@link PhoneAccount} which is the user-chosen default for making outgoing phone
+     * calls. This {@code PhoneAccount} will always be a member of the list which is returned from
+     * calling {@link #getEnabledPhoneAccounts()}
+     *
+     * Apps must be prepared for this method to return {@code null}, indicating that there currently
+     * exists no user-chosen default {@code PhoneAccount}.
+     *
+     * @return The user outgoing phone account selected by the user.
+     * @hide
+     */
+    public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().getUserSelectedOutgoingPhoneAccount();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#getUserSelectedOutgoingPhoneAccount", e);
+        }
+        return null;
+    }
+
+    /**
      * Sets the default account for making outgoing phone calls.
      * @hide
      */
-    public void setDefaultOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
+    public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
-                getTelecommService().setDefaultOutgoingPhoneAccount(accountHandle);
+                getTelecommService().setUserSelectedOutgoingPhoneAccount(accountHandle);
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelecommService#setDefaultOutgoingPhoneAccount");
+            Log.e(TAG, "Error calling ITelecommService#setUserSelectedOutgoingPhoneAccount");
         }
     }
 
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index a6ab3ac..6ab78c4 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -40,9 +40,14 @@
     PhoneAccountHandle getDefaultOutgoingPhoneAccount(in String uriScheme);
 
     /**
-     * @see TelecommServiceImpl#setDefaultOutgoingPhoneAccount
+     * @see TelecommServiceImpl#getUserSelectedOutgoingPhoneAccount
      */
-    void setDefaultOutgoingPhoneAccount(in PhoneAccountHandle account);
+    PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
+
+    /**
+     * @see TelecommServiceImpl#setUserSelectedOutgoingPhoneAccount
+     */
+    void setUserSelectedOutgoingPhoneAccount(in PhoneAccountHandle account);
 
     /**
      * @see TelecommServiceImpl#getOutgoingPhoneAccounts
diff --git a/telephony/java/com/android/internal/telephony/IMms.aidl b/telephony/java/com/android/internal/telephony/IMms.aidl
index 63b7a53..4337012 100644
--- a/telephony/java/com/android/internal/telephony/IMms.aidl
+++ b/telephony/java/com/android/internal/telephony/IMms.aidl
@@ -40,7 +40,7 @@
      *  broadcast when the message is successfully sent, or failed
      */
     void sendMessage(long subId, String callingPkg, in Uri contentUri,
-            String locationUrl, in ContentValues configOverrides, in PendingIntent sentIntent);
+            String locationUrl, in Bundle configOverrides, in PendingIntent sentIntent);
 
     /**
      * Download an MMS message using known location and transaction id
@@ -57,7 +57,7 @@
      *  broadcast when the message is downloaded, or the download is failed
      */
     void downloadMessage(long subId, String callingPkg, String locationUrl,
-            in Uri contentUri, in ContentValues configOverrides,
+            in Uri contentUri, in Bundle configOverrides,
             in PendingIntent downloadedIntent);
 
     /**
@@ -192,7 +192,7 @@
      *  broadcast when the message is successfully sent, or failed
      */
     void sendStoredMessage(long subId, String callingPkg, in Uri messageUri,
-            in ContentValues configOverrides, in PendingIntent sentIntent);
+            in Bundle configOverrides, in PendingIntent sentIntent);
 
     /**
      * Turns on/off the flag to automatically write sent/received SMS/MMS messages into system
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 6bb28e1..393d2ec 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -20,15 +20,20 @@
 
 Usage: apilint.py current.txt
 Usage: apilint.py current.txt previous.txt
+
+You can also splice in blame details like this:
+$ git blame api/current.txt -t -e > /tmp/currentblame.txt
+$ apilint.py /tmp/currentblame.txt previous.txt --no-color
 """
 
-import re, sys, collections
+import re, sys, collections, traceback
 
 
 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
 
 def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
     # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
+    if "--no-color" in sys.argv: return ""
     codes = []
     if reset: codes.append("0")
     else:
@@ -43,9 +48,10 @@
 
 
 class Field():
-    def __init__(self, clazz, raw):
+    def __init__(self, clazz, raw, blame):
         self.clazz = clazz
         self.raw = raw.strip(" {;")
+        self.blame = blame
 
         raw = raw.split()
         self.split = list(raw)
@@ -60,14 +66,20 @@
         else:
             self.value = None
 
+        self.ident = self.raw.replace(" deprecated ", " ")
+
     def __repr__(self):
         return self.raw
 
 
 class Method():
-    def __init__(self, clazz, raw):
+    def __init__(self, clazz, raw, blame):
         self.clazz = clazz
         self.raw = raw.strip(" {;")
+        self.blame = blame
+
+        # drop generics for now
+        raw = re.sub("<.+?>", "", raw)
 
         raw = re.split("[\s(),;]+", raw)
         for r in ["", ";"]:
@@ -84,14 +96,24 @@
             if r == "throws": break
             self.args.append(r)
 
+        # identity for compat purposes
+        ident = self.raw
+        ident = ident.replace(" deprecated ", " ")
+        ident = ident.replace(" synchronized ", " ")
+        ident = re.sub("<.+?>", "", ident)
+        if " throws " in ident:
+            ident = ident[:ident.index(" throws ")]
+        self.ident = ident
+
     def __repr__(self):
         return self.raw
 
 
 class Class():
-    def __init__(self, pkg, raw):
+    def __init__(self, pkg, raw, blame):
         self.pkg = pkg
         self.raw = raw.strip(" {;")
+        self.blame = blame
         self.ctors = []
         self.fields = []
         self.methods = []
@@ -102,19 +124,25 @@
             self.fullname = raw[raw.index("class")+1]
         elif "interface" in raw:
             self.fullname = raw[raw.index("interface")+1]
-
-        if "." in self.fullname:
-            self.name = self.fullname[self.fullname.rindex(".")+1:]
         else:
-            self.name = self.fullname
+            raise ValueError("Funky class type %s" % (self.raw))
+
+        if "extends" in raw:
+            self.extends = raw[raw.index("extends")+1]
+        else:
+            self.extends = None
+
+        self.fullname = self.pkg.name + "." + self.fullname
+        self.name = self.fullname[self.fullname.rindex(".")+1:]
 
     def __repr__(self):
         return self.raw
 
 
 class Package():
-    def __init__(self, raw):
+    def __init__(self, raw, blame):
         self.raw = raw.strip(" {;")
+        self.blame = blame
 
         raw = raw.split()
         self.name = raw[raw.index("package")+1]
@@ -124,55 +152,68 @@
 
 
 def parse_api(fn):
-    api = []
+    api = {}
     pkg = None
     clazz = None
+    blame = None
+
+    re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
 
     with open(fn) as f:
         for raw in f.readlines():
             raw = raw.rstrip()
+            match = re_blame.match(raw)
+            if match is not None:
+                blame = match.groups()[0:2]
+                raw = match.groups()[2]
+            else:
+                blame = None
 
             if raw.startswith("package"):
-                pkg = Package(raw)
+                pkg = Package(raw, blame)
             elif raw.startswith("  ") and raw.endswith("{"):
-                clazz = Class(pkg, raw)
-                api.append(clazz)
+                clazz = Class(pkg, raw, blame)
+                api[clazz.fullname] = clazz
             elif raw.startswith("    ctor"):
-                clazz.ctors.append(Method(clazz, raw))
+                clazz.ctors.append(Method(clazz, raw, blame))
             elif raw.startswith("    method"):
-                clazz.methods.append(Method(clazz, raw))
+                clazz.methods.append(Method(clazz, raw, blame))
             elif raw.startswith("    field"):
-                clazz.fields.append(Field(clazz, raw))
+                clazz.fields.append(Field(clazz, raw, blame))
 
     return api
 
 
-failures = []
-
-def filter_dupe(s):
-    return s.replace(" deprecated ", " ")
+failures = {}
 
 def _fail(clazz, detail, msg):
     """Records an API failure to be processed later."""
     global failures
 
+    sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
+    sig = sig.replace(" deprecated ", " ")
+
     res = msg
+    blame = clazz.blame
     if detail is not None:
         res += "\n    in " + repr(detail)
+        blame = detail.blame
     res += "\n    in " + repr(clazz)
     res += "\n    in " + repr(clazz.pkg)
-    failures.append(filter_dupe(res))
+    if blame is not None:
+        res += "\n    last modified by %s in %s" % (blame[1], blame[0])
+    failures[sig] = res
 
 def warn(clazz, detail, msg):
-    _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK), format(reset=True), msg))
+    _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), format(reset=True), msg))
 
 def error(clazz, detail, msg):
-    _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK), format(reset=True), msg))
+    _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK, bold=True), format(reset=True), msg))
 
 
 def verify_constants(clazz):
     """All static final constants must be FOO_NAME style."""
-    if re.match("R\.[a-z]+", clazz.fullname): return
+    if re.match("android\.R\.[a-z]+", clazz.fullname): return
 
     for f in clazz.fields:
         if "static" in f.split and "final" in f.split:
@@ -188,6 +229,10 @@
 
 def verify_class_names(clazz):
     """Try catching malformed class names like myMtp or MTPUser."""
+    if clazz.fullname.startswith("android.opengl"): return
+    if clazz.fullname.startswith("android.renderscript"): return
+    if re.match("android\.R\.[a-z]+", clazz.fullname): return
+
     if re.search("[A-Z]{2,}", clazz.name) is not None:
         warn(clazz, None, "Class name style should be Mtp not MTP")
     if re.match("[^A-Z]", clazz.name):
@@ -196,32 +241,35 @@
 
 def verify_method_names(clazz):
     """Try catching malformed method names, like Foo() or getMTU()."""
-    if clazz.pkg.name == "android.opengl": return
+    if clazz.fullname.startswith("android.opengl"): return
+    if clazz.fullname.startswith("android.renderscript"): return
+    if clazz.fullname == "android.system.OsConstants": return
 
     for m in clazz.methods:
         if re.search("[A-Z]{2,}", m.name) is not None:
             warn(clazz, m, "Method name style should be getMtu() instead of getMTU()")
         if re.match("[^a-z]", m.name):
-            error(clazz, None, "Method name must start with lowercase char")
+            error(clazz, m, "Method name must start with lowercase char")
 
 
 def verify_callbacks(clazz):
     """Verify Callback classes.
     All callback classes must be abstract.
     All methods must follow onFoo() naming style."""
+    if clazz.fullname == "android.speech.tts.SynthesisCallback": return
 
     if clazz.name.endswith("Callbacks"):
-        error(clazz, None, "Class must be named exactly Callback")
+        error(clazz, None, "Class name must not be plural")
     if clazz.name.endswith("Observer"):
-        warn(clazz, None, "Class should be named Callback")
+        warn(clazz, None, "Class should be named FooCallback")
 
     if clazz.name.endswith("Callback"):
         if "interface" in clazz.split:
-            error(clazz, None, "Callback must be abstract class")
+            error(clazz, None, "Callback must be abstract class to enable extension in future API levels")
 
         for m in clazz.methods:
             if not re.match("on[A-Z][a-z]*", m.name):
-                error(clazz, m, "Callback method names must be onFoo style")
+                error(clazz, m, "Callback method names must be onFoo() style")
 
 
 def verify_listeners(clazz):
@@ -233,16 +281,16 @@
 
     if clazz.name.endswith("Listener"):
         if " abstract class " in clazz.raw:
-            error(clazz, None, "Listener should be interface")
+            error(clazz, None, "Listener should be an interface, otherwise renamed Callback")
 
         for m in clazz.methods:
             if not re.match("on[A-Z][a-z]*", m.name):
-                error(clazz, m, "Listener method names must be onFoo style")
+                error(clazz, m, "Listener method names must be onFoo() style")
 
         if len(clazz.methods) == 1 and clazz.name.startswith("On"):
             m = clazz.methods[0]
             if (m.name + "Listener").lower() != clazz.name.lower():
-                error(clazz, m, "Single method name should match class name")
+                error(clazz, m, "Single listener method name should match class name")
 
 
 def verify_actions(clazz):
@@ -255,21 +303,24 @@
     for f in clazz.fields:
         if f.value is None: continue
         if f.name.startswith("EXTRA_"): continue
+        if f.name == "SERVICE_INTERFACE" or f.name == "PROVIDER_INTERFACE": continue
 
         if "static" in f.split and "final" in f.split and f.typ == "java.lang.String":
             if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower():
                 if not f.name.startswith("ACTION_"):
-                    error(clazz, f, "Intent action must be ACTION_FOO")
+                    error(clazz, f, "Intent action constant name must be ACTION_FOO")
                 else:
-                    if clazz.name == "Intent":
+                    if clazz.fullname == "android.content.Intent":
                         prefix = "android.intent.action"
-                    elif clazz.name == "Settings":
+                    elif clazz.fullname == "android.provider.Settings":
                         prefix = "android.settings"
+                    elif clazz.fullname == "android.app.admin.DevicePolicyManager" or clazz.fullname == "android.app.admin.DeviceAdminReceiver":
+                        prefix = "android.app.action"
                     else:
                         prefix = clazz.pkg.name + ".action"
                     expected = prefix + "." + f.name[7:]
                     if f.value != expected:
-                        error(clazz, f, "Inconsistent action value")
+                        error(clazz, f, "Inconsistent action value; expected %s" % (expected))
 
 
 def verify_extras(clazz):
@@ -279,6 +330,9 @@
         package android.foo {
             String EXTRA_BAR = "android.foo.extra.BAR";
         }"""
+    if clazz.fullname == "android.app.Notification": return
+    if clazz.fullname == "android.appwidget.AppWidgetManager": return
+
     for f in clazz.fields:
         if f.value is None: continue
         if f.name.startswith("ACTION_"): continue
@@ -288,13 +342,15 @@
                 if not f.name.startswith("EXTRA_"):
                     error(clazz, f, "Intent extra must be EXTRA_FOO")
                 else:
-                    if clazz.name == "Intent":
+                    if clazz.pkg.name == "android.content" and clazz.name == "Intent":
                         prefix = "android.intent.extra"
+                    elif clazz.pkg.name == "android.app.admin":
+                        prefix = "android.app.extra"
                     else:
                         prefix = clazz.pkg.name + ".extra"
                     expected = prefix + "." + f.name[6:]
                     if f.value != expected:
-                        error(clazz, f, "Inconsistent extra value")
+                        error(clazz, f, "Inconsistent extra value; expected %s" % (expected))
 
 
 def verify_equals(clazz):
@@ -303,7 +359,7 @@
     eq = "equals" in methods
     hc = "hashCode" in methods
     if eq != hc:
-        error(clazz, None, "Must override both equals and hashCode")
+        error(clazz, None, "Must override both equals and hashCode; missing one")
 
 
 def verify_parcelable(clazz):
@@ -314,34 +370,59 @@
         describe = [ i for i in clazz.methods if i.name == "describeContents" ]
 
         if len(creator) == 0 or len(write) == 0 or len(describe) == 0:
-            error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents")
+            error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one")
 
 
 def verify_protected(clazz):
     """Verify that no protected methods are allowed."""
     for m in clazz.methods:
         if "protected" in m.split:
-            error(clazz, m, "Protected method")
+            error(clazz, m, "No protected methods; must be public")
     for f in clazz.fields:
         if "protected" in f.split:
-            error(clazz, f, "Protected field")
+            error(clazz, f, "No protected fields; must be public")
 
 
 def verify_fields(clazz):
     """Verify that all exposed fields are final.
     Exposed fields must follow myName style.
     Catch internal mFoo objects being exposed."""
+
+    IGNORE_BARE_FIELDS = [
+        "android.app.ActivityManager.RecentTaskInfo",
+        "android.app.Notification",
+        "android.content.pm.ActivityInfo",
+        "android.content.pm.ApplicationInfo",
+        "android.content.pm.FeatureGroupInfo",
+        "android.content.pm.InstrumentationInfo",
+        "android.content.pm.PackageInfo",
+        "android.content.pm.PackageItemInfo",
+        "android.os.Message",
+        "android.system.StructPollfd",
+    ]
+
     for f in clazz.fields:
         if not "final" in f.split:
-            error(clazz, f, "Bare fields must be final; consider adding accessors")
+            if clazz.fullname in IGNORE_BARE_FIELDS:
+                pass
+            elif clazz.fullname.endswith("LayoutParams"):
+                pass
+            elif clazz.fullname.startswith("android.util.Mutable"):
+                pass
+            else:
+                error(clazz, f, "Bare fields must be marked final; consider adding accessors")
 
         if not "static" in f.split:
             if not re.match("[a-z]([a-zA-Z]+)?", f.name):
-                error(clazz, f, "Non-static fields must be myName")
+                error(clazz, f, "Non-static fields must be named with myField style")
 
-        if re.match("[m][A-Z]", f.name):
+        if re.match("[ms][A-Z]", f.name):
             error(clazz, f, "Don't expose your internal objects")
 
+        if re.match("[A-Z_]+", f.name):
+            if "static" not in f.split or "final" not in f.split:
+                error(clazz, f, "Constants must be marked static final")
+
 
 def verify_register(clazz):
     """Verify parity of registration methods.
@@ -353,34 +434,34 @@
             if m.name.startswith("register"):
                 other = "unregister" + m.name[8:]
                 if other not in methods:
-                    error(clazz, m, "Missing unregister")
+                    error(clazz, m, "Missing unregister method")
             if m.name.startswith("unregister"):
                 other = "register" + m.name[10:]
                 if other not in methods:
-                    error(clazz, m, "Missing register")
+                    error(clazz, m, "Missing register method")
 
             if m.name.startswith("add") or m.name.startswith("remove"):
-                error(clazz, m, "Callback should be register/unregister")
+                error(clazz, m, "Callback methods should be named register/unregister")
 
         if "Listener" in m.raw:
             if m.name.startswith("add"):
                 other = "remove" + m.name[3:]
                 if other not in methods:
-                    error(clazz, m, "Missing remove")
+                    error(clazz, m, "Missing remove method")
             if m.name.startswith("remove") and not m.name.startswith("removeAll"):
                 other = "add" + m.name[6:]
                 if other not in methods:
-                    error(clazz, m, "Missing add")
+                    error(clazz, m, "Missing add method")
 
             if m.name.startswith("register") or m.name.startswith("unregister"):
-                error(clazz, m, "Listener should be add/remove")
+                error(clazz, m, "Listener methods should be named add/remove")
 
 
 def verify_sync(clazz):
     """Verify synchronized methods aren't exposed."""
     for m in clazz.methods:
         if "synchronized" in m.split:
-            error(clazz, m, "Lock exposed")
+            error(clazz, m, "Internal lock exposed")
 
 
 def verify_intent_builder(clazz):
@@ -392,7 +473,7 @@
             if m.name.startswith("create") and m.name.endswith("Intent"):
                 pass
             else:
-                warn(clazz, m, "Should be createFooIntent()")
+                error(clazz, m, "Methods creating an Intent should be named createFooIntent()")
 
 
 def verify_helper_classes(clazz):
@@ -402,25 +483,57 @@
     if "extends android.app.Service" in clazz.raw:
         test_methods = True
         if not clazz.name.endswith("Service"):
-            error(clazz, None, "Inconsistent class name")
+            error(clazz, None, "Inconsistent class name; should be FooService")
+
+        found = False
+        for f in clazz.fields:
+            if f.name == "SERVICE_INTERFACE":
+                found = True
+                if f.value != clazz.fullname:
+                    error(clazz, f, "Inconsistent interface constant; expected %s" % (clazz.fullname))
+
+        if not found:
+            warn(clazz, None, "Missing SERVICE_INTERFACE constant")
+
+        if "abstract" in clazz.split and not clazz.fullname.startswith("android.service."):
+            warn(clazz, None, "Services extended by developers should be under android.service")
+
     if "extends android.content.ContentProvider" in clazz.raw:
         test_methods = True
         if not clazz.name.endswith("Provider"):
-            error(clazz, None, "Inconsistent class name")
+            error(clazz, None, "Inconsistent class name; should be FooProvider")
+
+        found = False
+        for f in clazz.fields:
+            if f.name == "PROVIDER_INTERFACE":
+                found = True
+                if f.value != clazz.fullname:
+                    error(clazz, f, "Inconsistent interface name; expected %s" % (clazz.fullname))
+
+        if not found:
+            warn(clazz, None, "Missing PROVIDER_INTERFACE constant")
+
+        if "abstract" in clazz.split and not clazz.fullname.startswith("android.provider."):
+            warn(clazz, None, "Providers extended by developers should be under android.provider")
+
     if "extends android.content.BroadcastReceiver" in clazz.raw:
         test_methods = True
         if not clazz.name.endswith("Receiver"):
-            error(clazz, None, "Inconsistent class name")
+            error(clazz, None, "Inconsistent class name; should be FooReceiver")
+
     if "extends android.app.Activity" in clazz.raw:
         test_methods = True
         if not clazz.name.endswith("Activity"):
-            error(clazz, None, "Inconsistent class name")
+            error(clazz, None, "Inconsistent class name; should be FooActivity")
 
     if test_methods:
         for m in clazz.methods:
             if "final" in m.split: continue
             if not re.match("on[A-Z]", m.name):
-                error(clazz, m, "Extendable methods should be onFoo() style, otherwise final")
+                if "abstract" in m.split:
+                    error(clazz, m, "Methods implemented by developers must be named onFoo()")
+                else:
+                    warn(clazz, m, "If implemented by developer, should be named onFoo(); otherwise consider marking final")
 
 
 def verify_builder(clazz):
@@ -430,7 +543,7 @@
     if not clazz.name.endswith("Builder"): return
 
     if clazz.name != "Builder":
-        warn(clazz, None, "Should be standalone Builder class")
+        warn(clazz, None, "Builder should be defined as inner class")
 
     has_build = False
     for m in clazz.methods:
@@ -442,11 +555,11 @@
         if m.name.startswith("clear"): continue
 
         if m.name.startswith("with"):
-            error(clazz, m, "Builder methods must be setFoo()")
+            error(clazz, m, "Builder methods names must follow setFoo() style")
 
         if m.name.startswith("set"):
             if not m.typ.endswith(clazz.fullname):
-                warn(clazz, m, "Should return the builder")
+                warn(clazz, m, "Methods should return the builder")
 
     if not has_build:
         warn(clazz, None, "Missing build() method")
@@ -474,7 +587,7 @@
         "android.view",
         "android.animation",
         "android.provider",
-        "android.content",
+        ["android.content","android.graphics.drawable"],
         "android.database",
         "android.graphics",
         "android.text",
@@ -508,29 +621,40 @@
                 warn(clazz, m, "Method argument type violates package layering")
 
 
-def verify_boolean(clazz):
+def verify_boolean(clazz, api):
     """Catches people returning boolean from getFoo() style methods.
     Ignores when matching setFoo() is present."""
+
     methods = [ m.name for m in clazz.methods ]
+
+    builder = clazz.fullname + ".Builder"
+    builder_methods = []
+    if builder in api:
+        builder_methods = [ m.name for m in api[builder].methods ]
+
     for m in clazz.methods:
         if m.typ == "boolean" and m.name.startswith("get") and m.name != "get" and len(m.args) == 0:
             setter = "set" + m.name[3:]
-            if setter not in methods:
-                error(clazz, m, "Methods returning boolean should be isFoo or hasFoo")
+            if setter in methods:
+                pass
+            elif builder is not None and setter in builder_methods:
+                pass
+            else:
+                warn(clazz, m, "Methods returning boolean should be named isFoo, hasFoo, areFoo")
 
 
 def verify_collections(clazz):
     """Verifies that collection types are interfaces."""
+    if clazz.fullname == "android.os.Bundle": return
+
     bad = ["java.util.Vector", "java.util.LinkedList", "java.util.ArrayList", "java.util.Stack",
            "java.util.HashMap", "java.util.HashSet", "android.util.ArraySet", "android.util.ArrayMap"]
     for m in clazz.methods:
-        filt = re.sub("<.+>", "", m.typ)
-        if filt in bad:
-            error(clazz, m, "Return type is concrete collection")
+        if m.typ in bad:
+            error(clazz, m, "Return type is concrete collection; should be interface")
         for arg in m.args:
-            filt = re.sub("<.+>", "", arg)
-            if filt in bad:
-                error(clazz, m, "Argument is concrete collection")
+            if arg in bad:
+                error(clazz, m, "Argument is concrete collection; should be interface")
 
 
 def verify_flags(clazz):
@@ -545,15 +669,18 @@
 
             scope = f.name[0:f.name.index("FLAG_")]
             if val & known[scope]:
-                warn(clazz, f, "Found overlapping flag")
+                warn(clazz, f, "Found overlapping flag constant value")
             known[scope] |= val
 
 
-def verify_all(api):
+def verify_style(api):
+    """Find all style issues in the given API level."""
     global failures
 
-    failures = []
-    for clazz in api:
+    failures = {}
+    for key in sorted(api.keys()):
+        clazz = api[key]
+
         if clazz.pkg.name.startswith("java"): continue
         if clazz.pkg.name.startswith("junit"): continue
         if clazz.pkg.name.startswith("org.apache"): continue
@@ -581,26 +708,90 @@
         verify_aidl(clazz)
         verify_internal(clazz)
         verify_layering(clazz)
-        verify_boolean(clazz)
+        verify_boolean(clazz, api)
         verify_collections(clazz)
         verify_flags(clazz)
 
     return failures
 
 
+def verify_compat(cur, prev):
+    """Find any incompatible API changes between two levels."""
+    global failures
+
+    def class_exists(api, test):
+        return test.fullname in api
+
+    def ctor_exists(api, clazz, test):
+        for m in clazz.ctors:
+            if m.ident == test.ident: return True
+        return False
+
+    def all_methods(api, clazz):
+        methods = list(clazz.methods)
+        if clazz.extends is not None:
+            methods.extend(all_methods(api, api[clazz.extends]))
+        return methods
+
+    def method_exists(api, clazz, test):
+        methods = all_methods(api, clazz)
+        for m in methods:
+            if m.ident == test.ident: return True
+        return False
+
+    def field_exists(api, clazz, test):
+        for f in clazz.fields:
+            if f.ident == test.ident: return True
+        return False
+
+    failures = {}
+    for key in sorted(prev.keys()):
+        prev_clazz = prev[key]
+
+        if not class_exists(cur, prev_clazz):
+            error(prev_clazz, None, "Class removed or incompatible change")
+            continue
+
+        cur_clazz = cur[key]
+
+        for test in prev_clazz.ctors:
+            if not ctor_exists(cur, cur_clazz, test):
+                error(prev_clazz, prev_ctor, "Constructor removed or incompatible change")
+
+        methods = all_methods(prev, prev_clazz)
+        for test in methods:
+            if not method_exists(cur, cur_clazz, test):
+                error(prev_clazz, test, "Method removed or incompatible change")
+
+        for test in prev_clazz.fields:
+            if not field_exists(cur, cur_clazz, test):
+                error(prev_clazz, test, "Field removed or incompatible change")
+
+    return failures
+
+
 cur = parse_api(sys.argv[1])
-cur_fail = verify_all(cur)
+cur_fail = verify_style(cur)
 
 if len(sys.argv) > 2:
     prev = parse_api(sys.argv[2])
-    prev_fail = verify_all(prev)
+    prev_fail = verify_style(prev)
 
     # ignore errors from previous API level
     for p in prev_fail:
         if p in cur_fail:
-            cur_fail.remove(p)
+            del cur_fail[p]
+
+    # look for compatibility issues
+    compat_fail = verify_compat(cur, prev)
+
+    print "%s API compatibility issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+    for f in sorted(compat_fail):
+        print compat_fail[f]
+        print
 
 
-for f in cur_fail:
-    print f
+print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+for f in sorted(cur_fail):
+    print cur_fail[f]
     print