Merge "Update tilebenchmark to work with current drawing infrastructure"
diff --git a/api/15.txt b/api/15.txt
index bca8e01..ddf5baf 100644
--- a/api/15.txt
+++ b/api/15.txt
@@ -83,6 +83,7 @@
     field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
     field public static final java.lang.String READ_PROFILE = "android.permission.READ_PROFILE";
     field public static final java.lang.String READ_SMS = "android.permission.READ_SMS";
+    field public static final java.lang.String READ_SOCIAL_STREAM = "android.permission.READ_SOCIAL_STREAM";
     field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
     field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
     field public static final java.lang.String REBOOT = "android.permission.REBOOT";
@@ -127,6 +128,7 @@
     field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
     field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
     field public static final java.lang.String WRITE_SMS = "android.permission.WRITE_SMS";
+    field public static final java.lang.String WRITE_SOCIAL_STREAM = "android.permission.WRITE_SOCIAL_STREAM";
     field public static final java.lang.String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS";
   }
 
@@ -3235,6 +3237,7 @@
     method public final android.app.Fragment getTargetFragment();
     method public final int getTargetRequestCode();
     method public final java.lang.CharSequence getText(int);
+    method public boolean getUserVisibleHint();
     method public android.view.View getView();
     method public final int hashCode();
     method public static android.app.Fragment instantiate(android.content.Context, java.lang.String);
@@ -3281,6 +3284,7 @@
     method public void setMenuVisibility(boolean);
     method public void setRetainInstance(boolean);
     method public void setTargetFragment(android.app.Fragment, int);
+    method public void setUserVisibleHint(boolean);
     method public void startActivity(android.content.Intent);
     method public void startActivityForResult(android.content.Intent, int);
     method public void unregisterForContextMenu(android.view.View);
@@ -4143,6 +4147,7 @@
     ctor public AppWidgetHostView(android.content.Context, int, int);
     method public int getAppWidgetId();
     method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo();
+    method public static android.graphics.Rect getDefaultPaddingForWidget(android.content.Context, android.content.ComponentName, android.graphics.Rect);
     method protected android.view.View getDefaultView();
     method protected android.view.View getErrorView();
     method protected void prepareView(android.view.View);
@@ -4472,10 +4477,12 @@
     method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
     method public int describeContents();
+    method public boolean fetchUuidsWithSdp();
     method public java.lang.String getAddress();
     method public android.bluetooth.BluetoothClass getBluetoothClass();
     method public int getBondState();
     method public java.lang.String getName();
+    method public android.os.ParcelUuid[] getUuids();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
     field public static final java.lang.String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED";
@@ -4484,6 +4491,7 @@
     field public static final java.lang.String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED";
     field public static final java.lang.String ACTION_FOUND = "android.bluetooth.device.action.FOUND";
     field public static final java.lang.String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED";
+    field public static final java.lang.String ACTION_UUID = "android.bluetooth.device.action.UUID";
     field public static final int BOND_BONDED = 12; // 0xc
     field public static final int BOND_BONDING = 11; // 0xb
     field public static final int BOND_NONE = 10; // 0xa
@@ -4495,6 +4503,7 @@
     field public static final java.lang.String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
     field public static final java.lang.String EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
     field public static final java.lang.String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
+    field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
   }
 
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -5325,6 +5334,7 @@
     method public java.util.ArrayList<T> getParcelableArrayListExtra(java.lang.String);
     method public T getParcelableExtra(java.lang.String);
     method public java.lang.String getScheme();
+    method public android.content.Intent getSelector();
     method public java.io.Serializable getSerializableExtra(java.lang.String);
     method public short[] getShortArrayExtra(java.lang.String);
     method public short getShortExtra(java.lang.String, short);
@@ -5337,6 +5347,7 @@
     method public boolean hasExtra(java.lang.String);
     method public boolean hasFileDescriptors();
     method public static android.content.Intent makeMainActivity(android.content.ComponentName);
+    method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String);
     method public static android.content.Intent makeRestartActivityTask(android.content.ComponentName);
     method public static android.content.Intent parseIntent(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static android.content.Intent parseUri(java.lang.String, int) throws java.net.URISyntaxException;
@@ -5390,6 +5401,7 @@
     method public void setExtrasClassLoader(java.lang.ClassLoader);
     method public android.content.Intent setFlags(int);
     method public android.content.Intent setPackage(java.lang.String);
+    method public void setSelector(android.content.Intent);
     method public void setSourceBounds(android.graphics.Rect);
     method public android.content.Intent setType(java.lang.String);
     method public deprecated java.lang.String toURI();
@@ -5493,7 +5505,16 @@
     field public static final java.lang.String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
     field public static final java.lang.String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
     field public static final java.lang.String CATEGORY_ALTERNATIVE = "android.intent.category.ALTERNATIVE";
+    field public static final java.lang.String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER";
+    field public static final java.lang.String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR";
+    field public static final java.lang.String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR";
+    field public static final java.lang.String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS";
+    field public static final java.lang.String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL";
+    field public static final java.lang.String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY";
+    field public static final java.lang.String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS";
     field public static final java.lang.String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET";
+    field public static final java.lang.String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING";
+    field public static final java.lang.String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC";
     field public static final java.lang.String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
     field public static final java.lang.String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
     field public static final java.lang.String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE";
@@ -5559,6 +5580,7 @@
     field public static final int FILL_IN_COMPONENT = 8; // 0x8
     field public static final int FILL_IN_DATA = 2; // 0x2
     field public static final int FILL_IN_PACKAGE = 16; // 0x10
+    field public static final int FILL_IN_SELECTOR = 64; // 0x40
     field public static final int FILL_IN_SOURCE_BOUNDS = 32; // 0x20
     field public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304; // 0x400000
     field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
@@ -6782,6 +6804,13 @@
     method public abstract boolean onMove(int, int);
   }
 
+  public class CrossProcessCursorWrapper extends android.database.CursorWrapper implements android.database.CrossProcessCursor {
+    ctor public CrossProcessCursorWrapper(android.database.Cursor);
+    method public void fillWindow(int, android.database.CursorWindow);
+    method public android.database.CursorWindow getWindow();
+    method public boolean onMove(int, int);
+  }
+
   public abstract interface Cursor {
     method public abstract void close();
     method public abstract void copyStringToBuffer(int, android.database.CharArrayBuffer);
@@ -6851,7 +6880,8 @@
   }
 
   public class CursorWindow extends android.database.sqlite.SQLiteClosable implements android.os.Parcelable {
-    ctor public CursorWindow(boolean);
+    ctor public CursorWindow(java.lang.String);
+    ctor public deprecated CursorWindow(boolean);
     method public boolean allocRow();
     method public void clear();
     method public void close();
@@ -8782,6 +8812,7 @@
     method public long getTimestamp();
     method public void getTransformMatrix(float[]);
     method public void release();
+    method public void setDefaultBufferSize(int, int);
     method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener);
     method public void updateTexImage();
   }
@@ -9356,6 +9387,7 @@
     method public java.util.List<android.hardware.Camera.Size> getSupportedVideoSizes();
     method public java.util.List<java.lang.String> getSupportedWhiteBalance();
     method public float getVerticalViewAngle();
+    method public boolean getVideoStabilization();
     method public java.lang.String getWhiteBalance();
     method public int getZoom();
     method public java.util.List<java.lang.Integer> getZoomRatios();
@@ -9363,6 +9395,7 @@
     method public boolean isAutoWhiteBalanceLockSupported();
     method public boolean isSmoothZoomSupported();
     method public boolean isVideoSnapshotSupported();
+    method public boolean isVideoStabilizationSupported();
     method public boolean isZoomSupported();
     method public void remove(java.lang.String);
     method public void removeGpsData();
@@ -9394,6 +9427,7 @@
     method public void setRecordingHint(boolean);
     method public void setRotation(int);
     method public void setSceneMode(java.lang.String);
+    method public void setVideoStabilization(boolean);
     method public void setWhiteBalance(java.lang.String);
     method public void setZoom(int);
     method public void unflatten(java.lang.String);
@@ -10529,6 +10563,7 @@
     field public static final int QUALITY_HIGH = 1; // 0x1
     field public static final int QUALITY_LOW = 0; // 0x0
     field public static final int QUALITY_QCIF = 2; // 0x2
+    field public static final int QUALITY_QVGA = 7; // 0x7
     field public static final int QUALITY_TIME_LAPSE_1080P = 1006; // 0x3ee
     field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec
     field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed
@@ -10536,6 +10571,7 @@
     field public static final int QUALITY_TIME_LAPSE_HIGH = 1001; // 0x3e9
     field public static final int QUALITY_TIME_LAPSE_LOW = 1000; // 0x3e8
     field public static final int QUALITY_TIME_LAPSE_QCIF = 1002; // 0x3ea
+    field public static final int QUALITY_TIME_LAPSE_QVGA = 1007; // 0x3ef
     field public int audioBitRate;
     field public int audioChannels;
     field public int audioCodec;
@@ -10675,6 +10711,7 @@
     field public static final int METADATA_KEY_GENRE = 6; // 0x6
     field public static final int METADATA_KEY_HAS_AUDIO = 16; // 0x10
     field public static final int METADATA_KEY_HAS_VIDEO = 17; // 0x11
+    field public static final int METADATA_KEY_LOCATION = 23; // 0x17
     field public static final int METADATA_KEY_MIMETYPE = 12; // 0xc
     field public static final int METADATA_KEY_NUM_TRACKS = 10; // 0xa
     field public static final int METADATA_KEY_TITLE = 7; // 0x7
@@ -13609,16 +13646,19 @@
     field public static final int GL_RENDERBUFFER_RED_SIZE_OES = 36176; // 0x8d50
     field public static final int GL_RENDERBUFFER_STENCIL_SIZE_OES = 36181; // 0x8d55
     field public static final int GL_RENDERBUFFER_WIDTH_OES = 36162; // 0x8d42
+    field public static final int GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES = 36200; // 0x8d68
     field public static final int GL_RGB565_OES = 36194; // 0x8d62
     field public static final int GL_RGB5_A1_OES = 32855; // 0x8057
     field public static final int GL_RGB8_OES = 32849; // 0x8051
     field public static final int GL_RGBA4_OES = 32854; // 0x8056
     field public static final int GL_RGBA8_OES = 32856; // 0x8058
+    field public static final int GL_SAMPLER_EXTERNAL_OES = 36198; // 0x8d66
     field public static final int GL_STENCIL_ATTACHMENT_OES = 36128; // 0x8d20
     field public static final int GL_STENCIL_INDEX1_OES = 36166; // 0x8d46
     field public static final int GL_STENCIL_INDEX4_OES = 36167; // 0x8d47
     field public static final int GL_STENCIL_INDEX8_OES = 36168; // 0x8d48
     field public static final int GL_TEXTURE_BINDING_CUBE_MAP_OES = 34068; // 0x8514
+    field public static final int GL_TEXTURE_BINDING_EXTERNAL_OES = 36199; // 0x8d67
     field public static final int GL_TEXTURE_CROP_RECT_OES = 35741; // 0x8b9d
     field public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES = 34070; // 0x8516
     field public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES = 34072; // 0x8518
@@ -13627,6 +13667,7 @@
     field public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES = 34069; // 0x8515
     field public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES = 34071; // 0x8517
     field public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES = 34073; // 0x8519
+    field public static final int GL_TEXTURE_EXTERNAL_OES = 36197; // 0x8d65
     field public static final int GL_TEXTURE_GEN_MODE_OES = 9472; // 0x2500
     field public static final int GL_TEXTURE_GEN_STR_OES = 36192; // 0x8d60
     field public static final int GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES = 35215; // 0x898f
@@ -14384,6 +14425,7 @@
     field public static final int HONEYCOMB_MR1 = 12; // 0xc
     field public static final int HONEYCOMB_MR2 = 13; // 0xd
     field public static final int ICE_CREAM_SANDWICH = 14; // 0xe
+    field public static final int ICE_CREAM_SANDWICH_MR1 = 15; // 0xf
   }
 
   public final class Bundle implements java.lang.Cloneable android.os.Parcelable {
@@ -14749,6 +14791,7 @@
     field public static final int FLAG_ONEWAY = 1; // 0x1
     field public static final int INTERFACE_TRANSACTION = 1598968902; // 0x5f4e5446
     field public static final int LAST_CALL_TRANSACTION = 16777215; // 0xffffff
+    field public static final int LIKE_TRANSACTION = 1598835019; // 0x5f4c494b
     field public static final int PING_TRANSACTION = 1599098439; // 0x5f504e47
     field public static final int TWEET_TRANSACTION = 1599362900; // 0x5f545754
   }
@@ -15110,6 +15153,7 @@
 
   public class RemoteException extends android.util.AndroidException {
     ctor public RemoteException();
+    ctor public RemoteException(java.lang.String);
   }
 
   public class ResultReceiver implements android.os.Parcelable {
@@ -15204,6 +15248,10 @@
     method public abstract void released();
   }
 
+  public class TransactionTooLargeException extends android.os.RemoteException {
+    ctor public TransactionTooLargeException();
+  }
+
   public class Vibrator {
     method public void cancel();
     method public boolean hasVibrator();
@@ -15720,6 +15768,7 @@
     field public static final int TYPE_NONE = 0; // 0x0
     field public static final int TYPE_OPTIONAL = 2; // 0x2
     field public static final int TYPE_REQUIRED = 1; // 0x1
+    field public static final int TYPE_RESOURCE = 3; // 0x3
   }
 
   public static final class CalendarContract.CalendarAlerts implements android.provider.BaseColumns android.provider.CalendarContract.CalendarAlertsColumns android.provider.CalendarContract.CalendarColumns android.provider.CalendarContract.EventsColumns {
@@ -15758,9 +15807,12 @@
   }
 
   protected static abstract interface CalendarContract.CalendarColumns {
+    field public static final java.lang.String ALLOWED_ATTENDEE_TYPES = "allowedAttendeeTypes";
+    field public static final java.lang.String ALLOWED_AVAILABILITY = "allowedAvailability";
     field public static final java.lang.String ALLOWED_REMINDERS = "allowedReminders";
     field public static final java.lang.String CALENDAR_ACCESS_LEVEL = "calendar_access_level";
     field public static final java.lang.String CALENDAR_COLOR = "calendar_color";
+    field public static final java.lang.String CALENDAR_COLOR_KEY = "calendar_color_index";
     field public static final java.lang.String CALENDAR_DISPLAY_NAME = "calendar_displayName";
     field public static final java.lang.String CALENDAR_TIME_ZONE = "calendar_timezone";
     field public static final int CAL_ACCESS_CONTRIBUTOR = 500; // 0x1f4
@@ -15805,6 +15857,18 @@
     field public static final java.lang.String NAME = "name";
   }
 
+  public static final class CalendarContract.Colors implements android.provider.CalendarContract.ColorsColumns {
+    field public static final android.net.Uri CONTENT_URI;
+  }
+
+  protected static abstract interface CalendarContract.ColorsColumns implements android.provider.SyncStateContract.Columns {
+    field public static final java.lang.String COLOR = "color";
+    field public static final java.lang.String COLOR_KEY = "color_index";
+    field public static final java.lang.String COLOR_TYPE = "color_type";
+    field public static final int TYPE_CALENDAR = 0; // 0x0
+    field public static final int TYPE_EVENT = 1; // 0x1
+  }
+
   public static final class CalendarContract.EventDays implements android.provider.CalendarContract.EventDaysColumns {
     method public static final android.database.Cursor query(android.content.ContentResolver, int, int, java.lang.String[]);
     field public static final android.net.Uri CONTENT_URI;
@@ -15830,6 +15894,7 @@
     field public static final java.lang.String AVAILABILITY = "availability";
     field public static final int AVAILABILITY_BUSY = 0; // 0x0
     field public static final int AVAILABILITY_FREE = 1; // 0x1
+    field public static final int AVAILABILITY_TENTATIVE = 2; // 0x2
     field public static final java.lang.String CALENDAR_ID = "calendar_id";
     field public static final java.lang.String CAN_INVITE_OTHERS = "canInviteOthers";
     field public static final java.lang.String DESCRIPTION = "description";
@@ -15837,6 +15902,7 @@
     field public static final java.lang.String DTSTART = "dtstart";
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String EVENT_COLOR = "eventColor";
+    field public static final java.lang.String EVENT_COLOR_KEY = "eventColor_index";
     field public static final java.lang.String EVENT_END_TIMEZONE = "eventEndTimezone";
     field public static final java.lang.String EVENT_LOCATION = "eventLocation";
     field public static final java.lang.String EVENT_TIMEZONE = "eventTimezone";
@@ -16572,6 +16638,10 @@
     field public static final java.lang.String PHOTO_FILE_ID = "data14";
   }
 
+  public static final class ContactsContract.Contacts.StreamItems implements android.provider.ContactsContract.StreamItemsColumns {
+    field public static final java.lang.String CONTENT_DIRECTORY = "stream_items";
+  }
+
   protected static abstract interface ContactsContract.ContactsColumns {
     field public static final java.lang.String DISPLAY_NAME = "display_name";
     field public static final java.lang.String HAS_PHONE_NUMBER = "has_phone_number";
@@ -16829,6 +16899,10 @@
     field public static final java.lang.String DATA_ID = "data_id";
   }
 
+  public static final class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
+    field public static final java.lang.String CONTENT_DIRECTORY = "stream_items";
+  }
+
   protected static abstract interface ContactsContract.RawContactsColumns {
     field public static final java.lang.String AGGREGATION_MODE = "aggregation_mode";
     field public static final java.lang.String CONTACT_ID = "contact_id";
@@ -16892,6 +16966,56 @@
     field public static final android.net.Uri PROFILE_CONTENT_URI;
   }
 
+  public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
+    field public static final java.lang.String PHOTO = "photo";
+  }
+
+  protected static abstract interface ContactsContract.StreamItemPhotosColumns {
+    field public static final java.lang.String PHOTO_FILE_ID = "photo_file_id";
+    field public static final java.lang.String PHOTO_URI = "photo_uri";
+    field public static final java.lang.String SORT_INDEX = "sort_index";
+    field public static final java.lang.String STREAM_ITEM_ID = "stream_item_id";
+    field public static final java.lang.String SYNC1 = "stream_item_photo_sync1";
+    field public static final java.lang.String SYNC2 = "stream_item_photo_sync2";
+    field public static final java.lang.String SYNC3 = "stream_item_photo_sync3";
+    field public static final java.lang.String SYNC4 = "stream_item_photo_sync4";
+  }
+
+  public static final class ContactsContract.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item";
+    field public static final android.net.Uri CONTENT_LIMIT_URI;
+    field public static final android.net.Uri CONTENT_PHOTO_URI;
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String MAX_ITEMS = "max_items";
+  }
+
+  public static final class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
+    field public static final java.lang.String CONTENT_DIRECTORY = "photo";
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo";
+    field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo";
+  }
+
+  protected static abstract interface ContactsContract.StreamItemsColumns {
+    field public static final java.lang.String ACCOUNT_NAME = "account_name";
+    field public static final java.lang.String ACCOUNT_TYPE = "account_type";
+    field public static final java.lang.String COMMENTS = "comments";
+    field public static final java.lang.String CONTACT_ID = "contact_id";
+    field public static final java.lang.String CONTACT_LOOKUP_KEY = "contact_lookup";
+    field public static final java.lang.String DATA_SET = "data_set";
+    field public static final java.lang.String RAW_CONTACT_ID = "raw_contact_id";
+    field public static final java.lang.String RAW_CONTACT_SOURCE_ID = "raw_contact_source_id";
+    field public static final java.lang.String RES_ICON = "icon";
+    field public static final java.lang.String RES_LABEL = "label";
+    field public static final java.lang.String RES_PACKAGE = "res_package";
+    field public static final java.lang.String SYNC1 = "stream_item_sync1";
+    field public static final java.lang.String SYNC2 = "stream_item_sync2";
+    field public static final java.lang.String SYNC3 = "stream_item_sync3";
+    field public static final java.lang.String SYNC4 = "stream_item_sync4";
+    field public static final java.lang.String TEXT = "text";
+    field public static final java.lang.String TIMESTAMP = "timestamp";
+  }
+
   protected static abstract interface ContactsContract.SyncColumns implements android.provider.ContactsContract.BaseSyncColumns {
     field public static final java.lang.String ACCOUNT_NAME = "account_name";
     field public static final java.lang.String ACCOUNT_TYPE = "account_type";
@@ -16946,7 +17070,7 @@
     field public static final java.lang.String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
     field public static final java.lang.String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH = "android.media.action.MEDIA_PLAY_FROM_SEARCH";
     field public static final java.lang.String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH";
-    field public static final java.lang.String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER";
+    field public static final deprecated java.lang.String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER";
     field public static final java.lang.String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
     field public static final java.lang.String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
     field public static final java.lang.String MEDIA_IGNORE_FILENAME = ".nomedia";
@@ -17309,6 +17433,7 @@
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
     method public static final void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
     field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
+    field public static final java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
     field public static final java.lang.String ADB_ENABLED = "adb_enabled";
     field public static final java.lang.String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins";
     field public static final java.lang.String ALLOW_MOCK_LOCATION = "mock_location";
@@ -18603,6 +18728,7 @@
     method public android.os.Bundle getBundle();
     method public java.lang.String getLocale();
     method public void onCancel();
+    method public void onClose();
     method public abstract void onCreate();
     method public abstract android.view.textservice.SuggestionsInfo onGetSuggestions(android.view.textservice.TextInfo, int);
     method public android.view.textservice.SuggestionsInfo[] onGetSuggestionsMultiple(android.view.textservice.TextInfo[], int, boolean);
@@ -18639,6 +18765,7 @@
     method public void onSurfaceRedrawNeeded(android.view.SurfaceHolder);
     method public void onTouchEvent(android.view.MotionEvent);
     method public void onVisibilityChanged(boolean);
+    method public void setOffsetNotificationsEnabled(boolean);
     method public void setTouchEventsEnabled(boolean);
   }
 
@@ -18777,6 +18904,7 @@
     method public boolean areDefaultsEnforced();
     method public java.lang.String getDefaultEngine();
     method public java.util.List<android.speech.tts.TextToSpeech.EngineInfo> getEngines();
+    method public java.util.Set<java.lang.String> getFeatures(java.util.Locale);
     method public java.util.Locale getLanguage();
     method public int isLanguageAvailable(java.util.Locale);
     method public boolean isSpeaking();
@@ -18784,7 +18912,8 @@
     method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
     method public deprecated int setEngineByPackageName(java.lang.String);
     method public int setLanguage(java.util.Locale);
-    method public int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
+    method public deprecated int setOnUtteranceCompletedListener(android.speech.tts.TextToSpeech.OnUtteranceCompletedListener);
+    method public int setOnUtteranceProgressListener(android.speech.tts.UtteranceProgressListener);
     method public int setPitch(float);
     method public int setSpeechRate(float);
     method public void shutdown();
@@ -18822,6 +18951,8 @@
     field public static final java.lang.String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
     field public static final java.lang.String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
     field public static final java.lang.String INTENT_ACTION_TTS_SERVICE = "android.intent.action.TTS_SERVICE";
+    field public static final java.lang.String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
+    field public static final java.lang.String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
     field public static final java.lang.String KEY_PARAM_PAN = "pan";
     field public static final java.lang.String KEY_PARAM_STREAM = "streamType";
     field public static final java.lang.String KEY_PARAM_UTTERANCE_ID = "utteranceId";
@@ -18847,6 +18978,7 @@
   public abstract class TextToSpeechService extends android.app.Service {
     ctor public TextToSpeechService();
     method public android.os.IBinder onBind(android.content.Intent);
+    method protected java.util.Set<java.lang.String> onGetFeaturesForLanguage(java.lang.String, java.lang.String, java.lang.String);
     method protected abstract java.lang.String[] onGetLanguage();
     method protected abstract int onIsLanguageAvailable(java.lang.String, java.lang.String, java.lang.String);
     method protected abstract int onLoadLanguage(java.lang.String, java.lang.String, java.lang.String);
@@ -18854,6 +18986,13 @@
     method protected abstract void onSynthesizeText(android.speech.tts.SynthesisRequest, android.speech.tts.SynthesisCallback);
   }
 
+  public abstract class UtteranceProgressListener {
+    ctor public UtteranceProgressListener();
+    method public abstract void onDone(java.lang.String);
+    method public abstract void onError(java.lang.String);
+    method public abstract void onStart(java.lang.String);
+  }
+
 }
 
 package android.telephony {
@@ -21018,6 +21157,7 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String ACTION_SUGGESTION_PICKED = "android.text.style.SUGGESTION_PICKED";
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int FLAG_AUTO_CORRECTION = 4; // 0x4
     field public static final int FLAG_EASY_CORRECT = 1; // 0x1
     field public static final int FLAG_MISSPELLED = 2; // 0x2
     field public static final int SUGGESTIONS_MAX_SIZE = 5; // 0x5
@@ -22090,6 +22230,8 @@
     field public static final int KEYCODE_BUTTON_Y = 100; // 0x64
     field public static final int KEYCODE_BUTTON_Z = 101; // 0x65
     field public static final int KEYCODE_C = 31; // 0x1f
+    field public static final int KEYCODE_CALCULATOR = 210; // 0xd2
+    field public static final int KEYCODE_CALENDAR = 208; // 0xd0
     field public static final int KEYCODE_CALL = 5; // 0x5
     field public static final int KEYCODE_CAMERA = 27; // 0x1b
     field public static final int KEYCODE_CAPS_LOCK = 115; // 0x73
@@ -22098,6 +22240,7 @@
     field public static final int KEYCODE_CHANNEL_UP = 166; // 0xa6
     field public static final int KEYCODE_CLEAR = 28; // 0x1c
     field public static final int KEYCODE_COMMA = 55; // 0x37
+    field public static final int KEYCODE_CONTACTS = 207; // 0xcf
     field public static final int KEYCODE_CTRL_LEFT = 113; // 0x71
     field public static final int KEYCODE_CTRL_RIGHT = 114; // 0x72
     field public static final int KEYCODE_D = 32; // 0x20
@@ -22165,6 +22308,7 @@
     field public static final int KEYCODE_MINUS = 69; // 0x45
     field public static final int KEYCODE_MOVE_END = 123; // 0x7b
     field public static final int KEYCODE_MOVE_HOME = 122; // 0x7a
+    field public static final int KEYCODE_MUSIC = 209; // 0xd1
     field public static final int KEYCODE_MUTE = 91; // 0x5b
     field public static final int KEYCODE_N = 42; // 0x2a
     field public static final int KEYCODE_NOTIFICATION = 83; // 0x53
@@ -22814,6 +22958,7 @@
     method public void buildDrawingCache();
     method public void buildDrawingCache(boolean);
     method public void buildLayer();
+    method public boolean callOnClick();
     method public boolean canScrollHorizontally(int);
     method public boolean canScrollVertically(int);
     method public void cancelLongPress();
@@ -22965,6 +23110,7 @@
     method public float getY();
     method public boolean hasFocus();
     method public boolean hasFocusable();
+    method public boolean hasOnClickListeners();
     method public boolean hasWindowFocus();
     method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
     method protected void initializeFadingEdge(android.content.res.TypedArray);
@@ -24082,6 +24228,8 @@
     method public int getCurrentItemIndex();
     method public int getFromIndex();
     method public int getItemCount();
+    method public int getMaxScrollX();
+    method public int getMaxScrollY();
     method public android.os.Parcelable getParcelableData();
     method public int getRemovedCount();
     method public int getScrollX();
@@ -24108,6 +24256,8 @@
     method public void setFromIndex(int);
     method public void setFullScreen(boolean);
     method public void setItemCount(int);
+    method public void setMaxScrollX(int);
+    method public void setMaxScrollY(int);
     method public void setParcelableData(android.os.Parcelable);
     method public void setPassword(boolean);
     method public void setRemovedCount(int);
@@ -24713,6 +24863,7 @@
   }
 
   public class SpellCheckerSession {
+    method public void cancel();
     method public void close();
     method public android.view.textservice.SpellCheckerInfo getSpellChecker();
     method public void getSuggestions(android.view.textservice.TextInfo, int);
@@ -24749,6 +24900,7 @@
     method public void setCookieAndSequence(int, int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 4; // 0x4
     field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1
     field public static final int RESULT_ATTR_LOOKS_LIKE_TYPO = 2; // 0x2
   }
@@ -25018,7 +25170,7 @@
     method public synchronized java.lang.String getFixedFontFamily();
     method public synchronized boolean getJavaScriptCanOpenWindowsAutomatically();
     method public synchronized boolean getJavaScriptEnabled();
-    method public deprecated synchronized android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
+    method public synchronized android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm();
     method public boolean getLightTouchEnabled();
     method public boolean getLoadWithOverviewMode();
     method public synchronized boolean getLoadsImagesAutomatically();
@@ -25065,7 +25217,7 @@
     method public synchronized void setGeolocationEnabled(boolean);
     method public synchronized void setJavaScriptCanOpenWindowsAutomatically(boolean);
     method public synchronized void setJavaScriptEnabled(boolean);
-    method public deprecated synchronized void setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm);
+    method public synchronized void setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm);
     method public void setLightTouchEnabled(boolean);
     method public void setLoadWithOverviewMode(boolean);
     method public synchronized void setLoadsImagesAutomatically(boolean);
@@ -25100,12 +25252,12 @@
     field public static final int LOAD_NO_CACHE = 2; // 0x2
   }
 
-  public static final deprecated class WebSettings.LayoutAlgorithm extends java.lang.Enum {
+  public static final class WebSettings.LayoutAlgorithm extends java.lang.Enum {
     method public static android.webkit.WebSettings.LayoutAlgorithm valueOf(java.lang.String);
     method public static final android.webkit.WebSettings.LayoutAlgorithm[] values();
     enum_constant public static final android.webkit.WebSettings.LayoutAlgorithm NARROW_COLUMNS;
     enum_constant public static final android.webkit.WebSettings.LayoutAlgorithm NORMAL;
-    enum_constant public static final android.webkit.WebSettings.LayoutAlgorithm SINGLE_COLUMN;
+    enum_constant public static final deprecated android.webkit.WebSettings.LayoutAlgorithm SINGLE_COLUMN;
   }
 
   public static final class WebSettings.PluginState extends java.lang.Enum {
@@ -26770,6 +26922,7 @@
     method public void setChar(int, java.lang.String, char);
     method public void setCharSequence(int, java.lang.String, java.lang.CharSequence);
     method public void setChronometer(int, long, java.lang.String, boolean);
+    method public void setContentDescription(int, java.lang.CharSequence);
     method public void setDisplayedChild(int, int);
     method public void setDouble(int, java.lang.String, double);
     method public void setEmptyView(int, int);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 0ec007c..c0ba543 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -212,6 +212,7 @@
         int getFlags = 0;
         boolean listDisabled = false, listEnabled = false;
         boolean listSystem = false, listThirdParty = false;
+        boolean listInstaller = false;
         try {
             String opt;
             while ((opt=nextOption()) != null) {
@@ -229,6 +230,8 @@
                     listSystem = true;
                 } else if (opt.equals("-3")) {
                     listThirdParty = true;
+                } else if (opt.equals("-i")) {
+                    listInstaller = true;
                 } else if (opt.equals("-u")) {
                     getFlags |= PackageManager.GET_UNINSTALLED_PACKAGES;
                 } else {
@@ -265,7 +268,12 @@
                         System.out.print(info.applicationInfo.sourceDir);
                         System.out.print("=");
                     }
-                    System.out.println(info.packageName);
+                    System.out.print(info.packageName);
+                    if (listInstaller) {
+                        System.out.print("  installer=");
+                        System.out.print(mPm.getInstallerPackageName(info.packageName));
+                    }
+                    System.out.println();
                 }
             }
         } catch (RemoteException e) {
@@ -1109,7 +1117,7 @@
     }
 
     private static void showUsage() {
-        System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-e] [-u] [FILTER]");
+        System.err.println("usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [FILTER]");
         System.err.println("       pm list permission-groups");
         System.err.println("       pm list permissions [-g] [-f] [-d] [-u] [GROUP]");
         System.err.println("       pm list instrumentation [-f] [TARGET-PACKAGE]");
@@ -1134,6 +1142,7 @@
         System.err.println("    -e: filter to only show enabled packages.");
         System.err.println("    -s: filter to only show system packages.");
         System.err.println("    -3: filter to only show third party packages.");
+        System.err.println("    -i: see the installer for the packages.");
         System.err.println("    -u: also include uninstalled packages.");
         System.err.println("");
         System.err.println("pm list permission-groups: prints all known permission groups.");
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 473a2d1..f427e78 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -181,7 +181,8 @@
  *
  * While the Fragment API was introduced in
  * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API
- * is also available for use on older platforms.  See the blog post
+ * at is also available for use on older platforms through
+ * {@link android.support.v4.app.FragmentActivity}.  See the blog post
  * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
  * Fragments For All</a> for more details.
  *
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index a8c9cba..1abb7de 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -51,6 +51,13 @@
  * <p>For more information about using fragments, read the
  * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p>
  * </div>
+ *
+ * While the FragmentManager API was introduced in
+ * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API
+ * at is also available for use on older platforms through
+ * {@link android.support.v4.app.FragmentActivity}.  See the blog post
+ * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
+ * Fragments For All</a> for more details.
  */
 public abstract class FragmentManager {
     /**
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 1b8a4f5..d83d2e6 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -36,7 +36,8 @@
  *
  * While the LoaderManager API was introduced in
  * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API
- * is also available for use on older platforms.  See the blog post
+ * at is also available for use on older platforms through
+ * {@link android.support.v4.app.FragmentActivity}.  See the blog post
  * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
  * Fragments For All</a> for more details.
  *
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index d71a8d6..b609c26 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -28,11 +28,21 @@
 
 /**
  * Base class for code that will receive intents sent by sendBroadcast().
- * You can either dynamically register an instance of this class with
+ *
+ * <p>If you don't need to send broadcasts across applications, consider using
+ * this class with {@link android.support.v4.content.LocalBroadcastManager} instead
+ * of the more general facilities described below.  This will give you a much
+ * more efficient implementation (no cross-process communication needed) and allow
+ * you to avoid thinking about any security issues related to other applications
+ * being able to receive or send your broadcasts.
+ *
+ * <p>You can either dynamically register an instance of this class with
  * {@link Context#registerReceiver Context.registerReceiver()}
  * or statically publish an implementation through the
  * {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
- * tag in your <code>AndroidManifest.xml</code>. <em><strong>Note:</strong></em>
+ * tag in your <code>AndroidManifest.xml</code>.
+ * 
+ * <p><em><strong>Note:</strong></em>
  * &nbsp;&nbsp;&nbsp;If registering a receiver in your
  * {@link android.app.Activity#onResume() Activity.onResume()}
  * implementation, you should unregister it in 
@@ -86,8 +96,8 @@
  * 
  * <p>Topics covered here:
  * <ol>
+ * <li><a href="#Security">Security</a>
  * <li><a href="#ReceiverLifecycle">Receiver Lifecycle</a>
- * <li><a href="#Permissions">Permissions</a>
  * <li><a href="#ProcessLifecycle">Process Lifecycle</a>
  * </ol>
  *
@@ -98,6 +108,63 @@
  * developer guide.</p>
  * </div>
  *
+ * <a name="Security"></a>
+ * <h3>Security</h3>
+ *
+ * <p>Receivers used with the {@link Context} APIs are by their nature a
+ * cross-application facility, so you must consider how other applications
+ * may be able to abuse your use of them.  Some things to consider are:
+ *
+ * <ul>
+ * <li><p>The Intent namespace is global.  Make sure that Intent action names and
+ * other strings are written in a namespace you own, or else you may inadvertantly
+ * conflict with other applications.
+ * <li><p>When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)},
+ * <em>any</em> application may send broadcasts to that registered receiver.  You can
+ * control who can send broadcasts to it through permissions described below.
+ * <li><p>When you publish a receiver in your application's manifest and specify
+ * intent-filters for it, any other application can send broadcasts to it regardless
+ * of the filters you specify.  To prevent others from sending to it, make it
+ * unavailable to them with <code>android:exported="false"</code>.
+ * <li><p>When you use {@link Context#sendBroadcast(Intent)} or related methods,
+ * normally any other application can receive these broadcasts.  You can control who
+ * can receive such broadcasts through permissions described below.  Alternatively,
+ * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you
+ * can also safely restrict the broadcast to a single application with
+ * {@link Intent#setPackage(String) Intent.setPackage}
+ * </ul>
+ *
+ * <p>None of these issues exist when using
+ * {@link android.support.v4.content.LocalBroadcastManager}, since intents
+ * broadcast it never go outside of the current process.
+ *
+ * <p>Access permissions can be enforced by either the sender or receiver
+ * of a broadcast.
+ *
+ * <p>To enforce a permission when sending, you supply a non-null
+ * <var>permission</var> argument to
+ * {@link Context#sendBroadcast(Intent, String)} or
+ * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}.
+ * Only receivers who have been granted this permission
+ * (by requesting it with the
+ * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
+ * tag in their <code>AndroidManifest.xml</code>) will be able to receive
+ * the broadcast.
+ *
+ * <p>To enforce a permission when receiving, you supply a non-null
+ * <var>permission</var> when registering your receiver -- either when calling
+ * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)}
+ * or in the static
+ * {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
+ * tag in your <code>AndroidManifest.xml</code>.  Only broadcasters who have
+ * been granted this permission (by requesting it with the
+ * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
+ * tag in their <code>AndroidManifest.xml</code>) will be able to send an
+ * Intent to the receiver.
+ *
+ * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
+ * document for more information on permissions and security in general.
+ *
  * <a name="ReceiverLifecycle"></a>
  * <h3>Receiver Lifecycle</h3>
  * 
@@ -117,37 +184,7 @@
  * {@link android.app.NotificationManager} API.  For the latter, you can
  * use {@link android.content.Context#startService Context.startService()} to
  * send a command to the service.
- * 
- * <a name="Permissions"></a>
- * <h3>Permissions</h3>
- * 
- * <p>Access permissions can be enforced by either the sender or receiver
- * of an Intent.
- * 
- * <p>To enforce a permission when sending, you supply a non-null
- * <var>permission</var> argument to
- * {@link Context#sendBroadcast(Intent, String)} or
- * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}.
- * Only receivers who have been granted this permission
- * (by requesting it with the
- * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
- * tag in their <code>AndroidManifest.xml</code>) will be able to receive
- * the broadcast.
- * 
- * <p>To enforce a permission when receiving, you supply a non-null
- * <var>permission</var> when registering your receiver -- either when calling
- * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)}
- * or in the static
- * {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
- * tag in your <code>AndroidManifest.xml</code>.  Only broadcasters who have
- * been granted this permission (by requesting it with the
- * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
- * tag in their <code>AndroidManifest.xml</code>) will be able to send an
- * Intent to the receiver.
- * 
- * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
- * document for more information on permissions and security in general.
- * 
+ *
  * <a name="ProcessLifecycle"></a>
  * <h3>Process Lifecycle</h3>
  * 
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 2d2a90d..ac05682 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -58,6 +58,13 @@
     boolean mReset = true;
     boolean mContentChanged = false;
 
+    /**
+     * An implementation of a ContentObserver that takes care of connecting
+     * it to the Loader to have the loader re-load its data when the observer
+     * is told it has changed.  You do not normally need to use this yourself;
+     * it is used for you by {@link CursorLoader} to take care of executing
+     * an update when the cursor's backing data changes.
+     */
     public final class ForceLoadContentObserver extends ContentObserver {
         public ForceLoadContentObserver() {
             super(new Handler());
@@ -74,6 +81,14 @@
         }
     }
 
+    /**
+     * Interface that is implemented to discover when a Loader has finished
+     * loading its data.  You do not normally need to implement this yourself;
+     * it is used in the implementation of {@link android.app.LoaderManager}
+     * to find out when a Loader it is managing has completed so that this can
+     * be reported to its client.  This interface should only be used if a
+     * Loader is not being used in conjunction with LoaderManager.
+     */
     public interface OnLoadCompleteListener<D> {
         /**
          * Called on the thread that created the Loader when the load is complete.
diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java
index f4a390e..ed59b03 100644
--- a/core/java/android/server/BluetoothAdapterStateMachine.java
+++ b/core/java/android/server/BluetoothAdapterStateMachine.java
@@ -360,13 +360,13 @@
             boolean retValue = HANDLED;
             switch(message.what) {
                 case USER_TURN_ON:
+                    broadcastState(BluetoothAdapter.STATE_TURNING_ON);
                     if ((Boolean) message.obj) {
                         persistSwitchSetting(true);
                     }
                     // let it fall to TURN_ON_CONTINUE:
                     //$FALL-THROUGH$
                 case TURN_ON_CONTINUE:
-                    broadcastState(BluetoothAdapter.STATE_TURNING_ON);
                     mBluetoothService.switchConnectable(true);
                     transitionTo(mSwitching);
                     break;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index b73d900..303a30e 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -59,6 +59,12 @@
     private boolean mCharsValid;
     private Spanned mSpanned;
     private final TextPaint mWorkPaint = new TextPaint();
+    private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet =
+            new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class);
+    private final SpanSet<CharacterStyle> mCharacterStyleSpanSet =
+            new SpanSet<CharacterStyle>(CharacterStyle.class);
+    private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet =
+            new SpanSet<ReplacementSpan>(ReplacementSpan.class);
 
     private static final TextLine[] sCached = new TextLine[3];
 
@@ -96,6 +102,11 @@
         tl.mText = null;
         tl.mPaint = null;
         tl.mDirections = null;
+
+        tl.mMetricAffectingSpanSpanSet.recycle();
+        tl.mCharacterStyleSpanSet.recycle();
+        tl.mReplacementSpanSpanSet.recycle();
+
         synchronized(sCached) {
             for (int i = 0; i < sCached.length; ++i) {
                 if (sCached[i] == null) {
@@ -119,7 +130,6 @@
      * @param hasTabs true if the line might contain tabs or emoji
      * @param tabStops the tabStops. Can be null.
      */
-    @SuppressWarnings("null")
     void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
             Directions directions, boolean hasTabs, TabStops tabStops) {
         mPaint = paint;
@@ -135,12 +145,10 @@
         mSpanned = null;
 
         boolean hasReplacement = false;
-        SpanSet<ReplacementSpan> replacementSpans = null;
         if (text instanceof Spanned) {
             mSpanned = (Spanned) text;
-            replacementSpans = new SpanSet<ReplacementSpan>(mSpanned, start, limit,
-                    ReplacementSpan.class);
-            hasReplacement = replacementSpans.numberOfSpans > 0;
+            mReplacementSpanSpanSet.init(mSpanned, start, limit);
+            hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
         }
 
         mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
@@ -158,9 +166,8 @@
                 // zero-width characters.
                 char[] chars = mChars;
                 for (int i = start, inext; i < limit; i = inext) {
-                    // replacementSpans cannot be null if hasReplacement is true
-                    inext = replacementSpans.getNextTransition(i, limit);
-                    if (replacementSpans.hasSpansIntersecting(i, inext)) {
+                    inext = mReplacementSpanSpanSet.getNextTransition(i, limit);
+                    if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) {
                         // transition into a span
                         chars[i - start] = '\ufffc';
                         for (int j = i - start + 1, e = inext - start; j < e; ++j) {
@@ -854,21 +861,30 @@
     }
 
     private static class SpanSet<E> {
-        final int numberOfSpans;
-        final E[] spans;
-        final int[] spanStarts;
-        final int[] spanEnds;
-        final int[] spanFlags;
+        int numberOfSpans;
+        E[] spans;
+        int[] spanStarts;
+        int[] spanEnds;
+        int[] spanFlags;
+        final Class<? extends E> classType;
+
+        SpanSet(Class<? extends E> type) {
+            classType = type;
+            numberOfSpans = 0;
+        }
 
         @SuppressWarnings("unchecked")
-        SpanSet(Spanned spanned, int start, int limit, Class<? extends E> type) {
-            final E[] allSpans = spanned.getSpans(start, limit, type);
+        public void init(Spanned spanned, int start, int limit) {
+            final E[] allSpans = spanned.getSpans(start, limit, classType);
             final int length = allSpans.length;
-            // These arrays may end up being too large because of empty spans
-            spans = (E[]) Array.newInstance(type, length);
-            spanStarts = new int[length];
-            spanEnds = new int[length];
-            spanFlags = new int[length];
+
+            if (length > 0 && (spans == null || spans.length < length)) {
+                // These arrays may end up being too large because of empty spans
+                spans = (E[]) Array.newInstance(classType, length);
+                spanStarts = new int[length];
+                spanEnds = new int[length];
+                spanFlags = new int[length];
+            }
 
             int count = 0;
             for (int i = 0; i < length; i++) {
@@ -879,30 +895,11 @@
                 if (spanStart == spanEnd) continue;
 
                 final int spanFlag = spanned.getSpanFlags(span);
-                final int priority = spanFlag & Spanned.SPAN_PRIORITY;
-                if (priority != 0 && count != 0) {
-                    int j;
 
-                    for (j = 0; j < count; j++) {
-                        final int otherPriority = spanFlags[j] & Spanned.SPAN_PRIORITY;
-                        if (priority > otherPriority) break;
-                    }
-
-                    System.arraycopy(spans, j, spans, j + 1, count - j);
-                    System.arraycopy(spanStarts, j, spanStarts, j + 1, count - j);
-                    System.arraycopy(spanEnds, j, spanEnds, j + 1, count - j);
-                    System.arraycopy(spanFlags, j, spanFlags, j + 1, count - j);
-
-                    spans[j] = span;
-                    spanStarts[j] = spanStart;
-                    spanEnds[j] = spanEnd;
-                    spanFlags[j] = spanFlag;
-                } else {
-                    spans[i] = span;
-                    spanStarts[i] = spanStart;
-                    spanEnds[i] = spanEnd;
-                    spanFlags[i] = spanFlag;
-                }
+                spans[i] = span;
+                spanStarts[i] = spanStart;
+                spanEnds[i] = spanEnd;
+                spanFlags[i] = spanFlag;
 
                 count++;
             }
@@ -927,6 +924,12 @@
             }
             return limit;
         }
+
+        public void recycle() {
+            for (int i = 0; i < numberOfSpans; i++) {
+                spans[i] = null; // prevent a leak: no reference kept when TextLine is recycled
+            }
+        }
     }
 
     /**
@@ -970,10 +973,8 @@
                     y, bottom, fmi, needWidth || mlimit < measureLimit);
         }
 
-        final SpanSet<MetricAffectingSpan> metricAffectingSpans = new SpanSet<MetricAffectingSpan>(
-                mSpanned, mStart + start, mStart + limit, MetricAffectingSpan.class);
-        final SpanSet<CharacterStyle> characterStyleSpans = new SpanSet<CharacterStyle>(
-                    mSpanned, mStart + start, mStart + limit, CharacterStyle.class);
+        mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);
+        mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit);
 
         // Shaping needs to take into account context up to metric boundaries,
         // but rendering needs to take into account character style boundaries.
@@ -985,17 +986,18 @@
             TextPaint wp = mWorkPaint;
             wp.set(mPaint);
 
-            inext = metricAffectingSpans.getNextTransition(mStart + i, mStart + limit) - mStart;
+            inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) -
+                    mStart;
             int mlimit = Math.min(inext, measureLimit);
 
             ReplacementSpan replacement = null;
 
-            for (int j = 0; j < metricAffectingSpans.numberOfSpans; j++) {
+            for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) {
                 // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT
                 // empty by construction. This special case in getSpans() explains the >= & <= tests
-                if ((metricAffectingSpans.spanStarts[j] >= mStart + mlimit) ||
-                        (metricAffectingSpans.spanEnds[j] <= mStart + i)) continue;
-                MetricAffectingSpan span = metricAffectingSpans.spans[j];
+                if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) ||
+                        (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue;
+                MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j];
                 if (span instanceof ReplacementSpan) {
                     replacement = (ReplacementSpan)span;
                 } else {
@@ -1016,16 +1018,16 @@
                         y, bottom, fmi, needWidth || mlimit < measureLimit);
             } else {
                 for (int j = i, jnext; j < mlimit; j = jnext) {
-                    jnext = characterStyleSpans.getNextTransition(mStart + j, mStart + mlimit) -
+                    jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) -
                             mStart;
 
                     wp.set(mPaint);
-                    for (int k = 0; k < characterStyleSpans.numberOfSpans; k++) {
+                    for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
                         // Intentionally using >= and <= as explained above
-                        if ((characterStyleSpans.spanStarts[k] >= mStart + jnext) ||
-                                (characterStyleSpans.spanEnds[k] <= mStart + j)) continue;
+                        if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) ||
+                                (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
 
-                        CharacterStyle span = characterStyleSpans.spans[k];
+                        CharacterStyle span = mCharacterStyleSpanSet.spans[k];
                         span.updateDrawState(wp);
                     }
 
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 5ae65df..121c6f2 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -21,7 +21,7 @@
 
 /**
  * Utility class to aid in formatting common values that are not covered
- * by {@link java.util.Formatter}
+ * by the {@link java.util.Formatter} class in {@link java.util}
  */
 public final class Formatter {
 
diff --git a/core/java/android/text/format/package.html b/core/java/android/text/format/package.html
new file mode 100644
index 0000000..b9e6a44
--- /dev/null
+++ b/core/java/android/text/format/package.html
@@ -0,0 +1,7 @@
+<HTML>
+<BODY>
+This package contains alternative classes for some text formatting classes
+defined in {@link java.util} and {@link java.text}. It also contains additional text formatting
+classes for situations not covered by {@link java.util} or {@link java.text}.
+</BODY>
+</HTML>
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1102a47..0d34b90 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4190,10 +4190,10 @@
      * {@inheritDoc}
      */
     public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
-        // The View is not attached to a window, 'visible' does not make sense, return false
-        if (mAttachInfo == null) return false;
-
-        final RectF rect = mAttachInfo.mTmpTransformRect;
+        // It doesn't make a whole lot of sense to call this on a view that isn't attached,
+        // but for some simple tests it can be useful. If we don't have attach info this
+        // will allocate memory.
+        final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
         rect.set(r);
 
         if (!child.hasIdentityMatrix()) {
@@ -4207,7 +4207,8 @@
 
         if (offset != null) {
             if (!child.hasIdentityMatrix()) {
-                float[] position = mAttachInfo.mTmpTransformLocation;
+                float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation
+                        : new float[2];
                 position[0] = offset.x;
                 position[1] = offset.y;
                 child.getMatrix().mapPoints(position);
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 31da5b5..e9a2644 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -45,14 +45,15 @@
 
     // No more than this number of words will be parsed on each iteration to ensure a minimum
     // lock of the UI thread
-    public static final int MAX_NUMBER_OF_WORDS = 10;
+    public static final int MAX_NUMBER_OF_WORDS = 50;
 
-    // Safe estimate, will ensure that the interval below usually does not have to be updated
-    public static final int AVERAGE_WORD_LENGTH = 10;
+    // Rough estimate, such that the word iterator interval usually does not need to be shifted
+    public static final int AVERAGE_WORD_LENGTH = 7;
 
     // When parsing, use a character window of that size. Will be shifted if needed
     public static final int WORD_ITERATOR_INTERVAL = AVERAGE_WORD_LENGTH * MAX_NUMBER_OF_WORDS;
 
+    // Pause between each spell check to keep the UI smooth
     private final static int SPELL_PAUSE_DURATION = 400; // milliseconds
 
     private final TextView mTextView;
@@ -75,6 +76,14 @@
 
     private Locale mCurrentLocale;
 
+    // Shared by all SpellParsers. Cannot be shared with TextView since it may be used
+    // concurrently due to the asynchronous nature of onGetSuggestions.
+    private WordIterator mWordIterator;
+
+    private TextServicesManager mTextServicesManager;
+
+    private Runnable mSpellRunnable;
+
     public SpellChecker(TextView textView) {
         mTextView = textView;
 
@@ -136,6 +145,10 @@
         for (int i = 0; i < length; i++) {
             mSpellParsers[i].stop();
         }
+
+        if (mSpellRunnable != null) {
+            mTextView.removeCallbacks(mSpellRunnable);
+        }
     }
 
     private int nextSpellCheckSpanIndex() {
@@ -285,18 +298,29 @@
             }
         }
 
-        mTextView.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                final int length = mSpellParsers.length;
-                for (int i = 0; i < length; i++) {
-                    final SpellParser spellParser = mSpellParsers[i];
-                    if (spellParser.isParsing()) {
-                        spellParser.parse();
+        scheduleNewSpellCheck();
+    }
+
+    private void scheduleNewSpellCheck() {
+        if (mSpellRunnable == null) {
+            mSpellRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    final int length = mSpellParsers.length;
+                    for (int i = 0; i < length; i++) {
+                        final SpellParser spellParser = mSpellParsers[i];
+                        if (!spellParser.isParsing()) {
+                            spellParser.parse();
+                            break; // run one spell parser at a time to bound running time
+                        }
                     }
                 }
-            }
-        }, SPELL_PAUSE_DURATION);
+            };
+        } else {
+            mTextView.removeCallbacks(mSpellRunnable);
+        }
+
+        mTextView.postDelayed(mSpellRunnable, SPELL_PAUSE_DURATION);
     }
 
     private void createMisspelledSuggestionSpan(Editable editable, SuggestionsInfo suggestionsInfo,
@@ -361,7 +385,7 @@
                 SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
         editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
-        mTextView.invalidateRegion(start, end);
+        mTextView.invalidateRegion(start, end, false /* No cursor involved */);
     }
 
     private class SpellParser {
@@ -393,20 +417,19 @@
             final int start = editable.getSpanStart(mRange);
             final int end = editable.getSpanEnd(mRange);
 
-            final WordIterator wordIterator = mTextView.getWordIterator();
             int wordIteratorWindowEnd = Math.min(end, start + WORD_ITERATOR_INTERVAL);
-            wordIterator.setCharSequence(editable, start, wordIteratorWindowEnd);
+            mWordIterator.setCharSequence(editable, start, wordIteratorWindowEnd);
 
             // Move back to the beginning of the current word, if any
-            int wordStart = wordIterator.preceding(start);
+            int wordStart = mWordIterator.preceding(start);
             int wordEnd;
             if (wordStart == BreakIterator.DONE) {
-                wordEnd = wordIterator.following(start);
+                wordEnd = mWordIterator.following(start);
                 if (wordEnd != BreakIterator.DONE) {
-                    wordStart = wordIterator.getBeginning(wordEnd);
+                    wordStart = mWordIterator.getBeginning(wordEnd);
                 }
             } else {
-                wordEnd = wordIterator.getEnd(wordStart);
+                wordEnd = mWordIterator.getEnd(wordStart);
             }
             if (wordEnd == BreakIterator.DONE) {
                 removeRangeSpan(editable);
@@ -472,15 +495,15 @@
 
                 // iterate word by word
                 int originalWordEnd = wordEnd;
-                wordEnd = wordIterator.following(wordEnd);
+                wordEnd = mWordIterator.following(wordEnd);
                 if ((wordIteratorWindowEnd < end) &&
                         (wordEnd == BreakIterator.DONE || wordEnd >= wordIteratorWindowEnd)) {
                     wordIteratorWindowEnd = Math.min(end, originalWordEnd + WORD_ITERATOR_INTERVAL);
-                    wordIterator.setCharSequence(editable, originalWordEnd, wordIteratorWindowEnd);
-                    wordEnd = wordIterator.following(originalWordEnd);
+                    mWordIterator.setCharSequence(editable, originalWordEnd, wordIteratorWindowEnd);
+                    wordEnd = mWordIterator.following(originalWordEnd);
                 }
                 if (wordEnd == BreakIterator.DONE) break;
-                wordStart = wordIterator.getBeginning(wordEnd);
+                wordStart = mWordIterator.getBeginning(wordEnd);
                 if (wordStart == BreakIterator.DONE) {
                     break;
                 }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5b73a74..66fd06d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -339,7 +339,7 @@
 
     private int mCursorDrawableRes;
     private final Drawable[] mCursorDrawable = new Drawable[2];
-    private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2
+    private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split)
 
     private Drawable mSelectHandleLeft;
     private Drawable mSelectHandleRight;
@@ -3210,7 +3210,6 @@
         }
 
         boolean needEditableForNotification = false;
-        boolean startSpellCheck = false;
 
         if (mListeners != null && mListeners.size() != 0) {
             needEditableForNotification = true;
@@ -3222,7 +3221,6 @@
             setFilters(t, mFilters);
             InputMethodManager imm = InputMethodManager.peekInstance();
             if (imm != null) imm.restartInput(this);
-            startSpellCheck = true;
         } else if (type == BufferType.SPANNABLE || mMovement != null) {
             text = mSpannableFactory.newSpannable(text);
         } else if (!(text instanceof CharWrapper)) {
@@ -3311,11 +3309,6 @@
         sendOnTextChanged(text, 0, oldlen, textLength);
         onTextChanged(text, 0, oldlen, textLength);
 
-        if (startSpellCheck && mSpellChecker != null) {
-            // This view has to have been previously attached for mSpellChecker to exist  
-            updateSpellCheckSpans(0, textLength);
-        }
-
         if (needEditableForNotification) {
             sendAfterTextChanged((Editable) text);
         }
@@ -4329,7 +4322,7 @@
         if (a >= 0 || b >= 0 || c >= 0) {
             int start = Math.min(Math.min(a, b), c);
             int end = Math.max(Math.max(a, b), c);
-            invalidateRegion(start, end);
+            invalidateRegion(start, end, true /* Also invalidates blinking cursor */);
         }
     }
 
@@ -4338,7 +4331,7 @@
      *
      * @hide
      */
-    void invalidateRegion(int start, int end) {
+    void invalidateRegion(int start, int end, boolean invalidateCursor) {
         if (mLayout == null) {
             invalidate();
         } else {
@@ -4364,11 +4357,19 @@
 
                 int bottom = mLayout.getLineBottom(lineEnd);
 
+                if (invalidateCursor) {
+                    for (int i = 0; i < mCursorCount; i++) {
+                        Rect bounds = mCursorDrawable[i].getBounds();
+                        top = Math.min(top, bounds.top);
+                        bottom = Math.max(bottom, bounds.bottom);
+                    }
+                }
+
                 final int compoundPaddingLeft = getCompoundPaddingLeft();
                 final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
 
                 int left, right;
-                if (lineStart == lineEnd) {
+                if (lineStart == lineEnd && !invalidateCursor) {
                     left = (int) mLayout.getPrimaryHorizontal(start);
                     right = (int) (mLayout.getPrimaryHorizontal(end) + 1.0);
                     left += compoundPaddingLeft;
@@ -4482,8 +4483,8 @@
 
         // Resolve drawables as the layout direction has been resolved
         resolveDrawables();
-        
-        updateSpellCheckSpans(0, mText.length());
+
+        updateSpellCheckSpans(0, mText.length(), true /* create the spell checker if needed */);
     }
 
     @Override
@@ -7636,7 +7637,7 @@
             }
         }
 
-        updateSpellCheckSpans(start, start + after);
+        updateSpellCheckSpans(start, start + after, false);
 
         // Hide the controllers as soon as text is modified (typing, procedural...)
         // We do not hide the span controllers, since they can be added when a new text is
@@ -7794,17 +7795,22 @@
             }
         }
 
-        if (newStart < 0 && what instanceof SpellCheckSpan) {
-            getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what);
+        if (mSpellChecker != null && newStart < 0 && what instanceof SpellCheckSpan) {
+            mSpellChecker.removeSpellCheckSpan((SpellCheckSpan) what);
         }
     }
 
     /**
      * Create new SpellCheckSpans on the modified region.
      */
-    private void updateSpellCheckSpans(int start, int end) {
+    private void updateSpellCheckSpans(int start, int end, boolean createSpellChecker) {
         if (isTextEditable() && isSuggestionsEnabled()) {
-            getSpellChecker().spellCheck(start, end);
+            if (mSpellChecker == null && createSpellChecker) {
+                mSpellChecker = new SpellChecker(this);
+            }
+            if (mSpellChecker != null) {
+                mSpellChecker.spellCheck(start, end);
+            }
         }
     }
 
@@ -8976,13 +8982,6 @@
         return packRangeInLong(offset,  offset);
     }
 
-    private SpellChecker getSpellChecker() {
-        if (mSpellChecker == null) {
-            mSpellChecker = new SpellChecker(this);
-        }
-        return mSpellChecker;
-    }
-
     private long getLastTouchOffsets() {
         SelectionModifierCursorController selectionController = getSelectionController();
         final int minOffset = selectionController.getMinTouchOffset();
@@ -9937,7 +9936,7 @@
                 // There is no way to know if the word was indeed added. Re-check.
                 // TODO The ExtractEditText should remove the span in the original text instead
                 editable.removeSpan(suggestionInfo.suggestionSpan);
-                updateSpellCheckSpans(spanStart, spanEnd);
+                updateSpellCheckSpans(spanStart, spanEnd, false);
             } else {
                 // SuggestionSpans are removed by replace: save them before
                 SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml
index c65dd83..0b94fc1 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml
@@ -93,17 +93,6 @@
         android:layout_marginLeft="8dip"
         android:layout_marginRight="8dip">
 
-        <Button android:id="@+id/ok"
-            android:text="@android:string/ok"
-            android:layout_alignParentBottom="true"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1.0"
-            android:layout_marginBottom="8dip"
-            android:layout_marginRight="8dip"
-            android:textSize="18sp"
-            />
-
         <Button android:id="@+id/emergencyCallButton"
             android:text="@android:string/lockscreen_emergency_call"
             android:layout_alignParentBottom="true"
@@ -112,11 +101,22 @@
             android:layout_height="wrap_content"
             android:layout_weight="1.0"
             android:layout_marginBottom="8dip"
-            android:layout_marginLeft="8dip"
+            android:layout_marginRight="8dip"
             android:textSize="18sp"
             android:drawableLeft="@drawable/ic_emergency"
             android:drawablePadding="8dip"
         />
+
+        <Button android:id="@+id/ok"
+            android:text="@android:string/ok"
+            android:layout_alignParentBottom="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:textSize="18sp"
+        />
     </LinearLayout>
 
 </RelativeLayout>
diff --git a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml
index 59065e1..3cb19c3 100644
--- a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml
@@ -91,17 +91,6 @@
         android:layout_marginLeft="8dip"
         android:layout_marginRight="8dip">
 
-        <Button android:id="@+id/ok"
-            android:text="@android:string/ok"
-            android:layout_alignParentBottom="true"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1.0"
-            android:layout_marginBottom="8dip"
-            android:layout_marginRight="8dip"
-            android:textSize="18sp"
-            />
-
         <Button android:id="@+id/emergencyCallButton"
             android:text="@android:string/lockscreen_emergency_call"
             android:layout_alignParentBottom="true"
@@ -110,11 +99,22 @@
             android:layout_height="wrap_content"
             android:layout_weight="1.0"
             android:layout_marginBottom="8dip"
-            android:layout_marginLeft="8dip"
+            android:layout_marginRight="8dip"
             android:textSize="18sp"
             android:drawableLeft="@drawable/ic_emergency"
             android:drawablePadding="4dip"
         />
+
+        <Button android:id="@+id/ok"
+            android:text="@android:string/ok"
+            android:layout_alignParentBottom="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:textSize="18sp"
+        />
     </LinearLayout>
 
 </RelativeLayout>
diff --git a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
index b662e82..722dc26 100644
--- a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
@@ -153,17 +153,6 @@
         android:layout_marginLeft="8dip"
         android:layout_marginRight="8dip">
 
-        <Button android:id="@+id/ok"
-            android:text="@android:string/ok"
-            android:layout_alignParentBottom="true"
-            android:layout_width="0dip"
-            android:layout_height="wrap_content"
-            android:layout_weight="1.0"
-            android:layout_marginBottom="8dip"
-            android:layout_marginRight="8dip"
-            android:textSize="18sp"
-            />
-
         <Button android:id="@+id/emergencyCallButton"
             android:text="@android:string/lockscreen_emergency_call"
             android:layout_alignParentBottom="true"
@@ -172,11 +161,22 @@
             android:layout_height="wrap_content"
             android:layout_weight="1.0"
             android:layout_marginBottom="8dip"
-            android:layout_marginLeft="8dip"
+            android:layout_marginRight="8dip"
             android:textSize="18sp"
             android:drawableLeft="@drawable/ic_emergency"
             android:drawablePadding="4dip"
         />
+
+        <Button android:id="@+id/ok"
+            android:text="@android:string/ok"
+            android:layout_alignParentBottom="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:textSize="18sp"
+        />
     </LinearLayout>
 
 </RelativeLayout>
diff --git a/core/res/res/layout/twelve_key_entry.xml b/core/res/res/layout/twelve_key_entry.xml
index 46301cd..09c749d 100644
--- a/core/res/res/layout/twelve_key_entry.xml
+++ b/core/res/res/layout/twelve_key_entry.xml
@@ -144,7 +144,7 @@
         android:layout_marginRight="2dip"
         android:orientation="horizontal">
 
-        <Button android:id="@+id/ok"
+        <Button android:id="@+id/cancel"
             android:layout_width="0sp"
             android:layout_height="fill_parent"
             android:layout_weight="1"
@@ -152,7 +152,7 @@
             android:layout_marginRight="2dip"
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:textStyle="bold"
-            android:text="@android:string/ok"
+            android:text="@android:string/cancel"
         />
 
         <Button android:id="@+id/zero"
@@ -165,7 +165,7 @@
             android:textStyle="bold"
         />
 
-        <Button android:id="@+id/cancel"
+        <Button android:id="@+id/ok"
             android:layout_width="0sp"
             android:layout_height="fill_parent"
             android:layout_weight="1"
@@ -173,7 +173,7 @@
             android:layout_marginRight="2dip"
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:textStyle="bold"
-            android:text="@android:string/cancel"
+            android:text="@android:string/ok"
         />
 
     </LinearLayout>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
index e22b018..e44023b 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
@@ -271,4 +271,46 @@
 
     }
 
+    // Test case 6: test configured network status
+    @LargeTest
+    public void testWifiConfiguredNetworkStatus() {
+
+        /* Initialize */
+        mWifiManager.setWifiEnabled(false);
+        sleepAfterWifiEnable();
+
+        /* Ensure no network is CURRENT */
+        List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration c : configList) {
+            assertTrue(c.status != WifiConfiguration.Status.CURRENT);
+        }
+
+        /* Enable wifi */
+        mWifiManager.setWifiEnabled(true);
+        sleepAfterWifiEnable();
+
+        /* Ensure connected network is CURRENT */
+        String connectedSSID = mWifiManager.getConnectionInfo().getSSID();
+        configList = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration c : configList) {
+            if (c.SSID.contains(connectedSSID)) {
+                assertTrue(c.status == WifiConfiguration.Status.CURRENT);
+            } else {
+                assertTrue(c.status != WifiConfiguration.Status.CURRENT);
+            }
+        }
+
+        /* Disable wifi */
+        mWifiManager.setWifiEnabled(false);
+        sleepAfterWifiEnable();
+
+        /* Ensure no network is CURRENT */
+        configList = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration c : configList) {
+            assertTrue(c.status != WifiConfiguration.Status.CURRENT);
+        }
+    }
+
+
+
 }
diff --git a/docs/html/sdk/android-4.0.jd b/docs/html/sdk/android-4.0.jd
index e886bdf..7161b03 100644
--- a/docs/html/sdk/android-4.0.jd
+++ b/docs/html/sdk/android-4.0.jd
@@ -65,26 +65,42 @@
 <p>To determine what revision of the Android {@sdkPlatformVersion} platform you
 have installed, refer to the "Installed Packages" listing in the Android SDK Manager.</p>
 
+<p class="caution"><strong>Important:</strong> To download the new Android
+4.0 system components from the Android SDK Manager, you must first update the
+SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not,
+the Android 4.0 system components will not be available for download.</p>
 
 <div class="toggle-content opened" style="padding-left:1em;">
 
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png"
 class="toggle-content-img" alt="" />
+    Android {@sdkPlatformVersion}, Revision 2</a> <em>(December 2011)</em>
+  </a></p>
+
+  <div class="toggle-content-toggleme" style="padding-left:2em;">
+    <p>Maintenance update. The system version is 4.0.2.</p>
+    <dl>
+      <dt>Dependencies:</dt>
+      <dd>SDK Tools r14 or higher is required.</dd>
+    </dl>
+  </div>
+</div>
+
+<div class="toggle-content closed" style="padding-left:1em;">
+
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png"
+class="toggle-content-img" alt="" />
     Android {@sdkPlatformVersion}, Revision 1</a> <em>(October 2011)</em>
   </a></p>
 
   <div class="toggle-content-toggleme" style="padding-left:2em;">
-
-<dl>
-<dt>Initial release. SDK Tools r14 or higher is required.
-  <p class="caution"><strong>Important:</strong> To download the new Android
-  4.0 system components from the Android SDK Manager, you must first update the
-  SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not,
-  the Android 4.0 system components will not be available for download.</p>
-</dt>
-</dl>
-
+    <p>Initial release. The system version is 4.0.1.</p>
+    <dl>
+      <dt>Dependencies:</dt>
+      <dd>SDK Tools r14 or higher is required.</dd>
+    </dl>
   </div>
 </div>
 
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 50b20ce..2445bff 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -1,8 +1,8 @@
 page.title=ADT Plugin for Eclipse
-adt.zip.version=15.0.1
-adt.zip.download=ADT-15.0.1.zip
-adt.zip.bytes=6752327
-adt.zip.checksum=2c12a71d7124aa512b8ee016e19c0e69
+adt.zip.version=16.0.0
+adt.zip.download=ADT-16.0.0.zip
+adt.zip.bytes=6999205
+adt.zip.checksum=b7e512572580291279469845386b31b6
 
 @jd:body
 
@@ -109,18 +109,49 @@
 </style>
 
 
-
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
         <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px"
 width="9px" />
+ADT 16.0.0</a> <em>(December 2011)</em>
+  <div class="toggleme">
+<dl>
+  <dt>Dependencies:</dt>
+
+  <dd>
+    <ul>
+      <li>Eclipse Helios (Version 3.6) or higher is required for ADT
+16.0.0.</li>
+      <li>ADT 16.0.0 is designed for use with <a
+href="{@docRoot}sdk/tools-notes.html">SDK Tools r16</a>. If you haven't already installed SDK Tools
+r16 into your SDK, use the Android SDK Manager to do so.</li>
+    </ul>
+  </dd>
+
+  <dt>General improvements:</dt>
+  <dd>
+    <ul>
+      <li>Added Lint tools to detect common errors in Android projects. (<a
+href="http://tools.android.com/recent/lint">more info</a>)</li>
+    </ul>
+  </dd>
+</dl>
+
+</div>
+</div>
+
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+        <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
+width="9px" />
 ADT 15.0.1</a> <em>(November 2011)</em>
   <div class="toggleme">
 <dl>
   <dt>Dependencies:</dt>
   
   <dd>ADT 15.0.1 is designed for use with <a href="{@docRoot}sdk/tools-notes.html">SDK Tools r15</a>.
-  If you haven't already installed SDK Tools r15 into your SDK, use the Android SDK and AVD Manager to
+  If you haven't already installed SDK Tools r15 into your SDK, use the Android SDK Manager to
   do so.</dd>
   
   <dt>Bug fixes:</dt>
@@ -154,7 +185,7 @@
 <dt>Dependencies:</dt>
 
 <dd>ADT 15.0.0 is designed for use with <a href="{@docRoot}sdk/tools-notes.html">SDK Tools r15</a>.
-If you haven't already installed SDK Tools r15 into your SDK, use the Android SDK and AVD Manager to
+If you haven't already installed SDK Tools r15 into your SDK, use the Android SDK Manager to
 do so.</dd>
 
 <dt>Bug fixes:</dt>
@@ -185,10 +216,10 @@
 <dt>Dependencies:</dt>
 
 <dd>ADT 14.0.0 is designed for use with <a href="{@docRoot}sdk/tools-notes.html">SDK Tools r14</a>.
-If you haven't already installed SDK Tools r14 into your SDK, use the Android SDK and AVD Manager to
+If you haven't already installed SDK Tools r14 into your SDK, use the Android SDK Manager to
 do so.</dd>
 
-<dt>Build system</dt>
+<dt>Build system:</dt>
 <dd>
   <ul>
     <li>Changed <code>default.properties</code> to <code>project.properties</code> and
@@ -211,7 +242,7 @@
 site</a>.</p>
 </dd>
 
-<dt>General improvements</dt>
+<dt>General improvements:</dt>
 <dd>
   <ul>
 
@@ -236,7 +267,7 @@
 </ul>
 </dd>
 
-<dt>XML and Java editors</dt>
+<dt>XML and Java editors:</dt>
 <dd>
   <ul>
     <li>Added a new XML formatter that formats all XML files according to the
@@ -255,7 +286,7 @@
   </ul>
 </dd>
 
-<dt>Layout editor</dt>
+<dt>Layout editor:</dt>
 <dd>
   <ul>
     <li>Added tooltip feedback for dragging and resizing operations. For
@@ -281,7 +312,7 @@
   </ul>
 </dd>
 
-<dt>Bug fixes</dt>
+<dt>Bug fixes:</dt>
 <dd>Fixed many bugs and added <a
 href="http://tools.android.com/recent/miscellaneousrecentfixes">minor improvements</a>, in
 particular some <a href="http://tools.android.com/recent/linuxfixes">critical bug fixes on
@@ -324,7 +355,7 @@
 </ul>
 </dd>
 
-<dt>Build system</dt>
+<dt>Build system:</dt>
 <dd>
 <ul>
   <li id="build-option">A new option lets you disable the packaging step in the automatic
@@ -336,7 +367,7 @@
 </ul>
 </dd>
 
-<dt>Bug fixes</dt>
+<dt>Bug fixes:</dt>
 <dd>Many bug fixes are part of this release
 (<a href="http://tools.android.com/recent/adt12bugfixroundup">more info</a>).</dd>
 
@@ -928,7 +959,7 @@
 see <a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.</p>
 </dd>
 
-<dt>General Notes:</dt>
+<dt>General notes:</dt>
 <dd>
 <ul>
 <li>AVD Launch dialog now shows scale value.</li>
@@ -974,7 +1005,7 @@
 </ul>
 </dd>
 
-<dt>DDMS Integration:</dt>
+<dt>DDMS integration:</dt>
 <dd>
 <ul>
 <li>Includes the improvements from the standlone DDMS, revision 3.</li>
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 193066b..65a1f46 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -1,21 +1,21 @@
 page.title=Android SDK
 sdk.redirect=0
 
-sdk.win_installer=installer_r15-windows.exe
-sdk.win_installer_bytes=33902520
-sdk.win_installer_checksum=ee8481cb86a6646a4d963d5142902c5c
+sdk.win_installer=installer_r16-windows.exe
+sdk.win_installer_bytes=29561554
+sdk.win_installer_checksum=3521dda4904886b05980590f83cf3469
 
-sdk.win_download=android-sdk_r15-windows.zip
-sdk.win_bytes=33895447
-sdk.win_checksum=cc2aadf7120d12b574981461736a96e9
+sdk.win_download=android-sdk_r16-windows.zip
+sdk.win_bytes=29562413
+sdk.win_checksum=6b926d0c0a871f1a946e65259984701a
 
-sdk.mac_download=android-sdk_r15-macosx.zip
-sdk.mac_bytes=30469921
-sdk.mac_checksum=03d2cdd3565771e8c7a438f1c40cc8a5
+sdk.mac_download=android-sdk_r16-macosx.zip
+sdk.mac_bytes=26158334
+sdk.mac_checksum=d1dc2b6f13eed5e3ce5cf26c4e4c47aa
 
-sdk.linux_download=android-sdk_r15-linux.tgz
-sdk.linux_bytes=26124434
-sdk.linux_checksum=f529681fd1eda11c6e1e1d44b42c1432
+sdk.linux_download=android-sdk_r16-linux.tgz
+sdk.linux_bytes=22048174
+sdk.linux_checksum=3ba457f731d51da3741c29c8830a4583
 
 @jd:body
 
diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd
index f12d0aa..c970f6c9 100644
--- a/docs/html/sdk/requirements.jd
+++ b/docs/html/sdk/requirements.jd
@@ -24,8 +24,8 @@
 
 <h4 style="margin-top:.25em"><em>Eclipse IDE</em></h4>
     <ul>
-      <li>Eclipse 3.5 (Galileo) or greater 
-<p class="note"><strong>Note:</strong> Eclipse 3.4 (Ganymede) is no longer
+      <li>Eclipse 3.6 (Helios) or greater
+<p class="note"><strong>Note:</strong> Eclipse 3.5 (Galileo) is no longer
 supported with the latest version of ADT.</p></li>
       <li>Eclipse <a href="http://www.eclipse.org/jdt">JDT</a> plugin (included
 in most Eclipse IDE packages) </li>
@@ -37,7 +37,7 @@
 packages: </p>
          <ul>
            <li>Eclipse IDE for Java Developers</li>
-           <li>Eclipse Classic (versions 3.5.1 and higher)</li>
+           <li>Eclipse Classic</li>
            <li>Eclipse IDE for Java EE Developers</li>
          </ul>
       </li>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 0ae2c6d..791e7aa 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -150,7 +150,7 @@
       </li>
     </ul>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r15</a> <span
+      <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r16</a> <span
 class="new">new!</span></li>
       <li><a href="<?cs var:toroot ?>sdk/win-usb.html">Google USB Driver, r4</a></li>
       <li><a href="<?cs var:toroot ?>sdk/compatibility-library.html">Support Package, r4</a>
@@ -169,7 +169,7 @@
       <span style="display:none" class="zh-TW"></span>
       </h2>
     <ul>
-      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 15.0.1
+      <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 16.0.0
       <span style="display:none" class="de"></span>
       <span style="display:none" class="es"></span>
       <span style="display:none" class="fr"></span>
diff --git a/docs/html/sdk/tools-notes.jd b/docs/html/sdk/tools-notes.jd
index cd03d9f..9a63467 100644
--- a/docs/html/sdk/tools-notes.jd
+++ b/docs/html/sdk/tools-notes.jd
@@ -9,7 +9,7 @@
 latest revision of the SDK Tools in the <code>&lt;sdk&gt;/tools</code> directory.</p>
 
 <p>If you are already using the SDK and you want to update to the latest version
-of the SDK Tools, use the <em>Android SDK and AVD Manager</em> to get the
+of the SDK Tools, use the <em>Android SDK Manager</em> to get the
 update, rather than downloading a new SDK starter package. For more information
 about how to update, see <a
 href="{@docRoot}sdk/adding-components.html#UpdatingComponents">Updating SDK
@@ -20,8 +20,7 @@
 
 <p>The sections below provide notes about successive releases of
 the SDK Tools, as denoted by revision number. To determine what revision of the SDK
-Tools you are using, refer to the "Installed Packages" listing in the Android SDK
-and AVD Manager. </p>
+Tools you are using, refer to the "Installed Packages" listing in the Android SDK Manager. </p>
 
 <p>For a summary of all known issues in SDK Tools, see <a
 href="http://tools.android.com/knownissues">http://tools.android.com/knownissues</a>.</p>
@@ -69,7 +68,57 @@
 <div class="toggleable opened">
   <a href="#" onclick="return toggleDiv(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px"
-    width="9px" />SDK Tools, Revision 15</a> <em>(October 2011)</em>
+    width="9px" />
+    SDK Tools, Revision 16</a> <em>(December 2011)</em>
+
+  <div class="toggleme">
+    <p class="caution"><strong>Important:</strong> To download the new Android
+    4.0 system components from the Android SDK Manager, you must first update the
+    SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not,
+    the Android 4.0 system components will not be available for download.</p>
+
+<dl>
+<dt>Dependencies:</dt>
+<dd>
+  <ul>
+    <li>Android SDK Platform-tools revision 9 or later.</li>
+    <li>If you are developing in Eclipse with ADT, note that the SDK Tools r16 is designed for use
+    with ADT 16.0.0 and later. If you haven't already, we highly recommend updating your
+    <a href="{@docRoot}sdk/eclipse-adt.html">ADT Plugin</a> to 16.0.0.</li>
+    <li>If you are developing outside Eclipse, you must have <a href="http://ant.apache.org/">Apache
+    Ant</a> 1.8 or later.</li>
+</ul>
+</dd>
+<dt>General notes:</dt>
+<dd>
+  <ul>
+    <li>Added Lint tools to detect common errors in Android projects. (<a
+href="http://tools.android.com/recent/lint">more info</a>)</li>
+    <li>Added sensor emulation support, which allows the emulator to read sensor data from a
+physical Android device.</li>
+    <li>Added support for using a webcam to emulate a camera on Mac OS X.</li>
+  </ul>
+</dd>
+<dt>Bug fixes:</dt>
+<dd>
+  <ul>
+    <li>Snapshots now work for Android 4.0 system images.</li>
+    <li>Fixed several small issues for the build file.
+    (<a href="http://code.google.com/p/android/issues/detail?id=21023">Issue 21023</a>,
+    <a href="http://code.google.com/p/android/issues/detail?id=21267">Issue 21267</a>,
+    <a href="http://code.google.com/p/android/issues/detail?id=21465">Issue 21465</a>,
+    <a href="http://code.google.com/p/android/issues/detail?id=21525">Issue 21525</a>).</li>
+  </ul>
+</dd>
+</dl>
+</div>
+</div>
+
+<div class="toggleable closed">
+  <a href="#" onclick="return toggleDiv(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
+    width="9px" />
+    SDK Tools, Revision 15</a> <em>(October 2011)</em>
 
   <div class="toggleme">
     <p class="caution"><strong>Important:</strong> To download the new Android
@@ -116,7 +165,8 @@
 <div class="toggleable closed">
   <a href="#" onclick="return toggleDiv(this)">
     <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px"
-    width="9px" />SDK Tools, Revision 14</a> <em>(October 2011)</em>
+    width="9px" />
+    SDK Tools, Revision 14</a> <em>(October 2011)</em>
 
   <div class="toggleme">
     <p class="note"><strong>Important:</strong> To download the new Android
@@ -137,10 +187,11 @@
 <dt>General notes:</dt>
 <dd>
   <ul>
-    <li>Added webcam support to Android 4.0 or later platforms to emulate rear-facing cameras when one webcam is present,
-    and to emulate both rear-facing and front-facing cameras when two webcams are present. Webcam suport is for Windows and Linux only.
+    <li>Added webcam support to Android 4.0 or later platforms to emulate rear-facing cameras when
+    one webcam is present, and to emulate both rear-facing and front-facing cameras when two
+    webcams are present. Webcam support is for Windows and Linux only.
     Mac support will come in a later release.</li>
-     <li>Changed <code>default.properties</code> to <code>project.properties</code> and
+    <li>Changed <code>default.properties</code> to <code>project.properties</code> and
     <code>build.properties</code> to <code>ant.properties</code>. Any existing
     projects that you build with Ant must be updated with the <code>android update project</code>
     command.</li>
@@ -428,7 +479,7 @@
 for more information.</li>
 <li>Fixes location control in DDMS to work in any locale not using '.' as a
 decimal point.</li>
-</li>
+</ul>
 </ul>
 </dd>
 </dl>
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 3abf3d3..f6552dd 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -99,11 +99,12 @@
 
 status_t DrmManager::loadPlugIns() {
 
+    String8 pluginDirPath("/system/lib/drm");
+    loadPlugIns(pluginDirPath);
+
     String8 vendorPluginDirPath("/vendor/lib/drm");
     loadPlugIns(vendorPluginDirPath);
 
-    String8 pluginDirPath("/system/lib/drm");
-    loadPlugIns(pluginDirPath);
     return DRM_NO_ERROR;
 
 }
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 6f3051a..4310bed 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -500,8 +500,8 @@
         eglDestroySyncKHR(dpy, fence);
     }
 
-    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
-            mSlots[buf].mGraphicBuffer->handle, returnFlags);
+    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
+            mSlots[*outBuf].mGraphicBuffer->handle, returnFlags);
 
     return returnFlags;
 }
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index f293cba..4da576d 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -73,6 +73,9 @@
     glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
 
     mCurrentBuffer = meshBuffer;
+    mCurrentPositionPointer = this;
+    mCurrentTexCoordsPointer = this;
+
     mRegionMesh = NULL;
 
     blend = false;
@@ -218,22 +221,49 @@
 // VBO
 ///////////////////////////////////////////////////////////////////////////////
 
-void Caches::bindMeshBuffer() {
-    bindMeshBuffer(meshBuffer);
+bool Caches::bindMeshBuffer() {
+    return bindMeshBuffer(meshBuffer);
 }
 
-void Caches::bindMeshBuffer(const GLuint buffer) {
+bool Caches::bindMeshBuffer(const GLuint buffer) {
     if (mCurrentBuffer != buffer) {
         glBindBuffer(GL_ARRAY_BUFFER, buffer);
         mCurrentBuffer = buffer;
+        return true;
     }
+    return false;
 }
 
-void Caches::unbindMeshBuffer() {
+bool Caches::unbindMeshBuffer() {
     if (mCurrentBuffer) {
         glBindBuffer(GL_ARRAY_BUFFER, 0);
         mCurrentBuffer = 0;
+        return true;
     }
+    return false;
+}
+
+void Caches::bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices, GLsizei stride) {
+    if (force || vertices != mCurrentPositionPointer) {
+        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
+        mCurrentPositionPointer = vertices;
+    }
+}
+
+void Caches::bindTexCoordsVertexPointer(bool force, GLuint slot, GLvoid* vertices) {
+    if (force || vertices != mCurrentTexCoordsPointer) {
+        glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, gMeshStride, vertices);
+        mCurrentTexCoordsPointer = vertices;
+    }
+}
+
+void Caches::resetVertexPointers() {
+    mCurrentPositionPointer = this;
+    mCurrentTexCoordsPointer = this;
+}
+
+void Caches::resetTexCoordsVertexPointer() {
+    mCurrentTexCoordsPointer = this;
 }
 
 TextureVertex* Caches::getRegionMesh() {
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 5e58a9e..7ca198a 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -91,15 +91,6 @@
 
     CacheLogger mLogger;
 
-    GLuint mCurrentBuffer;
-
-    // Used to render layers
-    TextureVertex* mRegionMesh;
-    GLuint mRegionMeshIndices;
-
-    mutable Mutex mGarbageLock;
-    Vector<Layer*> mLayerGarbage;
-
 public:
     enum FlushMode {
         kFlushMode_Layers = 0,
@@ -147,17 +138,36 @@
     /**
      * Binds the VBO used to render simple textured quads.
      */
-    void bindMeshBuffer();
+    bool bindMeshBuffer();
 
     /**
      * Binds the specified VBO if needed.
      */
-    void bindMeshBuffer(const GLuint buffer);
+    bool bindMeshBuffer(const GLuint buffer);
 
     /**
      * Unbinds the VBO used to render simple textured quads.
      */
-    void unbindMeshBuffer();
+    bool unbindMeshBuffer();
+
+    /**
+     * Binds an attrib to the specified float vertex pointer.
+     * Assumes a stride of gMeshStride and a size of 2.
+     */
+    void bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices,
+            GLsizei stride = gMeshStride);
+
+    /**
+     * Binds an attrib to the specified float vertex pointer.
+     * Assumes a stride of gMeshStride and a size of 2.
+     */
+    void bindTexCoordsVertexPointer(bool force, GLuint slot, GLvoid* vertices);
+
+    /**
+     * Resets the vertex pointers.
+     */
+    void resetVertexPointers();
+    void resetTexCoordsVertexPointer();
 
     /**
      * Returns the mesh used to draw regions. Calling this method will
@@ -203,6 +213,17 @@
     ResourceCache resourceCache;
 
 private:
+    GLuint mCurrentBuffer;
+    void* mCurrentPositionPointer;
+    void* mCurrentTexCoordsPointer;
+
+    // Used to render layers
+    TextureVertex* mRegionMesh;
+    GLuint mRegionMeshIndices;
+
+    mutable Mutex mGarbageLock;
+    Vector<Layer*> mLayerGarbage;
+
     DebugLevel mDebugLevel;
     bool mInitialized;
 }; // class Caches
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 158f785..f04ea6f 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -104,10 +104,10 @@
     int width = (int) glyph->mBitmapWidth;
     int height = (int) glyph->mBitmapHeight;
 
-    mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
-            nPenX + width, nPenY, 0, u2, v2,
-            nPenX + width, nPenY - height, 0, u2, v1,
-            nPenX, nPenY - height, 0, u1, v1);
+    mState->appendMeshQuad(nPenX, nPenY, u1, v2,
+            nPenX + width, nPenY, u2, v2,
+            nPenX + width, nPenY - height, u2, v1,
+            nPenX, nPenY - height, u1, v1);
 }
 
 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
@@ -325,8 +325,6 @@
     mTextTexture = NULL;
 
     mIndexBufferID = 0;
-    mPositionAttrSlot = -1;
-    mTexcoordAttrSlot = -1;
 
     mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
     mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
@@ -519,8 +517,8 @@
 
 // Avoid having to reallocate memory and render quad by quad
 void FontRenderer::initVertexArrayBuffers() {
-    uint32_t numIndicies = mMaxNumberOfQuads * 6;
-    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
+    uint32_t numIndices = mMaxNumberOfQuads * 6;
+    uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
     uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
 
     // Four verts, two triangles , six indices per quad
@@ -544,7 +542,7 @@
 
     free(indexBufferData);
 
-    uint32_t coordSize = 3;
+    uint32_t coordSize = 2;
     uint32_t uvSize = 2;
     uint32_t vertsPerQuad = 4;
     uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
@@ -579,12 +577,12 @@
     // Iterate over all the cache lines and see which ones need to be updated
     for (uint32_t i = 0; i < mCacheLines.size(); i++) {
         CacheTextureLine* cl = mCacheLines[i];
-        if(cl->mDirty) {
+        if (cl->mDirty) {
             uint32_t xOffset = 0;
             uint32_t yOffset = cl->mCurrentRow;
             uint32_t width   = mCacheWidth;
             uint32_t height  = cl->mMaxHeight;
-            void* textureData = mTextTexture + yOffset*width;
+            void* textureData = mTextTexture + yOffset * width;
 
             glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
                     GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
@@ -599,51 +597,43 @@
 void FontRenderer::issueDrawCommand() {
     checkTextureUpdate();
 
-    float* vtx = mTextMeshPtr;
-    float* tex = vtx + 3;
-
-    glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
-    glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
-
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
     glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
 
     mDrawn = true;
 }
 
-void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
-        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
-        float x4, float y4, float z4, float u4, float v4) {
+void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
+        float x2, float y2, float u2, float v2,
+        float x3, float y3, float u3, float v3,
+        float x4, float y4, float u4, float v4) {
+
     if (mClip &&
             (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
         return;
     }
 
     const uint32_t vertsPerQuad = 4;
-    const uint32_t floatsPerVert = 5;
+    const uint32_t floatsPerVert = 4;
     float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
 
     (*currentPos++) = x1;
     (*currentPos++) = y1;
-    (*currentPos++) = z1;
     (*currentPos++) = u1;
     (*currentPos++) = v1;
 
     (*currentPos++) = x2;
     (*currentPos++) = y2;
-    (*currentPos++) = z2;
     (*currentPos++) = u2;
     (*currentPos++) = v2;
 
     (*currentPos++) = x3;
     (*currentPos++) = y3;
-    (*currentPos++) = z3;
     (*currentPos++) = u3;
     (*currentPos++) = v3;
 
     (*currentPos++) = x4;
     (*currentPos++) = y4;
-    (*currentPos++) = z4;
     (*currentPos++) = u4;
     (*currentPos++) = v4;
 
@@ -762,11 +752,6 @@
         return false;
     }
 
-    if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
-        LOGE("Font renderer unable to draw, attribute slots undefined");
-        return false;
-    }
-
     mDrawn = false;
     mBounds = bounds;
     mClip = clip;
@@ -914,9 +899,12 @@
 void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
     float *gaussian = new float[2 * radius + 1];
     computeGaussianWeights(gaussian, radius);
+
     uint8_t* scratch = new uint8_t[width * height];
+
     horizontalBlur(gaussian, radius, image, scratch, width, height);
     verticalBlur(gaussian, radius, scratch, image, width, height);
+
     delete[] gaussian;
     delete[] scratch;
 }
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 1922812..f945873 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -178,9 +178,13 @@
         mGammaTable = gammaTable;
     }
 
-    void setAttributeBindingSlots(int positionSlot, int texCoordSlot) {
-        mPositionAttrSlot = positionSlot;
-        mTexcoordAttrSlot = texCoordSlot;
+    inline float* getMeshBuffer() {
+        checkInit();
+        return mTextMeshPtr;
+    }
+
+    inline int getMeshTexCoordsOffset() const {
+        return 2;
     }
 
     void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
@@ -279,9 +283,10 @@
     void precacheLatin(SkPaint* paint);
 
     void issueDrawCommand();
-    void appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, float y2,
-            float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
-            float x4, float y4, float z4, float u4, float v4);
+    void appendMeshQuad(float x1, float y1, float u1, float v1,
+            float x2, float y2, float u2, float v2,
+            float x3, float y3, float u3, float v3,
+            float x4, float y4, float u4, float v4);
 
     uint32_t mCacheWidth;
     uint32_t mCacheHeight;
@@ -308,9 +313,6 @@
 
     uint32_t mIndexBufferID;
 
-    int32_t mPositionAttrSlot;
-    int32_t mTexcoordAttrSlot;
-
     const Rect* mClip;
     Rect* mBounds;
     bool mDrawn;
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index aacf22a..a88a59a 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -171,8 +171,8 @@
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
             GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
 
-    texture->setFilter(GL_LINEAR, GL_LINEAR);
-    texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+    texture->setFilter(GL_LINEAR);
+    texture->setWrap(GL_CLAMP_TO_EDGE);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 5298125..324765b 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -108,8 +108,8 @@
 
         layer->generateTexture();
         layer->bindTexture();
-        layer->setFilter(GL_NEAREST, GL_NEAREST);
-        layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, false);
+        layer->setFilter(GL_NEAREST);
+        layer->setWrap(GL_CLAMP_TO_EDGE, false);
         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 
 #if DEBUG_LAYERS
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index e2d9ea3..c30923f 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -233,7 +233,6 @@
             layer->getTexture(), 0);
 
     glDisable(GL_SCISSOR_TEST);
-    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
     glClear(GL_COLOR_BUFFER_BIT);
     glEnable(GL_SCISSOR_TEST);
 
@@ -459,6 +458,8 @@
         }
 
 error:
+        glEnable(GL_SCISSOR_TEST);
+
 #if DEBUG_OPENGL
         if (error != GL_NO_ERROR) {
             LOGD("GL error while copying layer into bitmap = 0x%x", error);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1d7b99d..fcac053 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -133,8 +133,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::setViewport(int width, int height) {
-    glDisable(GL_DITHER);
-    glViewport(0, 0, width, height);
     mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
 
     mWidth = width;
@@ -143,7 +141,10 @@
     mFirstSnapshot->height = height;
     mFirstSnapshot->viewport.set(0, 0, width, height);
 
-    mDirtyClip = false;
+    glDisable(GL_DITHER);
+    glEnable(GL_SCISSOR_TEST);
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glEnableVertexAttribArray(Program::kBindingPosition);
 }
 
 void OpenGLRenderer::prepare(bool opaque) {
@@ -156,17 +157,15 @@
     mSnapshot = new Snapshot(mFirstSnapshot,
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     mSnapshot->fbo = getTargetFbo();
-
     mSaveCount = 1;
 
     glViewport(0, 0, mWidth, mHeight);
-
-    glEnable(GL_SCISSOR_TEST);
     glScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
+
     mSnapshot->setClip(left, top, right, bottom);
+    mDirtyClip = false;
 
     if (!opaque) {
-        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         glClear(GL_COLOR_BUFFER_BIT);
     }
 }
@@ -200,18 +199,18 @@
         }
     }
     mCaches.unbindMeshBuffer();
+    mCaches.resetVertexPointers();
 }
 
 void OpenGLRenderer::resume() {
     sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot;
 
     glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight());
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
     glEnable(GL_SCISSOR_TEST);
     dirtyClip();
 
-    glDisable(GL_DITHER);
-
     glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
@@ -556,7 +555,6 @@
     // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
     glScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
             clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
-    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
     glClear(GL_COLOR_BUFFER_BIT);
 
     dirtyClip();
@@ -911,10 +909,8 @@
         setupDrawProgram();
         setupDrawPureColorUniforms();
         setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true);
+        setupDrawVertices(&mesh[0].position[0]);
 
-        mCaches.unbindMeshBuffer();
-        glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
-                gVertexStride, &mesh[0].position[0]);
         glDrawArrays(GL_TRIANGLES, 0, count * 6);
 
         glEnable(GL_SCISSOR_TEST);
@@ -1205,16 +1201,15 @@
 }
 
 void OpenGLRenderer::setupDrawSimpleMesh() {
-    mCaches.bindMeshBuffer();
-    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gMeshStride, 0);
+    bool force = mCaches.bindMeshBuffer();
+    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, 0);
 }
 
 void OpenGLRenderer::setupDrawTexture(GLuint texture) {
     bindTexture(texture);
     glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++);
 
-    mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
+    mTexCoordsSlot = mCaches.currentProgram->texCoords;
     glEnableVertexAttribArray(mTexCoordsSlot);
 }
 
@@ -1222,7 +1217,7 @@
     bindExternalTexture(texture);
     glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++);
 
-    mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
+    mTexCoordsSlot = mCaches.currentProgram->texCoords;
     glEnableVertexAttribArray(mTexCoordsSlot);
 }
 
@@ -1236,22 +1231,23 @@
 }
 
 void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
+    bool force = false;
     if (!vertices) {
-        mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+        force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
     } else {
-        mCaches.unbindMeshBuffer();
+        force = mCaches.unbindMeshBuffer();
     }
-    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gMeshStride, vertices);
+
+    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices);
     if (mTexCoordsSlot >= 0) {
-        glVertexAttribPointer(mTexCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
+        mCaches.bindTexCoordsVertexPointer(force, mTexCoordsSlot, texCoords);
     }
 }
 
 void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
-    mCaches.unbindMeshBuffer();
-    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gVertexStride, vertices);
+    bool force = mCaches.unbindMeshBuffer();
+    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
+            vertices, gVertexStride);
 }
 
 /**
@@ -1267,17 +1263,22 @@
  */
 void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
         GLvoid* lengthCoords, float boundaryWidthProportion) {
-    mCaches.unbindMeshBuffer();
-    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gAAVertexStride, vertices);
+    bool force = mCaches.unbindMeshBuffer();
+    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position,
+            vertices, gAAVertexStride);
+    mCaches.resetTexCoordsVertexPointer();
+
     int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
     glEnableVertexAttribArray(widthSlot);
     glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
+
     int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
     glEnableVertexAttribArray(lengthSlot);
     glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
+
     int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
     glUniform1f(boundaryWidthSlot, boundaryWidthProportion);
+
     // Setting the inverse value saves computations per-fragment in the shader
     int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
     glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion));
@@ -2184,12 +2185,14 @@
 #else
     bool hasActiveLayer = false;
 #endif
-    mCaches.unbindMeshBuffer();
 
-    // Tell font renderer the locations of position and texture coord
-    // attributes so it can bind its data properly
-    int positionSlot = mCaches.currentProgram->position;
-    fontRenderer.setAttributeBindingSlots(positionSlot, mTexCoordsSlot);
+    float* buffer = fontRenderer.getMeshBuffer();
+    int offset = fontRenderer.getMeshTexCoordsOffset();
+
+    bool force = mCaches.unbindMeshBuffer();
+    mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, buffer);
+    mCaches.bindTexCoordsVertexPointer(force, mTexCoordsSlot, buffer + offset);
+
     if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y,
             hasActiveLayer ? &bounds : NULL)) {
 #if RENDER_LAYERS_AS_REGIONS
@@ -2203,7 +2206,7 @@
     }
 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-    glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
+    glDisableVertexAttribArray(mCaches.currentProgram->texCoords);
 
     drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
 }
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 47a2c99..27f530c 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -197,7 +197,8 @@
     }
 
     if (verticesCount > 0) {
-        Caches::getInstance().bindMeshBuffer(meshBuffer);
+        Caches& caches = Caches::getInstance();
+        caches.bindMeshBuffer(meshBuffer);
         if (!mUploaded) {
             glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount,
                     mVertices, GL_DYNAMIC_DRAW);
@@ -206,6 +207,7 @@
             glBufferSubData(GL_ARRAY_BUFFER, 0,
                     sizeof(TextureVertex) * verticesCount, mVertices);
         }
+        caches.resetVertexPointers();
     }
 
     PATCH_LOGD("    patch: new vertices count = %d", verticesCount);
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 972dd87..cbea843 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -25,80 +25,106 @@
 // Base program
 ///////////////////////////////////////////////////////////////////////////////
 
-Program::Program(const char* vertex, const char* fragment) {
+// TODO: Program instance should be created from a factory method
+Program::Program(const ProgramDescription& description, const char* vertex, const char* fragment) {
     mInitialized = false;
+    mHasColorUniform = false;
+    mUse = false;
 
-    vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
-    if (vertexShader) {
+    // No need to cache compiled shaders, rely instead on Android's
+    // persistent shaders cache
+    mVertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+    if (mVertexShader) {
+        mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+        if (mFragmentShader) {
+            mProgramId = glCreateProgram();
 
-        fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
-        if (fragmentShader) {
+            glAttachShader(mProgramId, mVertexShader);
+            glAttachShader(mProgramId, mFragmentShader);
 
-            id = glCreateProgram();
-            glAttachShader(id, vertexShader);
-            glAttachShader(id, fragmentShader);
-            glLinkProgram(id);
+            position = bindAttrib("position", kBindingPosition);
+            if (description.hasTexture || description.hasExternalTexture) {
+                texCoords = bindAttrib("texCoords", kBindingTexCoords);
+            } else {
+                texCoords = -1;
+            }
+
+            glLinkProgram(mProgramId);
 
             GLint status;
-            glGetProgramiv(id, GL_LINK_STATUS, &status);
+            glGetProgramiv(mProgramId, GL_LINK_STATUS, &status);
             if (status != GL_TRUE) {
                 LOGE("Error while linking shaders:");
                 GLint infoLen = 0;
-                glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infoLen);
+                glGetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &infoLen);
                 if (infoLen > 1) {
                     GLchar log[infoLen];
-                    glGetProgramInfoLog(id, infoLen, 0, &log[0]);
+                    glGetProgramInfoLog(mProgramId, infoLen, 0, &log[0]);
                     LOGE("%s", log);
                 }
-                glDeleteShader(vertexShader);
-                glDeleteShader(fragmentShader);
-                glDeleteProgram(id);
+
+                glDetachShader(mProgramId, mVertexShader);
+                glDetachShader(mProgramId, mFragmentShader);
+
+                glDeleteShader(mVertexShader);
+                glDeleteShader(mFragmentShader);
+
+                glDeleteProgram(mProgramId);
             } else {
                 mInitialized = true;
             }
+        } else {
+            glDeleteShader(mVertexShader);
         }
     }
 
-    mUse = false;
-
     if (mInitialized) {
-        position = addAttrib("position");
         transform = addUniform("transform");
     }
 }
 
 Program::~Program() {
     if (mInitialized) {
-        glDeleteShader(vertexShader);
-        glDeleteShader(fragmentShader);
-        glDeleteProgram(id);
+        glDetachShader(mProgramId, mVertexShader);
+        glDetachShader(mProgramId, mFragmentShader);
+
+        glDeleteShader(mVertexShader);
+        glDeleteShader(mFragmentShader);
+
+        glDeleteProgram(mProgramId);
     }
 }
 
 int Program::addAttrib(const char* name) {
-    int slot = glGetAttribLocation(id, name);
-    attributes.add(name, slot);
+    int slot = glGetAttribLocation(mProgramId, name);
+    mAttributes.add(name, slot);
     return slot;
 }
 
+int Program::bindAttrib(const char* name, ShaderBindings bindingSlot) {
+    glBindAttribLocation(mProgramId, bindingSlot, name);
+    mAttributes.add(name, bindingSlot);
+    return bindingSlot;
+}
+
 int Program::getAttrib(const char* name) {
-    ssize_t index = attributes.indexOfKey(name);
+    ssize_t index = mAttributes.indexOfKey(name);
     if (index >= 0) {
-        return attributes.valueAt(index);
+        return mAttributes.valueAt(index);
     }
     return addAttrib(name);
 }
 
 int Program::addUniform(const char* name) {
-    int slot = glGetUniformLocation(id, name);
-    uniforms.add(name, slot);
+    int slot = glGetUniformLocation(mProgramId, name);
+    mUniforms.add(name, slot);
     return slot;
 }
 
 int Program::getUniform(const char* name) {
-    ssize_t index = uniforms.indexOfKey(name);
+    ssize_t index = mUniforms.indexOfKey(name);
     if (index >= 0) {
-        return uniforms.valueAt(index);
+        return mUniforms.valueAt(index);
     }
     return addUniform(name);
 }
@@ -127,10 +153,11 @@
         const mat4& transformMatrix, bool offset) {
     mat4 t(projectionMatrix);
     if (offset) {
-        // offset screenspace xy by an amount that compensates for typical precision issues
-        // in GPU hardware that tends to paint hor/vert lines in pixels shifted up and to the left.
-        // This offset value is based on an assumption that some hardware may use as little
-        // as 12.4 precision, so we offset by slightly more than 1/16.
+        // offset screenspace xy by an amount that compensates for typical precision
+        // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted
+        // up and to the left.
+        // This offset value is based on an assumption that some hardware may use as
+        // little as 12.4 precision, so we offset by slightly more than 1/16.
         t.translate(.375, .375, 0);
     }
     t.multiply(transformMatrix);
@@ -140,20 +167,20 @@
 }
 
 void Program::setColor(const float r, const float g, const float b, const float a) {
-    glUniform4f(getUniform("color"), r, g, b, a);
+    if (!mHasColorUniform) {
+        mColorUniform = getUniform("color");
+        mHasColorUniform = true;
+    }
+    glUniform4f(mColorUniform, r, g, b, a);
 }
 
 void Program::use() {
-    glUseProgram(id);
+    glUseProgram(mProgramId);
     mUse = true;
-
-    glEnableVertexAttribArray(position);
 }
 
 void Program::remove() {
     mUse = false;
-
-    glDisableVertexAttribArray(position);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 764cb05..559c717 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -17,27 +17,280 @@
 #ifndef ANDROID_HWUI_PROGRAM_H
 #define ANDROID_HWUI_PROGRAM_H
 
+#include <utils/KeyedVector.h>
+
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
-#include <utils/KeyedVector.h>
+#include <SkXfermode.h>
 
 #include "Matrix.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
 
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_PROGRAMS
+    #define PROGRAM_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define PROGRAM_LOGD(...)
+#endif
+
+#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH))
+#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH)
+
+#define PROGRAM_KEY_TEXTURE 0x1
+#define PROGRAM_KEY_A8_TEXTURE 0x2
+#define PROGRAM_KEY_BITMAP 0x4
+#define PROGRAM_KEY_GRADIENT 0x8
+#define PROGRAM_KEY_BITMAP_FIRST 0x10
+#define PROGRAM_KEY_COLOR_MATRIX 0x20
+#define PROGRAM_KEY_COLOR_LIGHTING 0x40
+#define PROGRAM_KEY_COLOR_BLEND 0x80
+#define PROGRAM_KEY_BITMAP_NPOT 0x100
+#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
+
+#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
+#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
+
+// Encode the xfermodes on 6 bits
+#define PROGRAM_MAX_XFERMODE 0x1f
+#define PROGRAM_XFERMODE_SHADER_SHIFT 26
+#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
+#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14
+
+#define PROGRAM_BITMAP_WRAPS_SHIFT 9
+#define PROGRAM_BITMAP_WRAPT_SHIFT 11
+
+#define PROGRAM_GRADIENT_TYPE_SHIFT 33
+#define PROGRAM_MODULATE_SHIFT 35
+
+#define PROGRAM_IS_POINT_SHIFT 36
+
+#define PROGRAM_HAS_AA_SHIFT 37
+
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
+
+///////////////////////////////////////////////////////////////////////////////
+// Types
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint64_t programid;
+
+///////////////////////////////////////////////////////////////////////////////
+// Program description
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Describe the features required for a given program. The features
+ * determine the generation of both the vertex and fragment shaders.
+ * A ProgramDescription must be used in conjunction with a ProgramCache.
+ */
+struct ProgramDescription {
+    enum ColorModifier {
+        kColorNone,
+        kColorMatrix,
+        kColorLighting,
+        kColorBlend
+    };
+
+    enum Gradient {
+        kGradientLinear,
+        kGradientCircular,
+        kGradientSweep
+    };
+
+    ProgramDescription() {
+        reset();
+    }
+
+    // Texturing
+    bool hasTexture;
+    bool hasAlpha8Texture;
+    bool hasExternalTexture;
+    bool hasTextureTransform;
+
+    // Modulate, this should only be set when setColor() return true
+    bool modulate;
+
+    // Shaders
+    bool hasBitmap;
+    bool isBitmapNpot;
+
+    bool isAA;
+
+    bool hasGradient;
+    Gradient gradientType;
+
+    SkXfermode::Mode shadersMode;
+
+    bool isBitmapFirst;
+    GLenum bitmapWrapS;
+    GLenum bitmapWrapT;
+
+    // Color operations
+    ColorModifier colorOp;
+    SkXfermode::Mode colorMode;
+
+    // Framebuffer blending (requires Extensions.hasFramebufferFetch())
+    // Ignored for all values < SkXfermode::kPlus_Mode
+    SkXfermode::Mode framebufferMode;
+    bool swapSrcDst;
+
+    bool isPoint;
+    float pointSize;
+
+    /**
+     * Resets this description. All fields are reset back to the default
+     * values they hold after building a new instance.
+     */
+    void reset() {
+        hasTexture = false;
+        hasAlpha8Texture = false;
+        hasExternalTexture = false;
+        hasTextureTransform = false;
+
+        isAA = false;
+
+        modulate = false;
+
+        hasBitmap = false;
+        isBitmapNpot = false;
+
+        hasGradient = false;
+        gradientType = kGradientLinear;
+
+        shadersMode = SkXfermode::kClear_Mode;
+
+        isBitmapFirst = false;
+        bitmapWrapS = GL_CLAMP_TO_EDGE;
+        bitmapWrapT = GL_CLAMP_TO_EDGE;
+
+        colorOp = kColorNone;
+        colorMode = SkXfermode::kClear_Mode;
+
+        framebufferMode = SkXfermode::kClear_Mode;
+        swapSrcDst = false;
+
+        isPoint = false;
+        pointSize = 0.0f;
+    }
+
+    /**
+     * Indicates, for a given color, whether color modulation is required in
+     * the fragment shader. When this method returns true, the program should
+     * be provided with a modulation color.
+     */
+    bool setColor(const float r, const float g, const float b, const float a) {
+        modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD ||
+                g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD;
+        return modulate;
+    }
+
+    /**
+     * Indicates, for a given color, whether color modulation is required in
+     * the fragment shader. When this method returns true, the program should
+     * be provided with a modulation color.
+     */
+    bool setAlpha8Color(const float r, const float g, const float b, const float a) {
+        modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD ||
+                g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD;
+        return modulate;
+    }
+
+    /**
+     * Computes the unique key identifying this program.
+     */
+    programid key() const {
+        programid key = 0;
+        if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
+        if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
+        if (hasBitmap) {
+            key |= PROGRAM_KEY_BITMAP;
+            if (isBitmapNpot) {
+                key |= PROGRAM_KEY_BITMAP_NPOT;
+                key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
+                key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
+            }
+        }
+        if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
+        key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
+        if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
+        if (hasBitmap && hasGradient) {
+            key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
+        }
+        switch (colorOp) {
+            case kColorMatrix:
+                key |= PROGRAM_KEY_COLOR_MATRIX;
+                break;
+            case kColorLighting:
+                key |= PROGRAM_KEY_COLOR_LIGHTING;
+                break;
+            case kColorBlend:
+                key |= PROGRAM_KEY_COLOR_BLEND;
+                key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
+                break;
+            case kColorNone:
+                break;
+        }
+        key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
+        if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
+        if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
+        if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
+        if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
+        if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
+        if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
+        return key;
+    }
+
+    /**
+     * Logs the specified message followed by the key identifying this program.
+     */
+    void log(const char* message) const {
+#if DEBUG_PROGRAMS
+        programid k = key();
+        PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32),
+                uint32_t(k & 0xffffffff));
+#endif
+    }
+
+private:
+    inline uint32_t getEnumForWrap(GLenum wrap) const {
+        switch (wrap) {
+            case GL_CLAMP_TO_EDGE:
+                return 0;
+            case GL_REPEAT:
+                return 1;
+            case GL_MIRRORED_REPEAT:
+                return 2;
+        }
+        return 0;
+    }
+
+}; // struct ProgramDescription
+
 /**
  * A program holds a vertex and a fragment shader. It offers several utility
  * methods to query attributes and uniforms.
  */
 class Program {
 public:
+    enum ShaderBindings {
+        kBindingPosition,
+        kBindingTexCoords
+    };
+
     /**
      * Creates a new program with the specified vertex and fragment
      * shaders sources.
      */
-    Program(const char* vertex, const char* fragment);
+    Program(const ProgramDescription& description, const char* vertex, const char* fragment);
     virtual ~Program();
 
     /**
@@ -94,6 +347,11 @@
     int position;
 
     /**
+     * Name of the texCoords attribute if it exists, -1 otherwise.
+     */
+    int texCoords;
+
+    /**
      * Name of the transform uniform.
      */
     int transform;
@@ -107,6 +365,11 @@
     int addAttrib(const char* name);
 
     /**
+     * Binds the specified attribute name to the specified slot.
+     */
+    int bindAttrib(const char* name, ShaderBindings bindingSlot);
+
+    /**
      * Adds a uniform with the specified name.
      *
      * @return The OpenGL name of the uniform.
@@ -121,19 +384,20 @@
      */
     GLuint buildShader(const char* source, GLenum type);
 
-    // Name of the OpenGL program
-    GLuint id;
-
-    // Name of the shaders
-    GLuint vertexShader;
-    GLuint fragmentShader;
+    // Name of the OpenGL program and shaders
+    GLuint mProgramId;
+    GLuint mVertexShader;
+    GLuint mFragmentShader;
 
     // Keeps track of attributes and uniforms slots
-    KeyedVector<const char*, int> attributes;
-    KeyedVector<const char*, int> uniforms;
+    KeyedVector<const char*, int> mAttributes;
+    KeyedVector<const char*, int> mUniforms;
 
     bool mUse;
     bool mInitialized;
+
+    bool mHasColorUniform;
+    int mColorUniform;
 }; // class Program
 
 }; // namespace uirenderer
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index c2383f4..a7f1277 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -388,7 +388,7 @@
     String8 vertexShader = generateVertexShader(description);
     String8 fragmentShader = generateFragmentShader(description);
 
-    Program* program = new Program(vertexShader.string(), fragmentShader.string());
+    Program* program = new Program(description, vertexShader.string(), fragmentShader.string());
     return program;
 }
 
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 5c7197b..e3ed79e 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -23,10 +23,9 @@
 
 #include <GLES2/gl2.h>
 
-#include <SkXfermode.h>
-
 #include "Debug.h"
 #include "Program.h"
+#include "Properties.h"
 
 namespace android {
 namespace uirenderer {
@@ -42,243 +41,11 @@
     #define PROGRAM_LOGD(...)
 #endif
 
-// TODO: This should be set in properties
-#define PANEL_BIT_DEPTH 20
-#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH))
-#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH)
-
-#define PROGRAM_KEY_TEXTURE 0x1
-#define PROGRAM_KEY_A8_TEXTURE 0x2
-#define PROGRAM_KEY_BITMAP 0x4
-#define PROGRAM_KEY_GRADIENT 0x8
-#define PROGRAM_KEY_BITMAP_FIRST 0x10
-#define PROGRAM_KEY_COLOR_MATRIX 0x20
-#define PROGRAM_KEY_COLOR_LIGHTING 0x40
-#define PROGRAM_KEY_COLOR_BLEND 0x80
-#define PROGRAM_KEY_BITMAP_NPOT 0x100
-#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
-
-#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
-#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
-
-// Encode the xfermodes on 6 bits
-#define PROGRAM_MAX_XFERMODE 0x1f
-#define PROGRAM_XFERMODE_SHADER_SHIFT 26
-#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
-#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14
-
-#define PROGRAM_BITMAP_WRAPS_SHIFT 9
-#define PROGRAM_BITMAP_WRAPT_SHIFT 11
-
-#define PROGRAM_GRADIENT_TYPE_SHIFT 33
-#define PROGRAM_MODULATE_SHIFT 35
-
-#define PROGRAM_IS_POINT_SHIFT 36
-
-#define PROGRAM_HAS_AA_SHIFT 37
-
-#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
-#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
-
-///////////////////////////////////////////////////////////////////////////////
-// Types
-///////////////////////////////////////////////////////////////////////////////
-
-typedef uint64_t programid;
-
 ///////////////////////////////////////////////////////////////////////////////
 // Cache
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
- * Describe the features required for a given program. The features
- * determine the generation of both the vertex and fragment shaders.
- * A ProgramDescription must be used in conjunction with a ProgramCache.
- */
-struct ProgramDescription {
-    enum ColorModifier {
-        kColorNone,
-        kColorMatrix,
-        kColorLighting,
-        kColorBlend
-    };
-
-    enum Gradient {
-        kGradientLinear,
-        kGradientCircular,
-        kGradientSweep
-    };
-
-    ProgramDescription() {
-        reset();
-    }
-
-    // Texturing
-    bool hasTexture;
-    bool hasAlpha8Texture;
-    bool hasExternalTexture;
-    bool hasTextureTransform;
-
-    // Modulate, this should only be set when setColor() return true
-    bool modulate;
-
-    // Shaders
-    bool hasBitmap;
-    bool isBitmapNpot;
-
-    bool isAA;
-
-    bool hasGradient;
-    Gradient gradientType;
-
-    SkXfermode::Mode shadersMode;
-
-    bool isBitmapFirst;
-    GLenum bitmapWrapS;
-    GLenum bitmapWrapT;
-
-    // Color operations
-    ColorModifier colorOp;
-    SkXfermode::Mode colorMode;
-
-    // Framebuffer blending (requires Extensions.hasFramebufferFetch())
-    // Ignored for all values < SkXfermode::kPlus_Mode
-    SkXfermode::Mode framebufferMode;
-    bool swapSrcDst;
-
-    bool isPoint;
-    float pointSize;
-
-    /**
-     * Resets this description. All fields are reset back to the default
-     * values they hold after building a new instance.
-     */
-    void reset() {
-        hasTexture = false;
-        hasAlpha8Texture = false;
-        hasExternalTexture = false;
-        hasTextureTransform = false;
-
-        isAA = false;
-
-        modulate = false;
-
-        hasBitmap = false;
-        isBitmapNpot = false;
-
-        hasGradient = false;
-        gradientType = kGradientLinear;
-
-        shadersMode = SkXfermode::kClear_Mode;
-
-        isBitmapFirst = false;
-        bitmapWrapS = GL_CLAMP_TO_EDGE;
-        bitmapWrapT = GL_CLAMP_TO_EDGE;
-
-        colorOp = kColorNone;
-        colorMode = SkXfermode::kClear_Mode;
-
-        framebufferMode = SkXfermode::kClear_Mode;
-        swapSrcDst = false;
-
-        isPoint = false;
-        pointSize = 0.0f;
-    }
-
-    /**
-     * Indicates, for a given color, whether color modulation is required in
-     * the fragment shader. When this method returns true, the program should
-     * be provided with a modulation color.
-     */
-    bool setColor(const float r, const float g, const float b, const float a) {
-        modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD ||
-                g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD;
-        return modulate;
-    }
-
-    /**
-     * Indicates, for a given color, whether color modulation is required in
-     * the fragment shader. When this method returns true, the program should
-     * be provided with a modulation color.
-     */
-    bool setAlpha8Color(const float r, const float g, const float b, const float a) {
-        modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD ||
-                g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD;
-        return modulate;
-    }
-
-    /**
-     * Computes the unique key identifying this program.
-     */
-    programid key() const {
-        programid key = 0;
-        if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
-        if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
-        if (hasBitmap) {
-            key |= PROGRAM_KEY_BITMAP;
-            if (isBitmapNpot) {
-                key |= PROGRAM_KEY_BITMAP_NPOT;
-                key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
-                key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
-            }
-        }
-        if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
-        key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
-        if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
-        if (hasBitmap && hasGradient) {
-            key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
-        }
-        switch (colorOp) {
-            case kColorMatrix:
-                key |= PROGRAM_KEY_COLOR_MATRIX;
-                break;
-            case kColorLighting:
-                key |= PROGRAM_KEY_COLOR_LIGHTING;
-                break;
-            case kColorBlend:
-                key |= PROGRAM_KEY_COLOR_BLEND;
-                key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
-                break;
-            case kColorNone:
-                break;
-        }
-        key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
-        if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
-        if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
-        if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
-        if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
-        if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
-        if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
-        return key;
-    }
-
-    /**
-     * Logs the specified message followed by the key identifying this program.
-     */
-    void log(const char* message) const {
-#if DEBUG_PROGRAMS
-        programid k = key();
-        PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32),
-                uint32_t(k & 0xffffffff));
-#endif
-    }
-
-private:
-    inline uint32_t getEnumForWrap(GLenum wrap) const {
-        switch (wrap) {
-            case GL_CLAMP_TO_EDGE:
-                return 0;
-            case GL_REPEAT:
-                return 1;
-            case GL_MIRRORED_REPEAT:
-                return 2;
-        }
-        return 0;
-    }
-
-}; // struct ProgramDescription
-
-/**
  * Generates and caches program. Programs are generated based on
  * ProgramDescriptions.
  */
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 8c01e3a..2eae0f1 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -73,6 +73,9 @@
 #define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold"
 #define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold"
 
+// TODO: This should be set by a system property
+#define PANEL_BIT_DEPTH 20
+
 // Converts a number of mega-bytes into bytes
 #define MB(s) s * 1024 * 1024
 
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index a3ee63b..bbefec6 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -137,8 +137,8 @@
         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
                 GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image);
 
-        texture->setFilter(GL_LINEAR, GL_LINEAR);
-        texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+        texture->setFilter(GL_LINEAR);
+        texture->setWrap(GL_CLAMP_TO_EDGE);
 
         if (size < mMaxSize) {
             if (mDebugEnabled) {
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index a4aed07..1adf2c7 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -49,7 +49,7 @@
             GLenum renderTarget = GL_TEXTURE_2D) {
 
         if (firstWrap || force || wrapS != this->wrapS || wrapT != this->wrapT) {
-            firstWrap = true;
+            firstWrap = false;
 
             this->wrapS = wrapS;
             this->wrapT = wrapT;
diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java
index 939bd4a..7c1485d 100755
--- a/location/java/android/location/Country.java
+++ b/location/java/android/location/Country.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 
 import java.util.Locale;
 
@@ -58,8 +59,14 @@
     private final int mSource;
 
     private int mHashCode;
+
     /**
-     *
+     * Time that this object was created (which we assume to be the time that the source was
+     * consulted). This time is in milliseconds since boot up.
+     */
+    private final long mTimestamp;
+
+    /**
      * @param countryIso the ISO 3166-1 two letters country code.
      * @param source where the countryIso came from, could be one of below
      *        values
@@ -78,11 +85,23 @@
         }
         mCountryIso = countryIso.toUpperCase(Locale.US);
         mSource = source;
+        mTimestamp = SystemClock.elapsedRealtime();
+    }
+
+    private Country(final String countryIso, final int source, long timestamp) {
+        if (countryIso == null || source < COUNTRY_SOURCE_NETWORK
+                || source > COUNTRY_SOURCE_LOCALE) {
+            throw new IllegalArgumentException();
+        }
+        mCountryIso = countryIso.toUpperCase(Locale.US);
+        mSource = source;
+        mTimestamp = timestamp;
     }
 
     public Country(Country country) {
         mCountryIso = country.mCountryIso;
         mSource = country.mSource;
+        mTimestamp = country.mTimestamp;
     }
 
     /**
@@ -106,9 +125,17 @@
         return mSource;
     }
 
+    /**
+     * Returns the time that this object was created (which we assume to be the time that the source
+     * was consulted).
+     */
+    public final long getTimestamp() {
+        return mTimestamp;
+    }
+
     public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
         public Country createFromParcel(Parcel in) {
-            return new Country(in.readString(), in.readInt());
+            return new Country(in.readString(), in.readInt(), in.readLong());
         }
 
         public Country[] newArray(int size) {
@@ -123,8 +150,14 @@
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mCountryIso);
         parcel.writeInt(mSource);
+        parcel.writeLong(mTimestamp);
     }
 
+    /**
+     * Returns true if this {@link Country} is equivalent to the given object. This ignores
+     * the timestamp value and just checks for equivalence of countryIso and source values.
+     * Returns false otherwise.
+     */
     @Override
     public boolean equals(Object object) {
         if (object == this) {
@@ -132,6 +165,7 @@
         }
         if (object instanceof Country) {
             Country c = (Country) object;
+            // No need to check the equivalence of the timestamp
             return mCountryIso.equals(c.getCountryIso()) && mSource == c.getSource();
         }
         return false;
@@ -150,8 +184,8 @@
     }
 
     /**
-     * Compare the specified country to this country object ignoring the mSource
-     * field, return true if the countryIso fields are equal
+     * Compare the specified country to this country object ignoring the source
+     * and timestamp fields, return true if the countryIso fields are equal
      *
      * @param country the country to compare
      * @return true if the specified country's countryIso field is equal to this
@@ -160,4 +194,9 @@
     public boolean equalsIgnoreSource(Country country) {
         return country != null && mCountryIso.equals(country.getCountryIso());
     }
+
+    @Override
+    public String toString() {
+        return "Country {ISO=" + mCountryIso + ", source=" + mSource + ", time=" + mTimestamp + "}";
+    }
 }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8405264..42f18d3 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -335,11 +335,13 @@
         return UNKNOWN_ERROR;
     }
 
-    dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
-    if (mDecryptHandle != NULL) {
-        CHECK(mDrmManagerClient);
-        if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
-            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
+    if (extractor->getDrmFlag()) {
+        dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
+        if (mDecryptHandle != NULL) {
+            CHECK(mDrmManagerClient);
+            if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
+                notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
+            }
         }
     }
 
@@ -2103,6 +2105,7 @@
 
         mWVMExtractor = new WVMExtractor(dataSource);
         mWVMExtractor->setAdaptiveStreamingMode(true);
+        mWVMExtractor->setDrmFlag(true);
         extractor = mWVMExtractor;
     } else {
         extractor = MediaExtractor::Create(
@@ -2113,12 +2116,14 @@
         }
     }
 
-    dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
+    if (extractor->getDrmFlag()) {
+        dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
 
-    if (mDecryptHandle != NULL) {
-        CHECK(mDrmManagerClient);
-        if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
-            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
+        if (mDecryptHandle != NULL) {
+            CHECK(mDrmManagerClient);
+            if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
+                notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
+            }
         }
     }
 
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 2171492..d65dc51 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -114,6 +114,9 @@
         ret = new AVIExtractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {
         ret = new WVMExtractor(source);
+        if (ret != NULL) {
+            isDrm = true;
+        }
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
         ret = new AACExtractor(source, meta);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_on.png
index 6208581..02da243 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_on.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_on.png
index fe4d318..d645a3c 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_on.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png
index 2536d92..9c117ae 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png
index 44e3577..4f51201 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_rotate_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_rotate_on.png
index b375396..35d85e1 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_rotate_on.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_rotate_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_wifi_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_wifi_on.png
index 54e3d1e..bc1628f 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_wifi_on.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_wifi_on.png
Binary files differ
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 088146b..e3c0601 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -1306,8 +1306,11 @@
             }
 
             if (mFaceLockAreaView != null) {
+                int[] faceLockPosition;
+                faceLockPosition = new int[2];
+                mFaceLockAreaView.getLocationInWindow(faceLockPosition);
                 startFaceLock(mFaceLockAreaView.getWindowToken(),
-                        mFaceLockAreaView.getLeft(), mFaceLockAreaView.getTop(),
+                        faceLockPosition[0], faceLockPosition[1],
                         mFaceLockAreaView.getWidth(), mFaceLockAreaView.getHeight());
             }
         }
@@ -1325,14 +1328,14 @@
     };
 
     // Tells the FaceLock service to start displaying its UI and perform recognition
-    public void startFaceLock(IBinder windowToken, int x, int y, int h, int w)
+    public void startFaceLock(IBinder windowToken, int x, int y, int w, int h)
     {
         if (usingFaceLock()) {
             synchronized (mFaceLockServiceRunningLock) {
                 if (!mFaceLockServiceRunning) {
                     if (DEBUG) Log.d(TAG, "Starting FaceLock");
                     try {
-                        mFaceLockService.startUi(windowToken, x, y, h, w);
+                        mFaceLockService.startUi(windowToken, x, y, w, h);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Caught exception starting FaceLock: " + e.toString());
                         return;
diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
index 3081ebe..ab61a3c 100644
--- a/services/java/com/android/server/CountryDetectorService.java
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.HashMap;
 
 import com.android.server.location.ComprehensiveCountryDetector;
@@ -30,6 +32,8 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
 import android.util.Slog;
 
 /**
@@ -75,7 +79,7 @@
         }
     }
 
-    private final static String TAG = "CountryDetectorService";
+    private final static String TAG = "CountryDetector";
 
     private final HashMap<IBinder, Receiver> mReceivers;
     private final Context mContext;
@@ -201,4 +205,20 @@
     boolean isSystemReady() {
         return mSystemReady;
     }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        try {
+            final Printer p = new PrintWriterPrinter(fout);
+            p.println("CountryDetectorService state:");
+            p.println("  Number of listeners=" + mReceivers.keySet().size());
+            if (mCountryDetector == null) {
+                p.println("  ComprehensiveCountryDetector not initialized");
+            } else {
+                p.println("  " + mCountryDetector.toString());
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to dump CountryDetectorService: ", e);
+        }
+    }
 }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index cb291cef..016dc82 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -152,8 +152,12 @@
     /* Wifi disabled due to airplane mode on */
     private static final int WIFI_DISABLED_AIRPLANE_ON      = 3;
 
-    private AtomicInteger mWifiState = new AtomicInteger(WIFI_DISABLED);
+    /* Persisted state that tracks the wifi & airplane interaction from settings */
+    private AtomicInteger mPersistWifiState = new AtomicInteger(WIFI_DISABLED);
+    /* Tracks current airplane mode state */
     private AtomicBoolean mAirplaneModeOn = new AtomicBoolean(false);
+    /* Tracks whether wifi is enabled from WifiStateMachine's perspective */
+    private boolean mWifiEnabled;
 
     private boolean mIsReceiverRegistered = false;
 
@@ -373,8 +377,8 @@
                         mAirplaneModeOn.set(isAirplaneModeOn());
                         /* On airplane mode disable, restore wifi state if necessary */
                         if (!mAirplaneModeOn.get() && (testAndClearWifiSavedState() ||
-                            mWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE)) {
-                                persistWifiEnabled(true);
+                            mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE)) {
+                                persistWifiState(true);
                         }
                         updateWifiState();
                     }
@@ -391,7 +395,12 @@
                     @Override
                     public void onReceive(Context context, Intent intent) {
                         if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                            // reset & clear notification on any wifi state change
+                            int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                                    WifiManager.WIFI_STATE_DISABLED);
+
+                            mWifiEnabled = (wifiState == WifiManager.WIFI_STATE_ENABLED);
+
+                           // reset & clear notification on any wifi state change
                             resetNotification();
                         } else if (intent.getAction().equals(
                                 WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
@@ -435,7 +444,7 @@
      */
     public void checkAndStartWifi() {
         mAirplaneModeOn.set(isAirplaneModeOn());
-        mWifiState.set(getPersistedWifiState());
+        mPersistWifiState.set(getPersistedWifiState());
         /* Start if Wi-Fi should be enabled or the saved state indicates Wi-Fi was on */
         boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState();
         Slog.i(TAG, "WifiService starting up with Wi-Fi " +
@@ -472,29 +481,30 @@
 
     private boolean shouldWifiBeEnabled() {
         if (mAirplaneModeOn.get()) {
-            return mWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE;
+            return mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE;
         } else {
-            return mWifiState.get() != WIFI_DISABLED;
+            return mPersistWifiState.get() != WIFI_DISABLED;
         }
     }
 
-    private void persistWifiEnabled(boolean enabled) {
+    private void persistWifiState(boolean enabled) {
         final ContentResolver cr = mContext.getContentResolver();
         boolean airplane = mAirplaneModeOn.get() && isAirplaneToggleable();
         if (enabled) {
             if (airplane) {
-                mWifiState.set(WIFI_ENABLED_AIRPLANE_OVERRIDE);
+                mPersistWifiState.set(WIFI_ENABLED_AIRPLANE_OVERRIDE);
             } else {
-                mWifiState.set(WIFI_ENABLED);
+                mPersistWifiState.set(WIFI_ENABLED);
             }
         } else {
             if (airplane) {
-                mWifiState.set(WIFI_DISABLED_AIRPLANE_ON);
+                mPersistWifiState.set(WIFI_DISABLED_AIRPLANE_ON);
             } else {
-                mWifiState.set(WIFI_DISABLED);
+                mPersistWifiState.set(WIFI_DISABLED);
             }
         }
-        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, mWifiState.get());
+
+        Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, mPersistWifiState.get());
     }
 
 
@@ -545,7 +555,6 @@
      */
     public synchronized boolean setWifiEnabled(boolean enable) {
         enforceChangePermission();
-
         if (DBG) {
             Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
         }
@@ -559,16 +568,20 @@
          * Caller might not have WRITE_SECURE_SETTINGS,
          * only CHANGE_WIFI_STATE is enforced
          */
-        long ident = Binder.clearCallingIdentity();
-        persistWifiEnabled(enable);
-        Binder.restoreCallingIdentity(ident);
+
+        /* Avoids overriding of airplane state when wifi is already in the expected state */
+        if (enable != mWifiEnabled) {
+            long ident = Binder.clearCallingIdentity();
+            persistWifiState(enable);
+            Binder.restoreCallingIdentity(ident);
+        }
 
         if (enable) {
             if (!mIsReceiverRegistered) {
                 registerForBroadcasts();
                 mIsReceiverRegistered = true;
             }
-        } else if (mIsReceiverRegistered){
+        } else if (mIsReceiverRegistered) {
             mContext.unregisterReceiver(mReceiver);
             mIsReceiverRegistered = false;
         }
@@ -663,7 +676,12 @@
      */
     public List<WifiConfiguration> getConfiguredNetworks() {
         enforceAccessPermission();
-        return mWifiStateMachine.syncGetConfiguredNetworks();
+        if (mWifiStateMachineChannel != null) {
+            return mWifiStateMachine.syncGetConfiguredNetworks(mWifiStateMachineChannel);
+        } else {
+            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+            return null;
+        }
     }
 
     /**
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index b1551a6..bc8ce7d 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -118,7 +118,7 @@
         "192.168.48.2", "192.168.48.254",
     };
 
-    private String[] mDnsServers;
+    private String[] mDefaultDnsServers;
     private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
     private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
 
@@ -171,9 +171,9 @@
         updateConfiguration();
 
         // TODO - remove and rely on real notifications of the current iface
-        mDnsServers = new String[2];
-        mDnsServers[0] = DNS_DEFAULT_SERVER1;
-        mDnsServers[1] = DNS_DEFAULT_SERVER2;
+        mDefaultDnsServers = new String[2];
+        mDefaultDnsServers[0] = DNS_DEFAULT_SERVER1;
+        mDefaultDnsServers[1] = DNS_DEFAULT_SERVER2;
     }
 
     void updateConfiguration() {
@@ -1244,7 +1244,7 @@
                     }
                 }
                 try {
-                    mNMService.setDnsForwarders(mDnsServers);
+                    mNMService.setDnsForwarders(mDefaultDnsServers);
                 } catch (Exception e) {
                     transitionTo(mSetDnsForwardersErrorState);
                     return false;
@@ -1320,7 +1320,19 @@
                     try {
                         linkProperties = mConnService.getLinkProperties(upType);
                     } catch (RemoteException e) { }
-                    if (linkProperties != null) iface = linkProperties.getInterfaceName();
+                    if (linkProperties != null) {
+                        iface = linkProperties.getInterfaceName();
+                        String[] dnsServers = mDefaultDnsServers;
+                        Collection<InetAddress> dnses = linkProperties.getDnses();
+                        if (dnses != null) {
+                            dnsServers = NetworkUtils.makeStrings(dnses);
+                        }
+                        try {
+                            mNMService.setDnsForwarders(dnsServers);
+                        } catch (Exception e) {
+                            transitionTo(mSetDnsForwardersErrorState);
+                        }
+                    }
                 }
                 notifyTetheredOfNewUpstreamIface(iface);
             }
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 55e0678..72cf512 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -482,6 +482,7 @@
                 if (state.exists()) {
                     throw new IllegalStateException("Cannot delete the state");
                 }
+                new File("/data/misc/vpn/abort").delete();
 
                 // Check if we need to restart any of the daemons.
                 boolean restart = false;
diff --git a/services/java/com/android/server/location/ComprehensiveCountryDetector.java b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
index f068b44..2d6a148 100755
--- a/services/java/com/android/server/location/ComprehensiveCountryDetector.java
+++ b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
@@ -20,16 +20,19 @@
 import android.location.Country;
 import android.location.CountryListener;
 import android.location.Geocoder;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.Slog;
 
 import java.util.Locale;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
  * This class is used to detect the country where the user is. The sources of
@@ -55,10 +58,15 @@
  */
 public class ComprehensiveCountryDetector extends CountryDetectorBase {
 
-    private final static String TAG = "ComprehensiveCountryDetector";
+    private final static String TAG = "CountryDetector";
     /* package */ static final boolean DEBUG = false;
 
     /**
+     * Max length of logs to maintain for debugging.
+     */
+    private static final int MAX_LENGTH_DEBUG_LOGS = 20;
+
+    /**
      * The refresh interval when the location based country was used
      */
     private final static long LOCATION_REFRESH_INTERVAL = 1000 * 60 * 60 * 24; // 1 day
@@ -74,6 +82,52 @@
     private PhoneStateListener mPhoneStateListener;
 
     /**
+     * List of the most recent country state changes for debugging. This should have
+     * a max length of MAX_LENGTH_LOGS.
+     */
+    private final ConcurrentLinkedQueue<Country> mDebugLogs = new ConcurrentLinkedQueue<Country>();
+
+    /**
+     * Most recent {@link Country} result that was added to the debug logs {@link #mDebugLogs}.
+     * We keep track of this value to help prevent adding many of the same {@link Country} objects
+     * to the logs.
+     */
+    private Country mLastCountryAddedToLogs;
+
+    /**
+     * Object used to synchronize access to {@link #mLastCountryAddedToLogs}. Be careful if
+     * using it to synchronize anything else in this file.
+     */
+    private final Object mObject = new Object();
+
+    /**
+     * Start time of the current session for which the detector has been active.
+     */
+    private long mStartTime;
+
+    /**
+     * Stop time of the most recent session for which the detector was active.
+     */
+    private long mStopTime;
+
+    /**
+     * The sum of all the time intervals in which the detector was active.
+     */
+    private long mTotalTime;
+
+    /**
+     * Number of {@link PhoneStateListener#onServiceStateChanged(ServiceState state)} events that
+     * have occurred for the current session for which the detector has been active.
+     */
+    private int mCountServiceStateChanges;
+
+    /**
+     * Total number of {@link PhoneStateListener#onServiceStateChanged(ServiceState state)} events
+     * that have occurred for all time intervals in which the detector has been active.
+     */
+    private int mTotalCountServiceStateChanges;
+
+    /**
      * The listener for receiving the notification from LocationBasedCountryDetector.
      */
     private CountryListener mLocationBasedCountryDetectionListener = new CountryListener() {
@@ -124,9 +178,36 @@
         if (result == null) {
             result = getLocaleCountry();
         }
+        addToLogs(result);
         return result;
     }
 
+    /**
+     * Attempt to add this {@link Country} to the debug logs.
+     */
+    private void addToLogs(Country country) {
+        if (country == null) {
+            return;
+        }
+        // If the country (ISO and source) are the same as before, then there is no
+        // need to add this country as another entry in the logs. Synchronize access to this
+        // variable since multiple threads could be calling this method.
+        synchronized (mObject) {
+            if (mLastCountryAddedToLogs != null && mLastCountryAddedToLogs.equals(country)) {
+                return;
+            }
+            mLastCountryAddedToLogs = country;
+        }
+        // Manually maintain a max limit for the list of logs
+        if (mDebugLogs.size() >= MAX_LENGTH_DEBUG_LOGS) {
+            mDebugLogs.poll();
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Slog.d(TAG, country.toString());
+        }
+        mDebugLogs.add(country);
+    }
+
     private boolean isNetworkCountryCodeAvailable() {
         // On CDMA TelephonyManager.getNetworkCountryIso() just returns SIM country.  We don't want
         // to prioritize it over location based country, so ignore it.
@@ -218,9 +299,14 @@
             removePhoneStateListener();
             stopLocationBasedDetector();
             cancelLocationRefresh();
+            mStopTime = SystemClock.elapsedRealtime();
+            mTotalTime += mStopTime;
         } else if (prevListener == null) {
             addPhoneStateListener();
             detectCountry(false, true);
+            mStartTime = SystemClock.elapsedRealtime();
+            mStopTime = 0;
+            mCountServiceStateChanges = 0;
         }
     }
 
@@ -308,9 +394,9 @@
     private void notifyIfCountryChanged(final Country country, final Country detectedCountry) {
         if (detectedCountry != null && mListener != null
                 && (country == null || !country.equals(detectedCountry))) {
-            Slog.d(TAG,
-                    "The country was changed from " + country != null ? country.getCountryIso() :
-                        country + " to " + detectedCountry.getCountryIso());
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Slog.d(TAG, "" + country + " --> " + detectedCountry);
+            }
             notifyListener(detectedCountry);
         }
     }
@@ -352,6 +438,9 @@
             mPhoneStateListener = new PhoneStateListener() {
                 @Override
                 public void onServiceStateChanged(ServiceState serviceState) {
+                    mCountServiceStateChanges++;
+                    mTotalCountServiceStateChanges++;
+
                     if (!isNetworkCountryCodeAvailable()) {
                         return;
                     }
@@ -374,4 +463,30 @@
     protected boolean isGeoCoderImplemented() {
         return Geocoder.isPresent();
     }
+
+    @Override
+    public String toString() {
+        long currentTime = SystemClock.elapsedRealtime();
+        long currentSessionLength = 0;
+        StringBuilder sb = new StringBuilder();
+        sb.append("ComprehensiveCountryDetector{");
+        // The detector hasn't stopped yet --> still running
+        if (mStopTime == 0) {
+            currentSessionLength = currentTime - mStartTime;
+            sb.append("timeRunning=" + currentSessionLength + ", ");
+        } else {
+            // Otherwise, it has already stopped, so take the last session
+            sb.append("lastRunTimeLength=" + (mStopTime - mStartTime) + ", ");
+        }
+        sb.append("totalCountServiceStateChanges=" + mTotalCountServiceStateChanges + ", ");
+        sb.append("currentCountServiceStateChanges=" + mCountServiceStateChanges + ", ");
+        sb.append("totalTime=" + (mTotalTime + currentSessionLength) + ", ");
+        sb.append("currentTime=" + currentTime + ", ");
+        sb.append("countries=");
+        for (Country country : mDebugLogs) {
+            sb.append("\n   " + country.toString());
+        }
+        sb.append("}");
+        return sb.toString();
+    }
 }
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 871ed68..f660520 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -1060,8 +1060,10 @@
         }
 
         // clear UID from current stats snapshot
-        mLastPollUidSnapshot = mLastPollUidSnapshot.withoutUid(uid);
-        mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
+        if (mLastPollUidSnapshot != null) {
+            mLastPollUidSnapshot = mLastPollUidSnapshot.withoutUid(uid);
+            mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
+        }
 
         // clear kernel stats associated with UID
         resetKernelUidStats(uid);
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 568a485..d345411 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -22,6 +22,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
+import android.net.NetworkInfo.DetailedState;
 import android.net.ProxyProperties;
 import android.net.RouteInfo;
 import android.net.wifi.WifiConfiguration.IpAssignment;
@@ -31,6 +32,9 @@
 import android.net.wifi.NetworkUpdateResult;
 import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
 import android.os.Environment;
+import android.os.Message;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -50,6 +54,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * This class provides the API to manage configured
@@ -136,6 +141,13 @@
     private static final String EXCLUSION_LIST_KEY = "exclusionList";
     private static final String EOS = "eos";
 
+    private static HandlerThread sDiskWriteHandlerThread;
+    private static DiskWriteHandler sDiskWriteHandler;
+    private static Object sDiskWriteHandlerSync = new Object();
+    /* Tracks multiple writes on the same thread */
+    private static int sWriteSequence = 0;
+    private static final int WRITE = 1;
+
     /**
      * Initialize context, fetch the list of configured networks
      * and enable all stored networks in supplicant.
@@ -153,10 +165,8 @@
      */
     static List<WifiConfiguration> getConfiguredNetworks() {
         List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
-        synchronized (sConfiguredNetworks) {
-            for(WifiConfiguration config : sConfiguredNetworks.values()) {
-                networks.add(new WifiConfiguration(config));
-            }
+        for(WifiConfiguration config : sConfiguredNetworks.values()) {
+            networks.add(new WifiConfiguration(config));
         }
         return networks;
     }
@@ -167,15 +177,13 @@
      */
     static void enableAllNetworks() {
         boolean networkEnabledStateChanged = false;
-        synchronized (sConfiguredNetworks) {
-            for(WifiConfiguration config : sConfiguredNetworks.values()) {
-                if(config != null && config.status == Status.DISABLED) {
-                    if(WifiNative.enableNetworkCommand(config.networkId, false)) {
-                        networkEnabledStateChanged = true;
-                        config.status = Status.ENABLED;
-                    } else {
-                        loge("Enable network failed on " + config.networkId);
-                    }
+        for(WifiConfiguration config : sConfiguredNetworks.values()) {
+            if(config != null && config.status == Status.DISABLED) {
+                if(WifiNative.enableNetworkCommand(config.networkId, false)) {
+                    networkEnabledStateChanged = true;
+                    config.status = Status.ENABLED;
+                } else {
+                    loge("Enable network failed on " + config.networkId);
                 }
             }
         }
@@ -226,12 +234,10 @@
     static void selectNetwork(int netId) {
         // Reset the priority of each network at start or if it goes too high.
         if (sLastPriority == -1 || sLastPriority > 1000000) {
-            synchronized (sConfiguredNetworks) {
-                for(WifiConfiguration config : sConfiguredNetworks.values()) {
-                    if (config.networkId != INVALID_NETWORK_ID) {
-                        config.priority = 0;
-                        addOrUpdateNetworkNative(config);
-                    }
+            for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                if (config.networkId != INVALID_NETWORK_ID) {
+                    config.priority = 0;
+                    addOrUpdateNetworkNative(config);
                 }
             }
             sLastPriority = 0;
@@ -264,15 +270,31 @@
         /* enable a new network */
         if (newNetwork && netId != INVALID_NETWORK_ID) {
             WifiNative.enableNetworkCommand(netId, false);
-            synchronized (sConfiguredNetworks) {
-                sConfiguredNetworks.get(netId).status = Status.ENABLED;
-            }
+            sConfiguredNetworks.get(netId).status = Status.ENABLED;
         }
         WifiNative.saveConfigCommand();
         sendConfiguredNetworksChangedBroadcast();
         return result;
     }
 
+    static void updateStatus(int netId, DetailedState state) {
+        if (netId != INVALID_NETWORK_ID) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config == null) return;
+            switch (state) {
+                case CONNECTED:
+                    config.status = Status.CURRENT;
+                    break;
+                case DISCONNECTED:
+                    config.status = Status.ENABLED;
+                    break;
+                default:
+                    //do nothing, retain the existing state
+                    break;
+            }
+        }
+    }
+
     /**
      * Forget the specified network and save config
      *
@@ -281,12 +303,10 @@
     static void forgetNetwork(int netId) {
         if (WifiNative.removeNetworkCommand(netId)) {
             WifiNative.saveConfigCommand();
-            synchronized (sConfiguredNetworks) {
-                WifiConfiguration config = sConfiguredNetworks.get(netId);
-                if (config != null) {
-                    sConfiguredNetworks.remove(netId);
-                    sNetworkIds.remove(configKey(config));
-                }
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null) {
+                sConfiguredNetworks.remove(netId);
+                sNetworkIds.remove(configKey(config));
             }
             writeIpAndProxyConfigurations();
             sendConfiguredNetworksChangedBroadcast();
@@ -319,13 +339,11 @@
      */
     static boolean removeNetwork(int netId) {
         boolean ret = WifiNative.removeNetworkCommand(netId);
-        synchronized (sConfiguredNetworks) {
-            if (ret) {
-                WifiConfiguration config = sConfiguredNetworks.get(netId);
-                if (config != null) {
-                    sConfiguredNetworks.remove(netId);
-                    sNetworkIds.remove(configKey(config));
-                }
+        if (ret) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null) {
+                sConfiguredNetworks.remove(netId);
+                sNetworkIds.remove(configKey(config));
             }
         }
         sendConfiguredNetworksChangedBroadcast();
@@ -349,10 +367,8 @@
     static boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
         boolean ret = WifiNative.enableNetworkCommand(netId, disableOthers);
 
-        synchronized (sConfiguredNetworks) {
-            WifiConfiguration config = sConfiguredNetworks.get(netId);
-            if (config != null) config.status = Status.ENABLED;
-        }
+        WifiConfiguration config = sConfiguredNetworks.get(netId);
+        if (config != null) config.status = Status.ENABLED;
 
         if (disableOthers) {
             markAllNetworksDisabledExcept(netId);
@@ -375,13 +391,11 @@
      */
     static boolean disableNetwork(int netId, int reason) {
         boolean ret = WifiNative.disableNetworkCommand(netId);
-        synchronized (sConfiguredNetworks) {
-            WifiConfiguration config = sConfiguredNetworks.get(netId);
-            /* Only change the reason if the network was not previously disabled */
-            if (config != null && config.status != Status.DISABLED) {
-                config.status = Status.DISABLED;
-                config.disableReason = reason;
-            }
+        WifiConfiguration config = sConfiguredNetworks.get(netId);
+        /* Only change the reason if the network was not previously disabled */
+        if (config != null && config.status != Status.DISABLED) {
+            config.status = Status.DISABLED;
+            config.disableReason = reason;
         }
         sendConfiguredNetworksChangedBroadcast();
         return ret;
@@ -450,10 +464,8 @@
      * Fetch the link properties for a given network id
      */
     static LinkProperties getLinkProperties(int netId) {
-        synchronized (sConfiguredNetworks) {
-            WifiConfiguration config = sConfiguredNetworks.get(netId);
-            if (config != null) return new LinkProperties(config.linkProperties);
-        }
+        WifiConfiguration config = sConfiguredNetworks.get(netId);
+        if (config != null) return new LinkProperties(config.linkProperties);
         return null;
     }
 
@@ -493,15 +505,13 @@
     static void setIpConfiguration(int netId, DhcpInfoInternal dhcpInfo) {
         LinkProperties linkProperties = dhcpInfo.makeLinkProperties();
 
-        synchronized (sConfiguredNetworks) {
-            WifiConfiguration config = sConfiguredNetworks.get(netId);
-            if (config != null) {
-                // add old proxy details
-                if(config.linkProperties != null) {
-                    linkProperties.setHttpProxy(config.linkProperties.getHttpProxy());
-                }
-                config.linkProperties = linkProperties;
+        WifiConfiguration config = sConfiguredNetworks.get(netId);
+        if (config != null) {
+            // add old proxy details
+            if(config.linkProperties != null) {
+                linkProperties.setHttpProxy(config.linkProperties.getHttpProxy());
             }
+            config.linkProperties = linkProperties;
         }
     }
 
@@ -509,14 +519,12 @@
      * clear IP configuration for a given network id
      */
     static void clearIpConfiguration(int netId) {
-        synchronized (sConfiguredNetworks) {
-            WifiConfiguration config = sConfiguredNetworks.get(netId);
-            if (config != null && config.linkProperties != null) {
-                // Clear everything except proxy
-                ProxyProperties proxy = config.linkProperties.getHttpProxy();
-                config.linkProperties.clear();
-                config.linkProperties.setHttpProxy(proxy);
-            }
+        WifiConfiguration config = sConfiguredNetworks.get(netId);
+        if (config != null && config.linkProperties != null) {
+            // Clear everything except proxy
+            ProxyProperties proxy = config.linkProperties.getHttpProxy();
+            config.linkProperties.clear();
+            config.linkProperties.setHttpProxy(proxy);
         }
     }
 
@@ -536,11 +544,9 @@
      * Return if the specified network is using static IP
      */
     static boolean isUsingStaticIp(int netId) {
-        synchronized (sConfiguredNetworks) {
-            WifiConfiguration config = sConfiguredNetworks.get(netId);
-            if (config != null && config.ipAssignment == IpAssignment.STATIC) {
-                return true;
-            }
+        WifiConfiguration config = sConfiguredNetworks.get(netId);
+        if (config != null && config.ipAssignment == IpAssignment.STATIC) {
+            return true;
         }
         return false;
     }
@@ -555,67 +561,62 @@
         String listStr = WifiNative.listNetworksCommand();
         sLastPriority = 0;
 
-        synchronized (sConfiguredNetworks) {
-            sConfiguredNetworks.clear();
-            sNetworkIds.clear();
+        sConfiguredNetworks.clear();
+        sNetworkIds.clear();
 
-            if (listStr == null)
-                return;
+        if (listStr == null)
+            return;
 
-            String[] lines = listStr.split("\n");
-            // Skip the first line, which is a header
-            for (int i = 1; i < lines.length; i++) {
-                String[] result = lines[i].split("\t");
-                // network-id | ssid | bssid | flags
-                WifiConfiguration config = new WifiConfiguration();
-                try {
-                    config.networkId = Integer.parseInt(result[0]);
-                } catch(NumberFormatException e) {
-                    continue;
-                }
-                if (result.length > 3) {
-                    if (result[3].indexOf("[CURRENT]") != -1)
-                        config.status = WifiConfiguration.Status.CURRENT;
-                    else if (result[3].indexOf("[DISABLED]") != -1)
-                        config.status = WifiConfiguration.Status.DISABLED;
-                    else
-                        config.status = WifiConfiguration.Status.ENABLED;
-                } else {
-                    config.status = WifiConfiguration.Status.ENABLED;
-                }
-                readNetworkVariables(config);
-                if (config.priority > sLastPriority) {
-                    sLastPriority = config.priority;
-                }
-                sConfiguredNetworks.put(config.networkId, config);
-                sNetworkIds.put(configKey(config), config.networkId);
+        String[] lines = listStr.split("\n");
+        // Skip the first line, which is a header
+        for (int i = 1; i < lines.length; i++) {
+            String[] result = lines[i].split("\t");
+            // network-id | ssid | bssid | flags
+            WifiConfiguration config = new WifiConfiguration();
+            try {
+                config.networkId = Integer.parseInt(result[0]);
+            } catch(NumberFormatException e) {
+                continue;
             }
+            if (result.length > 3) {
+                if (result[3].indexOf("[CURRENT]") != -1)
+                    config.status = WifiConfiguration.Status.CURRENT;
+                else if (result[3].indexOf("[DISABLED]") != -1)
+                    config.status = WifiConfiguration.Status.DISABLED;
+                else
+                    config.status = WifiConfiguration.Status.ENABLED;
+            } else {
+                config.status = WifiConfiguration.Status.ENABLED;
+            }
+            readNetworkVariables(config);
+            if (config.priority > sLastPriority) {
+                sLastPriority = config.priority;
+            }
+            sConfiguredNetworks.put(config.networkId, config);
+            sNetworkIds.put(configKey(config), config.networkId);
         }
+
         readIpAndProxyConfigurations();
         sendConfiguredNetworksChangedBroadcast();
     }
 
     static void updateIpAndProxyFromWpsConfig(int netId, WpsInfo wpsConfig) {
-        synchronized (sConfiguredNetworks) {
-            WifiConfiguration config = sConfiguredNetworks.get(netId);
-            if (config != null) {
-                config.ipAssignment = wpsConfig.ipAssignment;
-                config.proxySettings = wpsConfig.proxySettings;
-                config.linkProperties = wpsConfig.linkProperties;
-                writeIpAndProxyConfigurations();
-            }
+        WifiConfiguration config = sConfiguredNetworks.get(netId);
+        if (config != null) {
+            config.ipAssignment = wpsConfig.ipAssignment;
+            config.proxySettings = wpsConfig.proxySettings;
+            config.linkProperties = wpsConfig.linkProperties;
+            writeIpAndProxyConfigurations();
         }
     }
 
     /* Mark all networks except specified netId as disabled */
     private static void markAllNetworksDisabledExcept(int netId) {
-        synchronized (sConfiguredNetworks) {
-            for(WifiConfiguration config : sConfiguredNetworks.values()) {
-                if(config != null && config.networkId != netId) {
-                    if (config.status != Status.DISABLED) {
-                        config.status = Status.DISABLED;
-                        config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
-                    }
+        for(WifiConfiguration config : sConfiguredNetworks.values()) {
+            if(config != null && config.networkId != netId) {
+                if (config.status != Status.DISABLED) {
+                    config.status = Status.DISABLED;
+                    config.disableReason = WifiConfiguration.DISABLED_UNKNOWN_REASON;
                 }
             }
         }
@@ -627,15 +628,46 @@
 
     private static void writeIpAndProxyConfigurations() {
 
-        DataOutputStream out = null;
-        try {
-            out = new DataOutputStream(new BufferedOutputStream(
-                    new FileOutputStream(ipConfigFile)));
+        /* Make a copy */
+        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+        for(WifiConfiguration config : sConfiguredNetworks.values()) {
+            networks.add(new WifiConfiguration(config));
+        }
 
-            out.writeInt(IPCONFIG_FILE_VERSION);
+        /* Do a delayed write to disk on a seperate handler thread */
+        synchronized (sDiskWriteHandlerSync) {
+            if (++sWriteSequence == 1) {
+                sDiskWriteHandlerThread = new HandlerThread("WifiConfigThread");
+                sDiskWriteHandlerThread.start();
+                sDiskWriteHandler = new DiskWriteHandler(sDiskWriteHandlerThread.getLooper());
+            }
+        }
 
-            synchronized (sConfiguredNetworks) {
-                for(WifiConfiguration config : sConfiguredNetworks.values()) {
+        sDiskWriteHandler.sendMessage(Message.obtain(sDiskWriteHandler, WRITE, networks));
+    }
+
+    private static class DiskWriteHandler extends Handler {
+
+        DiskWriteHandler(android.os.Looper l) {
+            super(l);
+        }
+
+        public void handleMessage(Message msg) {
+
+            if (msg.what != WRITE) {
+                throw new RuntimeException("Unsupported message in WifiConfigStore: " + msg);
+            }
+
+            List<WifiConfiguration> networks = (List<WifiConfiguration>) msg.obj;
+
+            DataOutputStream out = null;
+            try {
+                out = new DataOutputStream(new BufferedOutputStream(
+                            new FileOutputStream(ipConfigFile)));
+
+                out.writeInt(IPCONFIG_FILE_VERSION);
+
+                for(WifiConfiguration config : networks) {
                     boolean writeToFile = false;
 
                     try {
@@ -720,17 +752,26 @@
                     }
                     out.writeUTF(EOS);
                 }
-            }
 
-        } catch (IOException e) {
-            loge("Error writing data file");
-        } finally {
-            if (out != null) {
-                try {
-                    out.close();
-                } catch (Exception e) {}
+            } catch (IOException e) {
+                loge("Error writing data file");
+            } finally {
+                if (out != null) {
+                    try {
+                        out.close();
+                    } catch (Exception e) {}
+                }
+
+                //Quit if no more writes sent
+                synchronized (sDiskWriteHandlerSync) {
+                    if (--sWriteSequence == 0) {
+                        getLooper().quit();
+                        sDiskWriteHandlerThread = null;
+                        sDiskWriteHandler= null;
+                    }
+                }
             }
-        }
+       }
     }
 
     private static void readIpAndProxyConfigurations() {
@@ -806,44 +847,42 @@
                 } while (true);
 
                 if (id != -1) {
-                    synchronized (sConfiguredNetworks) {
-                        WifiConfiguration config = sConfiguredNetworks.get(
-                                sNetworkIds.get(id));
+                    WifiConfiguration config = sConfiguredNetworks.get(
+                            sNetworkIds.get(id));
 
-                        if (config == null) {
-                            loge("configuration found for missing network, ignored");
-                        } else {
-                            config.linkProperties = linkProperties;
-                            switch (ipAssignment) {
-                                case STATIC:
-                                case DHCP:
-                                    config.ipAssignment = ipAssignment;
-                                    break;
-                                case UNASSIGNED:
-                                    //Ignore
-                                    break;
-                                default:
-                                    loge("Ignore invalid ip assignment while reading");
-                                    break;
-                            }
+                    if (config == null) {
+                        loge("configuration found for missing network, ignored");
+                    } else {
+                        config.linkProperties = linkProperties;
+                        switch (ipAssignment) {
+                            case STATIC:
+                            case DHCP:
+                                config.ipAssignment = ipAssignment;
+                                break;
+                            case UNASSIGNED:
+                                //Ignore
+                                break;
+                            default:
+                                loge("Ignore invalid ip assignment while reading");
+                                break;
+                        }
 
-                            switch (proxySettings) {
-                                case STATIC:
-                                    config.proxySettings = proxySettings;
-                                    ProxyProperties proxyProperties =
-                                        new ProxyProperties(proxyHost, proxyPort, exclusionList);
-                                    linkProperties.setHttpProxy(proxyProperties);
-                                    break;
-                                case NONE:
-                                    config.proxySettings = proxySettings;
-                                    break;
-                                case UNASSIGNED:
-                                    //Ignore
-                                    break;
-                                default:
-                                    loge("Ignore invalid proxy settings while reading");
-                                    break;
-                            }
+                        switch (proxySettings) {
+                            case STATIC:
+                                config.proxySettings = proxySettings;
+                                ProxyProperties proxyProperties =
+                                    new ProxyProperties(proxyHost, proxyPort, exclusionList);
+                                linkProperties.setHttpProxy(proxyProperties);
+                                break;
+                            case NONE:
+                                config.proxySettings = proxySettings;
+                                break;
+                            case UNASSIGNED:
+                                //Ignore
+                                break;
+                            default:
+                                loge("Ignore invalid proxy settings while reading");
+                                break;
                         }
                     }
                 } else {
@@ -1061,10 +1100,7 @@
          * when written. For example, wep key is stored as * irrespective
          * of the value sent to the supplicant
          */
-        WifiConfiguration sConfig;
-        synchronized (sConfiguredNetworks) {
-            sConfig = sConfiguredNetworks.get(netId);
-        }
+        WifiConfiguration sConfig = sConfiguredNetworks.get(netId);
         if (sConfig == null) {
             sConfig = new WifiConfiguration();
             sConfig.networkId = netId;
@@ -1072,10 +1108,8 @@
 
         readNetworkVariables(sConfig);
 
-        synchronized (sConfiguredNetworks) {
-            sConfiguredNetworks.put(netId, sConfig);
-            sNetworkIds.put(configKey(sConfig), netId);
-        }
+        sConfiguredNetworks.put(netId, sConfig);
+        sNetworkIds.put(configKey(sConfig), netId);
 
         NetworkUpdateResult result = writeIpAndProxyConfigurationsOnChange(sConfig, config);
         result.setNetworkId(netId);
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 5437caa..71d7c01 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -276,6 +276,8 @@
     static final int CMD_CLEAR_BLACKLIST                  = BASE + 58;
     /* Save configuration */
     static final int CMD_SAVE_CONFIG                      = BASE + 59;
+    /* Get configured networks*/
+    static final int CMD_GET_CONFIGURED_NETWORKS          = BASE + 60;
 
     /* Supplicant commands after driver start*/
     /* Initiate a scan */
@@ -847,8 +849,11 @@
         return result;
     }
 
-    public List<WifiConfiguration> syncGetConfiguredNetworks() {
-        return WifiConfigStore.getConfiguredNetworks();
+    public List<WifiConfiguration> syncGetConfiguredNetworks(AsyncChannel channel) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS);
+        List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
+        resultMsg.recycle();
+        return result;
     }
 
     /**
@@ -1637,8 +1642,10 @@
         mWifiInfo.setRssi(MIN_RSSI);
         mWifiInfo.setLinkSpeed(-1);
 
-        /* send event to CM & network change broadcast */
         setNetworkDetailedState(DetailedState.DISCONNECTED);
+        WifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
+
+        /* send event to CM & network change broadcast */
         sendNetworkStateChangeBroadcast(mLastBssid);
 
         /* Clear network properties */
@@ -1721,6 +1728,7 @@
         } else {
             configureLinkProperties();
             setNetworkDetailedState(DetailedState.CONNECTED);
+            WifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
             sendNetworkStateChangeBroadcast(mLastBssid);
         }
     }
@@ -1811,6 +1819,10 @@
                 case CMD_SAVE_CONFIG:
                     mReplyChannel.replyToMessage(message, message.what, FAILURE);
                     break;
+                case CMD_GET_CONFIGURED_NETWORKS:
+                    mReplyChannel.replyToMessage(message, message.what,
+                            WifiConfigStore.getConfiguredNetworks());
+                    break;
                 case CMD_ENABLE_RSSI_POLL:
                     mEnableRssiPolling = (message.arg1 == 1);
                     break;