Merge "Implement runtime switch to select default renderer mode"
diff --git a/api/current.txt b/api/current.txt
index bc519691..3a30749d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10068,17 +10068,14 @@
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
     method public java.lang.CharSequence getDisabledMessage();
-    method public int getDisabledMessageResourceId();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
     method public long getLastChangedTimestamp();
     method public java.lang.CharSequence getLongLabel();
-    method public int getLongLabelResourceId();
     method public java.lang.String getPackage();
     method public int getRank();
     method public java.lang.CharSequence getShortLabel();
-    method public int getShortLabelResourceId();
     method public android.os.UserHandle getUserHandle();
     method public boolean hasKeyFieldsOnly();
     method public boolean isDeclaredInManifest();
@@ -10092,7 +10089,6 @@
   }
 
   public static class ShortcutInfo.Builder {
-    ctor public deprecated ShortcutInfo.Builder(android.content.Context);
     ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
@@ -10100,7 +10096,6 @@
     method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
-    method public deprecated android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
     method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
     method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setRank(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 119ce41..5f1c069 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10493,17 +10493,14 @@
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
     method public java.lang.CharSequence getDisabledMessage();
-    method public int getDisabledMessageResourceId();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
     method public long getLastChangedTimestamp();
     method public java.lang.CharSequence getLongLabel();
-    method public int getLongLabelResourceId();
     method public java.lang.String getPackage();
     method public int getRank();
     method public java.lang.CharSequence getShortLabel();
-    method public int getShortLabelResourceId();
     method public android.os.UserHandle getUserHandle();
     method public boolean hasKeyFieldsOnly();
     method public boolean isDeclaredInManifest();
@@ -10517,7 +10514,6 @@
   }
 
   public static class ShortcutInfo.Builder {
-    ctor public deprecated ShortcutInfo.Builder(android.content.Context);
     ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
@@ -10525,7 +10521,6 @@
     method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
-    method public deprecated android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
     method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
     method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setRank(int);
@@ -39415,10 +39410,14 @@
     method public java.lang.String getConnectionService();
     method public java.util.List<android.telecom.ParcelableCallAnalytics.EventTiming> getEventTimings();
     method public long getStartTimeMillis();
+    method public java.util.List<android.telecom.ParcelableCallAnalytics.VideoEvent> getVideoEvents();
     method public boolean isAdditionalCall();
     method public boolean isCreatedFromExistingConnection();
     method public boolean isEmergencyCall();
     method public boolean isInterrupted();
+    method public boolean isVideoCall();
+    method public void setIsVideoCall(boolean);
+    method public void setVideoEvents(java.util.List<android.telecom.ParcelableCallAnalytics.VideoEvent>);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CALLTYPE_INCOMING = 1; // 0x1
     field public static final int CALLTYPE_OUTGOING = 2; // 0x2
@@ -39501,6 +39500,20 @@
     field public static final int UNHOLD_TIMING = 4; // 0x4
   }
 
+  public static final class ParcelableCallAnalytics.VideoEvent implements android.os.Parcelable {
+    ctor public ParcelableCallAnalytics.VideoEvent(int, long, int);
+    method public int describeContents();
+    method public int getEventName();
+    method public long getTimeSinceLastEvent();
+    method public int getVideoState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.ParcelableCallAnalytics.VideoEvent> CREATOR;
+    field public static final int RECEIVE_REMOTE_SESSION_MODIFY_REQUEST = 2; // 0x2
+    field public static final int RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE = 3; // 0x3
+    field public static final int SEND_LOCAL_SESSION_MODIFY_REQUEST = 0; // 0x0
+    field public static final int SEND_LOCAL_SESSION_MODIFY_RESPONSE = 1; // 0x1
+  }
+
   public final deprecated class Phone {
     method public final void addListener(android.telecom.Phone.Listener);
     method public final boolean canAddCall();
diff --git a/api/test-current.txt b/api/test-current.txt
index 7b95d1a..6b8f7d5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10081,17 +10081,14 @@
     method public android.content.ComponentName getActivity();
     method public java.util.Set<java.lang.String> getCategories();
     method public java.lang.CharSequence getDisabledMessage();
-    method public int getDisabledMessageResourceId();
     method public android.os.PersistableBundle getExtras();
     method public java.lang.String getId();
     method public android.content.Intent getIntent();
     method public long getLastChangedTimestamp();
     method public java.lang.CharSequence getLongLabel();
-    method public int getLongLabelResourceId();
     method public java.lang.String getPackage();
     method public int getRank();
     method public java.lang.CharSequence getShortLabel();
-    method public int getShortLabelResourceId();
     method public android.os.UserHandle getUserHandle();
     method public boolean hasKeyFieldsOnly();
     method public boolean isDeclaredInManifest();
@@ -10105,7 +10102,6 @@
   }
 
   public static class ShortcutInfo.Builder {
-    ctor public deprecated ShortcutInfo.Builder(android.content.Context);
     ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
@@ -10113,7 +10109,6 @@
     method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
-    method public deprecated android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
     method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
     method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setRank(int);
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 35370f0..39e15e0 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -696,7 +696,8 @@
         private PersistableBundle mExtras;
 
         /**
-         * Old style constructor.  STOPSHIP hide it before launch.
+         * Old style constructor.
+         * @hide
          */
         @Deprecated
         public Builder(Context context) {
@@ -704,7 +705,8 @@
         }
 
         /**
-         * Used with the old style constructor, kept for unit tests. STOPSHIP hide it before launch.
+         * Used with the old style constructor, kept for unit tests.
+         * @hide
          */
         @NonNull
         @Deprecated
@@ -1004,7 +1006,7 @@
         return mTitle;
     }
 
-    /** TODO Javadoc */
+    /** @hide */
     public int getShortLabelResourceId() {
         return mTitleResId;
     }
@@ -1017,7 +1019,7 @@
         return mText;
     }
 
-    /** TODO Javadoc */
+    /** @hide */
     public int getLongLabelResourceId() {
         return mTextResId;
     }
@@ -1030,7 +1032,7 @@
         return mDisabledMessage;
     }
 
-    /** TODO Javadoc */
+    /** @hide */
     public int getDisabledMessageResourceId() {
         return mDisabledMessageResId;
     }
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index bec6f29..6143404 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -334,7 +334,7 @@
         /**
          * Sets the content type.
          * <p>
-         * <strong>Default: </strong> {@link #CONTENT_TYPE_UNKNOWN}
+         * <strong>Default: </strong> {@link #CONTENT_TYPE_DOCUMENT}
          * </p>
          *
          * @param type The content type.
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 3881d6d..a56eba6 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -186,9 +186,13 @@
 
 static void send_query_for_apps() {
     hub_message_t msg;
+    query_apps_request_t queryMsg;
+
+    queryMsg.app_name.id = NANOAPP_VENDOR_ALL_APPS;
 
     msg.message_type = CONTEXT_HUB_QUERY_APPS;
-    msg.message_len  = 0;
+    msg.message_len  = sizeof(queryMsg);
+    msg.message = &queryMsg;
 
     for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
         ALOGD("Sending query for apps to hub %d", i);
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index cf56f2b..11e06f1 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -149,9 +149,11 @@
   to: /google/play/licensing/index.html
 - from: /google/play/billing/billing_about.html
   to: /google/play/billing/index.html
-- from: /guide/developing/tools/
+- from: /guide/developing/tools/proguard.html
+  to: /studio/build/shrink-code.html
+- from: /guide/developing/tools/...
   to: /studio/command-line/
-- from: /guide/developing/
+- from: /guide/developing/...
   to: /studio/
 - from: /tools/aidl.html
   to: /guide/components/aidl.html
diff --git a/docs/html/distribute/engage/_book.yaml b/docs/html/distribute/engage/_book.yaml
index 87e819a..c371268 100644
--- a/docs/html/distribute/engage/_book.yaml
+++ b/docs/html/distribute/engage/_book.yaml
@@ -31,3 +31,6 @@
 
 - title: Get Feedback with Beta Tests
   path: /distribute/engage/beta.html
+
+- title: Interact with Nearby Users
+  path: /distribute/engage/nearby.html
diff --git a/docs/html/distribute/engage/engage_toc.cs b/docs/html/distribute/engage/engage_toc.cs
index 4f3e0af..cc6e2844 100644
--- a/docs/html/distribute/engage/engage_toc.cs
+++ b/docs/html/distribute/engage/engage_toc.cs
@@ -66,6 +66,12 @@
         <span class="en">Get Feedback with Beta Tests</span></a>
     </div>
   </li>
+  <li class="nav-section">
+    <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs
+        var:toroot?>distribute/engage/nearby.html">
+        <span class="en">Interact with Nearby Users</span></a>
+    </div>
+  </li>
 </ul>
 
 <script type="text/javascript">
diff --git a/docs/html/distribute/engage/nearby.jd b/docs/html/distribute/engage/nearby.jd
new file mode 100644
index 0000000..b1571f6
--- /dev/null
+++ b/docs/html/distribute/engage/nearby.jd
@@ -0,0 +1,93 @@
+page.title=Interact with Nearby Users
+page.metaDescription=Use the Nearby feature to interact with nearby people, devices, and beacons.
+page.image=images/distribute/nearby_connections.png
+page.tags="users, nearby, engage"
+@jd:body
+
+<p>Create experiences that seem magical for users who are in close proximity by using the unique
+close-range and cross-platform capabilities of Nearby. Set up multiplayer games, ad-hoc groups,
+sharing, or collaborative sessions so that your users can work or play together more easily when
+they're close.</p>
+
+<p>Learn more about <a href="https://developers.google.com/nearby/">how to add nearby interactions
+to your app or game</a>.</p>
+
+<div class="wrap">
+  <div class="cols" style="margin-top:1em;">
+    <div class="col-4of12">
+      <h3>
+        Messaging
+      </h3>
+      <img src="{@docRoot}images/distribute/nearby_messaging.png">
+      <p class="figure-caption">
+        Find nearby devices and share messages to enable rich interactions and collaboration
+        among users.
+      </p>
+    </div>
+
+    <div class="col-4of12">
+      <h3>
+        Connections
+      </h3>
+      <img src="{@docRoot}images/distribute/nearby_connections.png">
+      <p class="figure-caption">
+        Discover other local devices and create connections that enable real-time, cross-device
+        experiences.
+      </p>
+    </div>
+
+    <div class="col-4of12">
+      <h3>
+        Beacons
+      </h3>
+      <img src="{@docRoot}images/distribute/nearby_beacons.png">
+      <p class="figure-caption">
+        Receive messages from beacons using
+        <a href="https://developers.google.com/beacons/eddystone">Eddystone</a> and add context to
+        location-based apps and games.
+      </p>
+    </div>
+  </div>
+</div>
+
+<p class="note"><strong>Note:</strong> Nearby uses Bluetooth 2.0, Bluetooth 4.0, Wi-Fi, and an
+ultrasonic modem to function over distances of up to 100 feet.</p>
+
+<h2 id="best-practices">Best practices</h2>
+
+<p>The following list contains some helpful tips and best practices that will help you set up
+and use Nearby effectively:
+
+<ul>
+  <li>Use Nearby features sparingly and only when they're needed because they can consume battery
+  life quickly (up to 3.5 times faster than normal).
+  </li>
+
+  <li>Invoke Nearby explicitly with a button, switch, or special screen, and provide a visual
+  indication when the features are actively sending or receiving content.
+  </li>
+
+  <li>Ensure that users are aware of the data that is made visible by Nearby before
+  they start using the features.
+  </li>
+
+  <li>Stop any publish or subscribe operations when the user exits the app or stops the
+  activity that requires Nearby.
+  </li>
+
+  <li>Use the <em>earshot</em> option, which uses only the ultrasonic modem to send and receive
+  messages, to limit the range of Nearby messages to about five feet when privacy is important.
+  </li>
+
+  <li>Accelerate the exchange of messages (when appropriate) by making one device the publisher
+  only, and all other devices subscribers.
+  </li>
+</ul>
+
+<h2 id="related-resources">Related resources</h2>
+
+<div class="resource-widget resource-flow-layout col-13"
+  data-query="collection:distribute/users/nearby"
+  data-sortOrder="-timestamp"
+  data-cardSizes="9x3"
+  data-maxResults="6"></div>
diff --git a/docs/html/distribute/monetize/_book.yaml b/docs/html/distribute/monetize/_book.yaml
index 2ebc695..974e9ed 100644
--- a/docs/html/distribute/monetize/_book.yaml
+++ b/docs/html/distribute/monetize/_book.yaml
@@ -16,3 +16,6 @@
 
 - title: Purchasing
   path: /distribute/monetize/payments.html
+
+- title: Drive Conversions
+  path: /distribute/monetize/conversions.html
diff --git a/docs/html/distribute/monetize/conversions.jd b/docs/html/distribute/monetize/conversions.jd
new file mode 100644
index 0000000..20b2333
--- /dev/null
+++ b/docs/html/distribute/monetize/conversions.jd
@@ -0,0 +1,100 @@
+page.title=Drive Conversions
+page.image=images/cards/card-drive-conversions_16-9_2x.png
+page.metaDescription=Discover where your users are coming from, drive engagement, and surface your in-app products to maximize your conversions.
+page.tags="conversions"
+
+@jd:body
+
+<div class="figure">
+  <img src="{@docRoot}images/cards/card-drive-conversions_16-9_2x.png">
+</div>
+
+<p>
+  Users who've made in-app purchases or converted in other ways are more likely
+  to do so again.
+  You can now easily discover where those users are coming from, drive engagement,
+  and surface your in-app products to maximize your conversions.
+</p>
+
+<div class="headerLine">
+  <h2 id="dicover">
+  Discover your most valuable channels
+  </h2>
+
+</div>
+
+<p>
+  From the <strong>User Acquisition</strong> page in the Google Play Developer Console, explore
+  how users convert into spenders across your acquisition channels and find the best prospects
+  for app install campaigns.
+</p>
+
+<p>For more information, view the guide on how to <a class="external-link"
+ href="https://support.google.com/googleplay/android-developer/answer/6263332">
+ measure your app's user acquisition</a> in the Google Play Developer Console Help Center.</p>
+
+</p>
+
+<div class="headerLine">
+  <h2 id="adwords">
+  Re-engage users with AdWords ads
+  </h2>
+
+</div>
+
+<p>
+  Bring users back to your app by creating an AdWords re-engagement campaign.
+  Use display and search ads that appear only to users who have installed your app.
+  Use deep links to bring them to where they'll find the content or actions they're
+  searching for.
+</p>
+
+<p>For more information, view the guide on <a class="external-link"
+ href="https://support.google.com/adwords/topic/3119078?hl=en">campaign settings</a> in the
+ AdWords Help Center.</p>
+
+<div class="headerLine">
+  <h2 id="gift-cards">
+  Drive spending with AdMob in-app purchase ads
+  </h2>
+</div>
+
+<p>Use your Google Analytics data to create Remarketing Audience lists for the high-value
+ users most likely to purchase products within your app. Then create an AdMob campaign that
+ targets these users to increase their awareness of your products.</p>
+
+<p>For more information, view the guide on how to <a class="external-link"
+ href="https://www.google.com/admob/promote.html">drive more in-app purchases and installs</a>
+ in the AdMob platform.</p>
+
+<div class="headerLine">
+  <h2 id="tips">
+  Tips
+  </h2>
+</div>
+<ul>
+<li>Add <a class="external-link"
+ href="https://developers.google.com/app-indexing/webmasters/app">deep links</a>
+ to your app so ads bring users directly to
+ conversion activities.</li>
+<li>Track what users do in your app by installing the
+ AdWords <a class="external-link"
+ href="https://developers.google.com/app-conversion-tracking">
+ Conversion Tracking SDK</a>.</li>
+<li>Link your Google Analytics and AdMob accounts to share audience lists.</li>
+<li>Make conversion ads compelling, such as promoting a booking search or
+ in-app product special offer.</li>
+<li>Use the AdMob Conversion Optimizer with existing campaigns. Predictions
+ are more accurate when there is more data to work with.</li>
+<li>Re-engage with your app's users across the Display Network with remarketing
+ lists in AdMob and with search keywords in AdWords.</li>
+</ul>
+
+
+<div class="headerLine"><h2 id="related-resources">Related resources</h2></div>
+
+<div class="resource-widget resource-flow-layout col-13"
+  data-query="collection:distribute/monetize/conversions"
+  data-sortOrder="-timestamp"
+  data-cardSizes="9x3"
+  data-maxResults="8"></div>
diff --git a/docs/html/distribute/monetize/monetize_toc.cs b/docs/html/distribute/monetize/monetize_toc.cs
index a3aa50fe..b586633 100644
--- a/docs/html/distribute/monetize/monetize_toc.cs
+++ b/docs/html/distribute/monetize/monetize_toc.cs
@@ -34,6 +34,12 @@
         </a>
     </div>
   </li>
+  <li class="nav-section">
+    <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/monetize/conversions.html">
+          <span class="en">Drive Conversions</span>
+        </a>
+    </div>
+  </li>
 
 </ul>
 
@@ -44,4 +50,3 @@
     changeNavLang(getLangPref());
 //-->
 </script>
-
diff --git a/docs/html/distribute/monetize/payments.jd b/docs/html/distribute/monetize/payments.jd
index 7d972bb..004e47e 100644
--- a/docs/html/distribute/monetize/payments.jd
+++ b/docs/html/distribute/monetize/payments.jd
@@ -15,36 +15,43 @@
   instantly with a streamlined, consistent purchasing process and convenient
   payment methods.
 </p>
-
+<p><strong>Key facts</strong></p>
+<ul>
+<li>Direct carrier billing in over 35 countries.</li>
+<li>Google Play gift cards in over 25 countries.</li>
+<li>PayPal in over 20 countries.</li>
+<li>Developers can sell their apps from over 75 countries.</li>
+<li>Users can buy apps in over 135 countries.</li>
+</ul>
 <div class="headerLine">
   <h2 id="dcb">
-  Direct Carrier Billing
+  Direct carrier billing
   </h2>
 
 
 </div>
 
 <p>
-  Users pay by charging their monthly carrier bills . The benefit of Direct
-  Carrier Billing is that it opens up markets where credit cards are less
-  common, as purchases are charged to your customers’ monthly mobile phone
+  Users pay by charging their monthly carrier bills. The benefit of direct
+  carrier billing is that it opens up markets where credit cards are less
+  common, as purchases are charged to your customers' monthly mobile phone
   bills. This option is available to users in key markets
   around the world. Many more will get the option in the months ahead.
 </p>
 
 <div class="headerLine">
   <h2 id="credit">
-  Credit Cards
+  Credit cards
   </h2>
 
 </div>
 
 <p>
-  Users can pay using any credit card that they’ve registered in Google Play.
-  Credit Cards added to Google Play are stored in the user’s Google wallet and
-  available for in-app purchases and instant buys too. To make it easy for
-  users to get started, registration is offered as a part of the initial device
-  setup process.
+  Users can pay using any credit card that they've registered in Google Play.
+  The credit cards that a user adds to Google Play are stored in the user's Google wallet.
+  They are available for in-app purchases and instant buys. It's easy for
+  users to get started, as the initial device setup process allows users to register a credit
+  card that they can use in Google Play.
 </p>
 
 <div class="headerLine">
@@ -62,16 +69,40 @@
 <p>
   Gift cards enable users to add value to their Google Play balance by entering
   a unique code printed on a card purchased online or from major retailers.
-  More information gift cards can be found <a href=
+  More information on gift cards can be found <a href=
   "http://play.google.com/intl/en-US_us/about/giftcards/" target=
   "_android">here</a>.
 </p>
 
 <p style="clear:both">
 </p>
+
+<div class="headerLine">
+  <h2 id="paypal">
+  PayPal
+  </h2>
+
+
+</div>
+
+<div class="figure">
+  <img src="{@docRoot}images/paypal-logo.png">
+</div>
+
+<p>
+  Users with PayPal accounts can buy apps and digital content on Google Play using any
+  of their available payment methods. They sign into their PayPal account and
+  complete the purchase. The popularity of PayPal for payments on the web gives
+  you more customers.
+</p>
+
+<p style="clear:both">
+</p>
+
+
 <div class="headerLine">
   <h2 id="balance">
-  Google Play Balance
+  Google Play balance
   </h2>
 
 
@@ -95,14 +126,13 @@
   The payment methods available to users may vary based on location, carrier
   network, and other factors.
 </p>
- 
+
 <p style="clear:both">
 </p>
-<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div>
+<div class="headerLine"><h2 id="related-resources">Related resources</h2></div>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/monetize/paymentmethods"
   data-sortOrder="-timestamp"
   data-cardSizes="9x3"
   data-maxResults="8"></div>
-
diff --git a/docs/html/distribute/users/promote-with-ads.jd b/docs/html/distribute/users/promote-with-ads.jd
index 2db4ca3..d99f449 100644
--- a/docs/html/distribute/users/promote-with-ads.jd
+++ b/docs/html/distribute/users/promote-with-ads.jd
@@ -6,20 +6,27 @@
 
 <p>Users have a huge amount of choice when it comes to which apps they install and
 use, so it’s important to actively find new ways to promote your app and drive
-ongoing engagement. AdWords is a powerful and effective way to do both.</p>
+ongoing engagement. AdWords campaigns, which you create in the
+<a href="http://play.google.com/apps/publish">Google Play Developer Console</a>,
+are a powerful and effective way to do both.</p>
 
 
-<h2 id=drive_installs>Drive installs</h2>
+<h2 id="drive_installs">Drive installs with universal app campaigns</h2>
 
-<p><a href="http://adwords.google.com">AdWords</a> promotes your app to interested
-users where they spend time on phones and
-tablets – with app install ads on Google Search, YouTube, Gmail, and within
-apps and across the web on  the Google Display Network. AdWords is a powerful
-way to scale app promotion across Google networks and find customers that are
-most likely to install your app. </p>
+<p><a href="http://adwords.google.com">AdWords</a> is a powerful way to scale
+app promotion across Google networks and find customers who are most likely to
+install your app. AdWords promotes your app to interested users where they spend
+time on phones and tablets – with app install ads on Google Play, Google Search,
+YouTube, Gmail, and within apps and across the web.</p>
 
-<p><a href="https://support.google.com/adwords/answer/6032059">Get started with AdWords
-app install ads</a>.</p>
+<p>By creating a <em>universal app camapign</em>, you can reach all of these
+networks. This type of campaign allocates ads, bids, and budgets automatically,
+making it easier to improve install volume for your app.</p>
+
+<p>To learn more about creating universal ad campaigns, read the article about
+<a class="external-link" href="https://support.google.com/googleplay/android-developer/answer/6262700">creating
+an AdWords campaign for your app</a> in the Google Play Developer Console Help
+Center.</p>
 
 <div class="wrap">
   <div class="cols" style="margin-top:1em;">
@@ -27,18 +34,16 @@
       <h3>
         From Google Play
       </h3>
-      <img src="/images/distribute/promote_ads_play.png">
+      <img src="{@docRoot}images/distribute/promote_ads_play.png">
       <p class="figure-caption">
-        Promote your app on Google Play when users are searching and browsing
-        for apps.
+        Reach users as they search for apps and games on Google Play.
       </p>
     </div>
-
     <div class="col-4of12">
       <h3>
-        From search
+        From Google Search
       </h3>
-      <img src="/images/distribute/promote_ads_search.png">
+      <img src="{@docRoot}images/distribute/promote_ads_search.png">
       <p class="figure-caption">
         Connect with users as they search for content and services provided by
         your app.
@@ -49,47 +54,32 @@
       <h3>
         From YouTube
       </h3>
-      <img src="/images/distribute/promote_ads_youtube.png">
+      <img src="{@docRoot}images/distribute/promote_ads_youtube.png">
       <p class="figure-caption">
         Promote your app when users are watching related videos.
       </p>
     </div>
-  </div>
-</div>
 
-<div class="wrap">
-  <div class="cols" style="margin-top:1em;">
     <div class="col-4of12">
       <h3>
         From apps
       </h3>
-      <img src="/images/distribute/promote_ads_apps.png">
+      <img src="{@docRoot}images/distribute/promote_ads_apps.png">
       <p class="figure-caption">
         Reach users while they’re engaged with apps and games across the AdMob
         network.
       </p>
     </div>
-
     <div class="col-4of12">
       <h3>
         From the web
       </h3>
-      <img src="/images/distribute/promote_ads_web.png">
+      <img src="{@docRoot}images/distribute/promote_ads_web.png">
       <p class="figure-caption">
         Reach users while they’re engaged with websites across the Google
         Display Network.
       </p>
     </div>
-
-    <div class="col-4of12">
-      <h3>
-        From Gmail
-      </h3>
-      <img src="/images/distribute/promote_ads_gmail.png">
-      <p class="figure-caption">
-        Promote your app while users communicate and get things done in Gmail.
-      </p>
-    </div>
   </div>
 </div>
 
@@ -130,77 +120,7 @@
   </li>
 </ul>
 
-<h2 id="engage_with_users">
-  Engage with users
-</h2>
-
-<p>
-  Getting a user to install an app is one thing, but you'll also want them to
-  open it regularly. AdWords offers app re-engagement tools to help your app
-  stay in mind with users who’ve already installed it on their phone. AdWords
-  can remind them of key features and encourage them to try your app again, or
-  help them complete an activity they didn't know your app could handle.
-</p>
-
-<div class="wrap">
-  <div class="cols" style="margin-top:1em;">
-    <div class="col-4of12">
-      <h3>
-        From search
-      </h3>
-      <img src="/images/distribute/promote_ads.png">
-      <p class="figure-caption">
-        Add deep links to your app, then bring users straight to relevant app
-        content when they’re searching.
-      </p>
-    </div>
-
-    <div class="col-4of12">
-      <h3>
-        From apps
-      </h3>
-      <img src="/images/distribute/promote_ads_inapp.png">
-      <p class="figure-caption">
-        Use remarketing and deep links to bring users to just the right place
-        in your app to re-engage and convert, from other apps and games they
-        love.
-      </p>
-    </div>
-  </div>
-</div>
-
-<h3>
-  Tips
-</h3>
-
-<ul>
-  <li>Track what users do in your app after they’ve clicked an ad, by
-  installing the AdWords <a href=
-  "https://developers.google.com/app-conversion-tracking/">conversion tracking
-  SDK</a>.
-  </li>
-
-  <li>Advertise a compelling reason for users to re-engage with your app (such
-  as a reminder or a special offer).
-  </li>
-
-  <li>
-    <a href="https://developers.google.com/app-indexing/webmasters/app">Add
-    deep links</a> to your app that’ll take users directly to the parts of your
-    app that will be most relevant and interesting to them, where they can
-    easily take action.
-  </li>
-
-  <li>Re-engage your app users across the display network with remarketing
-  lists and search with keywords.
-  </li>
-
-  <li>Use remarketing lists to target high value users so that you can drive
-  more conversions in your app.
-  </li>
-</ul>
-
-<h2 id="related-resources">Related Resources</h2>
+<h2 id="related-resources">Related resources</h2>
 
 <div class="resource-widget resource-flow-layout col-13"
   data-query="collection:distribute/users/promotewithads"
diff --git a/docs/html/guide/topics/ui/accessibility/apps.jd b/docs/html/guide/topics/ui/accessibility/apps.jd
index 90781f7..eb639e3 100644
--- a/docs/html/guide/topics/ui/accessibility/apps.jd
+++ b/docs/html/guide/topics/ui/accessibility/apps.jd
@@ -454,18 +454,20 @@
 
 <pre>
 &#64;Override
-public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-    super.dispatchPopulateAccessibilityEvent(event);
+public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
     // Call the super implementation to populate its text to the event, which
     // calls onPopulateAccessibilityEvent() on API Level 14 and up.
-
+    boolean completed = super.dispatchPopulateAccessibilityEvent(event);
+    
     // In case this is running on a API revision earlier that 14, check
     // the text content of the event and add an appropriate text
     // description for this custom view:
     CharSequence text = getText();
     if (!TextUtils.isEmpty(text)) {
         event.getText().add(text);
+        return true;
     }
+    return completed;
 }
 </pre>
 
diff --git a/docs/html/images/cards/card-drive-conversions_16-9_2x.png b/docs/html/images/cards/card-drive-conversions_16-9_2x.png
new file mode 100644
index 0000000..3448012
--- /dev/null
+++ b/docs/html/images/cards/card-drive-conversions_16-9_2x.png
Binary files differ
diff --git a/docs/html/images/distribute/nearby_beacons.png b/docs/html/images/distribute/nearby_beacons.png
new file mode 100644
index 0000000..aba2f39
--- /dev/null
+++ b/docs/html/images/distribute/nearby_beacons.png
Binary files differ
diff --git a/docs/html/images/distribute/nearby_connections.png b/docs/html/images/distribute/nearby_connections.png
new file mode 100644
index 0000000..52e6daa
--- /dev/null
+++ b/docs/html/images/distribute/nearby_connections.png
Binary files differ
diff --git a/docs/html/images/distribute/nearby_messaging.png b/docs/html/images/distribute/nearby_messaging.png
new file mode 100644
index 0000000..6ae2d00
--- /dev/null
+++ b/docs/html/images/distribute/nearby_messaging.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_apps.png b/docs/html/images/distribute/promote_ads_apps.png
index 2f57865..1c25be3 100644
--- a/docs/html/images/distribute/promote_ads_apps.png
+++ b/docs/html/images/distribute/promote_ads_apps.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_gmail.png b/docs/html/images/distribute/promote_ads_gmail.png
index 1d21b4a..c1013fc 100644
--- a/docs/html/images/distribute/promote_ads_gmail.png
+++ b/docs/html/images/distribute/promote_ads_gmail.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_play.png b/docs/html/images/distribute/promote_ads_play.png
index 1cf51b2..ae0f84b 100644
--- a/docs/html/images/distribute/promote_ads_play.png
+++ b/docs/html/images/distribute/promote_ads_play.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_search.png b/docs/html/images/distribute/promote_ads_search.png
index 27c0b38..adcede1 100644
--- a/docs/html/images/distribute/promote_ads_search.png
+++ b/docs/html/images/distribute/promote_ads_search.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_web.png b/docs/html/images/distribute/promote_ads_web.png
index 588a3d4..8fefed1 100644
--- a/docs/html/images/distribute/promote_ads_web.png
+++ b/docs/html/images/distribute/promote_ads_web.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_youtube.png b/docs/html/images/distribute/promote_ads_youtube.png
index e88a796..ad44e51 100644
--- a/docs/html/images/distribute/promote_ads_youtube.png
+++ b/docs/html/images/distribute/promote_ads_youtube.png
Binary files differ
diff --git a/docs/html/images/paypal-logo.png b/docs/html/images/paypal-logo.png
new file mode 100644
index 0000000..3e08b95
--- /dev/null
+++ b/docs/html/images/paypal-logo.png
Binary files differ
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index aa0620a..03efa86 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -419,7 +419,8 @@
       "distribute/engage/easy-signin.html",
       "distribute/analyze/build-better-apps.html",
       "distribute/engage/gcm.html",
-      "distribute/engage/beta.html"
+      "distribute/engage/beta.html",
+      "distribute/engage/nearby.html"
     ]
   },
   "distribute/monetize": {
@@ -430,6 +431,7 @@
       "distribute/monetize/ads.html",
       "distribute/monetize/ecommerce.html",
       "distribute/monetize/payments.html",
+      "distribute/monetize/conversions.html",
       "distribute/analyze/understand-user-value.html",
     ]
   },
@@ -761,6 +763,14 @@
       "https://support.google.com/adwords/answer/6167162"
     ]
   },
+  "distribute/users/nearby": {
+    "title": "",
+    "resources": [
+      "https://developers.google.com/nearby/",
+      "https://www.youtube.com/watch?v=hultDpBS22s",
+      "https://developers.google.com/beacons"
+    ]
+  },
   "distribute/users/buildbuzz": {
     "title": "",
     "resources": [
@@ -1556,6 +1566,15 @@
       "https://support.google.com/googleplay/answer/2651410"
     ]
   },
+  "distribute/monetize/conversions": {
+    "title": "",
+    "resources": [
+      "https://support.google.com/adwords/answer/2471188",
+      "https://developers.google.com/app-conversion-tracking/",
+      "https://support.google.com/analytics/answer/2611404",
+      "https://support.google.com/adwords/answer/1704341"
+    ]
+  },
   "autolanding": {
     "title": "",
     "resources": [
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index e5347d9..44ccafa 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -1942,7 +1942,7 @@
     "url": "https://support.google.com/googleplay/answer/2651410",
     "timestamp": null,
     "image": "images/play_dev.jpg",
-    "title": "Google Play Accepted Payment Methods",
+    "title": "Google Play accepted payment methods",
     "summary": "Support details on the payment methods supported in Google Play.",
     "keywords": ["gift card"],
     "type": "distribute",
@@ -1951,6 +1951,59 @@
   {
     "lang": "en",
     "group": "",
+    "tags": [],
+    "url": "https://support.google.com/adwords/answer/2471188",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "AdWords Conversion Optimizer",
+    "summary": "Learn how Conversion Optimizer works to find the users who are most likely to convert and to serve them your conversion ads.",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://developers.google.com/app-conversion-tracking/",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Track conversions with the AdWords SDK or server API",
+    "summary": "Use the lightweight AdWords app SDK or server-to-server API to track remarketing conversions.",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/analytics/answer/2611404",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Create Remarketing Audiences in Google Analytics",
+    "summary": "Learn how to use preconfigured audiences created by the Analytics team or create your own to use in your conversion campaigns.",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/adwords/answer/1704341",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Link your Google Analytics and AdWords accounts",
+    "summary": "Gain greater insight into how AdWords is driving app engagement and conversions, and use this insight to improve your ads and app.",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+
+  {
+    "lang": "en",
+    "group": "",
     "tags": ["plus", "social"],
     "url": "https://plus.google.com/+AndroidDevelopers/",
     "timestamp": null,
@@ -2719,7 +2772,6 @@
     "type": "material design",
     "titleFriendly": ""
   },
-
   {
     "lang": "en",
     "group": "",
@@ -2737,6 +2789,45 @@
     "lang": "en",
     "group": "",
     "tags": [],
+    "url": "https://developers.google.com/nearby/",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Create features based on proximity",
+    "summary": "Build simple interactions between nearby devices and people.",
+    "keywords": ["nearby", "engage"],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://www.youtube.com/watch?v=hultDpBS22s",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Use Nearby Messages to collaborate",
+    "summary": "Nearby Messages is perfect for setting up ad-hoc groups, collaborative sessions, or sharing resources with people in a co-located space.",
+    "keywords": ["nearby", "engage"],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://developers.google.com/beacons",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Mark up the world using beacons",
+    "summary": "Give your users better location and proximity experiences by providing a strong context signal for their devices in the form of Bluetooth low energy (BLE) beacons with Eddystone.",
+    "keywords": ["nearby", "engage"],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
     "url": "https://support.google.com/adwords/answer/6167164",
     "timestamp": null,
     "image": "distribute/images/advertising.jpg",
diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js
index 9cc110d..434f211 100644
--- a/docs/html/jd_extras_en.js
+++ b/docs/html/jd_extras_en.js
@@ -1943,13 +1943,65 @@
     "url": "https://support.google.com/googleplay/answer/2651410",
     "timestamp": null,
     "image": "images/cards/google-play_2x.png",
-    "title": "Google Play Accepted Payment Methods",
+    "title": "Google Play accepted payment methods",
     "summary": "Support details on the payment methods supported in Google Play.",
     "keywords": ["gift card"],
     "type": "distribute",
     "category": "google play"
   },
   {
+   "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/adwords/answer/2471188",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "AdWords Conversion Optimizer",
+    "summary": "Learn how Conversion Optimizer works to find the users who are most likely to convert and to serve them your conversion ads.",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://developers.google.com/app-conversion-tracking/",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Track conversions with the AdWords SDK or server API",
+    "summary": "Use the lightweight AdWords app SDK or server-to-server API to track remarketing conversions.",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/analytics/answer/2611404",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Create Remarketing Audiences in Google Analytics",
+    "summary": "Learn how to use preconfigured audiences created by the Analytics team or create your own to use in your conversion campaigns.",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://support.google.com/adwords/answer/1704341",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Link your Google Analytics and AdWords accounts",
+    "summary": "Gain greater insight into how AdWords is driving app engagement and conversions, and use this insight to improve your ads and app.",
+    "keywords": [],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
     "lang": "en",
     "group": "",
     "tags": ["plus", "social"],
@@ -2699,6 +2751,45 @@
     "lang": "en",
     "group": "",
     "tags": [],
+    "url": "https://developers.google.com/nearby/",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Create features based on proximity",
+    "summary": "Build simple interactions between nearby devices and people.",
+    "keywords": ["nearby", "engage"],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://www.youtube.com/watch?v=hultDpBS22s",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Use Nearby Messages to collaborate",
+    "summary": "Nearby Messages is perfect for setting up ad-hoc groups, collaborative sessions, or sharing resources with people in a co-located space.",
+    "keywords": ["nearby", "engage"],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://developers.google.com/beacons",
+    "timestamp": null,
+    "image": "images/play_dev.jpg",
+    "title": "Mark up the world using beacons",
+    "summary": "Give your users better location and proximity experiences by providing a strong context signal for their devices in the form of Bluetooth low energy (BLE) beacons with Eddystone.",
+    "keywords": ["nearby", "engage"],
+    "type": "distribute",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
     "url": "https://support.google.com/adwords/answer/6167164",
     "timestamp": null,
     "image": "distribute/images/advertising.jpg",
@@ -4160,7 +4251,8 @@
       "distribute/engage/easy-signin.html",
       "distribute/analyze/build-better-apps.html",
       "distribute/engage/gcm.html",
-      "distribute/engage/beta.html"
+      "distribute/engage/beta.html",
+      "distribute/engage/nearby.html"
     ]
   },
   "distribute/monetize": {
@@ -4171,6 +4263,7 @@
       "distribute/monetize/ads.html",
       "distribute/monetize/ecommerce.html",
       "distribute/monetize/payments.html",
+      "distribute/monetize/conversions.html",
       "distribute/analyze/understand-user-value.html",
     ]
   },
@@ -4480,6 +4573,14 @@
       "https://support.google.com/adwords/answer/6167162"
     ]
   },
+  "distribute/users/nearby": {
+    "title": "",
+    "resources": [
+      "https://developers.google.com/nearby/",
+      "https://www.youtube.com/watch?v=hultDpBS22s",
+      "https://developers.google.com/beacons"
+    ]
+  },
   "distribute/users/buildbuzz": {
     "title": "",
     "resources": [
@@ -5117,6 +5218,15 @@
       "https://support.google.com/googleplay/answer/2651410"
     ]
   },
+  "distribute/monetize/conversions": {
+     "title": "",
+     "resources": [
+       "https://support.google.com/adwords/answer/2471188",
+       "https://developers.google.com/app-conversion-tracking/",
+       "https://support.google.com/analytics/answer/2611404",
+       "https://support.google.com/adwords/answer/1704341"
+     ]
+   },
   "topic/libraries": {
     "title": "",
     "resources": [
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 137316f..405165f 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -351,115 +351,5 @@
 #endif
 }
 
-bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) {
-    Caches& caches = Caches::getInstance();
-    if (layer && layer->isRenderable()
-            && bitmap->width() <= caches.maxTextureSize
-            && bitmap->height() <= caches.maxTextureSize) {
-
-        GLuint fbo = renderState.createFramebuffer();
-        if (!fbo) {
-            ALOGW("Could not obtain an FBO");
-            return false;
-        }
-
-        SkAutoLockPixels alp(*bitmap);
-
-        GLuint texture;
-        GLuint previousFbo;
-        GLsizei previousViewportWidth;
-        GLsizei previousViewportHeight;
-
-        GLenum format;
-        GLenum type;
-
-        bool status = false;
-
-        switch (bitmap->colorType()) {
-            case kAlpha_8_SkColorType:
-                format = GL_ALPHA;
-                type = GL_UNSIGNED_BYTE;
-                break;
-            case kRGB_565_SkColorType:
-                format = GL_RGB;
-                type = GL_UNSIGNED_SHORT_5_6_5;
-                break;
-            case kARGB_4444_SkColorType:
-                format = GL_RGBA;
-                type = GL_UNSIGNED_SHORT_4_4_4_4;
-                break;
-            case kN32_SkColorType:
-            default:
-                format = GL_RGBA;
-                type = GL_UNSIGNED_BYTE;
-                break;
-        }
-
-        float alpha = layer->getAlpha();
-        SkXfermode::Mode mode = layer->getMode();
-        GLuint previousLayerFbo = layer->getFbo();
-
-        layer->setAlpha(255, SkXfermode::kSrc_Mode);
-        layer->setFbo(fbo);
-
-        previousFbo = renderState.getFramebuffer();
-        renderState.getViewport(&previousViewportWidth, &previousViewportHeight);
-        renderState.bindFramebuffer(fbo);
-
-        glGenTextures(1, &texture);
-
-        caches.textureState().activateTexture(0);
-        caches.textureState().bindTexture(texture);
-
-        glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
-
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-        glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(),
-                0, format, type, nullptr);
-
-        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                GL_TEXTURE_2D, texture, 0);
-
-        {
-            LayerRenderer renderer(renderState, layer);
-            renderer.OpenGLRenderer::prepareDirty(bitmap->width(), bitmap->height(),
-                    0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend());
-
-            renderState.scissor().setEnabled(false);
-            renderer.translate(0.0f, bitmap->height());
-            renderer.scale(1.0f, -1.0f);
-
-            {
-                Rect bounds;
-                bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
-                renderer.drawTextureLayer(layer, bounds);
-
-                glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
-                        type, bitmap->getPixels());
-
-            }
-
-            status = true;
-        }
-
-        renderState.bindFramebuffer(previousFbo);
-        layer->setAlpha(alpha, mode);
-        layer->setFbo(previousLayerFbo);
-        caches.textureState().deleteTexture(texture);
-        renderState.deleteFramebuffer(fbo);
-        renderState.setViewport(previousViewportWidth, previousViewportHeight);
-
-        GL_CHECKPOINT(MODERATE);
-
-        return status;
-    }
-    return false;
-}
-
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index 38c3705..1fb6b14 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -61,7 +61,6 @@
     static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
             bool isOpaque, bool forceFilter, GLenum renderTarget, const float* textureTransform);
     static void destroyLayer(Layer* layer);
-    static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap);
 
     static void flushLayer(RenderState& renderState, Layer* layer);
 
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 55f823d..60eadff 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -19,6 +19,7 @@
 #include "Caches.h"
 #include "Image.h"
 #include "GlopBuilder.h"
+#include "Layer.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/EglManager.h"
 #include "utils/GLUtils.h"
@@ -30,14 +31,8 @@
 namespace android {
 namespace uirenderer {
 
-CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
-        Surface& surface, SkBitmap* bitmap) {
-    // TODO: Clean this up and unify it with LayerRenderer::copyLayer,
-    // of which most of this is copied from.
-    renderThread.eglManager().initialize();
-
-    Caches& caches = Caches::getInstance();
-    RenderState& renderState = renderThread.renderState();
+static CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
+        Texture& sourceTexture, Matrix4& texTransform, SkBitmap* bitmap) {
     int destWidth = bitmap->width();
     int destHeight = bitmap->height();
     if (destWidth > caches.maxTextureSize
@@ -98,6 +93,44 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
             GL_TEXTURE_2D, texture, 0);
 
+    {
+        // Draw & readback
+        renderState.setViewport(destWidth, destHeight);
+        renderState.scissor().setEnabled(false);
+        renderState.blend().syncEnabled();
+        renderState.stencil().disable();
+
+        Glop glop;
+        GlopBuilder(renderState, caches, &glop)
+                .setRoundRectClipState(nullptr)
+                .setMeshTexturedUnitQuad(nullptr)
+                .setFillExternalTexture(sourceTexture, texTransform)
+                .setTransform(Matrix4::identity(), TransformFlags::None)
+                .setModelViewMapUnitToRect(Rect(destWidth, destHeight))
+                .build();
+        Matrix4 ortho;
+        ortho.loadOrtho(destWidth, destHeight);
+        renderState.render(glop, ortho);
+
+        glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
+                type, bitmap->getPixels());
+    }
+
+    // Cleanup
+    caches.textureState().deleteTexture(texture);
+    renderState.deleteFramebuffer(fbo);
+
+    GL_CHECKPOINT(MODERATE);
+
+    return CopyResult::Success;
+}
+
+CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
+        Surface& surface, SkBitmap* bitmap) {
+    renderThread.eglManager().initialize();
+
+    Caches& caches = Caches::getInstance();
+
     // Setup the source
     sp<GraphicBuffer> sourceBuffer;
     sp<Fence> sourceFence;
@@ -142,7 +175,7 @@
     GLuint sourceTexId;
     // Create a 2D texture to sample from the EGLImage
     glGenTextures(1, &sourceTexId);
-    Caches::getInstance().textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
+    caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
     glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, sourceImage);
 
     GLenum status = GL_NO_ERROR;
@@ -155,37 +188,13 @@
     sourceTexture.wrap(sourceTexId,
             sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
 
-    {
-        // Draw & readback
-        renderState.setViewport(destWidth, destHeight);
-        renderState.scissor().setEnabled(false);
-        renderState.blend().syncEnabled();
-        renderState.stencil().disable();
+    return copyTextureInto(caches, renderThread.renderState(), sourceTexture, texTransform, bitmap);
+}
 
-        Rect destRect(destWidth, destHeight);
-        Glop glop;
-        GlopBuilder(renderState, caches, &glop)
-                .setRoundRectClipState(nullptr)
-                .setMeshTexturedUnitQuad(nullptr)
-                .setFillExternalTexture(sourceTexture, texTransform)
-                .setTransform(Matrix4::identity(), TransformFlags::None)
-                .setModelViewMapUnitToRect(destRect)
-                .build();
-        Matrix4 ortho;
-        ortho.loadOrtho(destWidth, destHeight);
-        renderState.render(glop, ortho);
-
-        glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
-                type, bitmap->getPixels());
-    }
-
-    // Cleanup
-    caches.textureState().deleteTexture(texture);
-    renderState.deleteFramebuffer(fbo);
-
-    GL_CHECKPOINT(MODERATE);
-
-    return CopyResult::Success;
+CopyResult Readback::copyTextureLayerInto(renderthread::RenderThread& renderThread,
+        Layer& layer, SkBitmap* bitmap) {
+    return copyTextureInto(Caches::getInstance(), renderThread.renderState(),
+            layer.getTexture(), layer.getTexTransform(), bitmap);
 }
 
 } // namespace uirenderer
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index a112c42..bd73734 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -24,6 +24,8 @@
 namespace android {
 namespace uirenderer {
 
+class Layer;
+
 // Keep in sync with PixelCopy.java codes
 enum class CopyResult {
     Success = 0,
@@ -36,8 +38,18 @@
 
 class Readback {
 public:
+    /**
+     * Copies the surface's most recently queued buffer into the provided bitmap.
+     */
     static CopyResult copySurfaceInto(renderthread::RenderThread& renderThread,
             Surface& surface, SkBitmap* bitmap);
+
+    /**
+     * Copies the TextureLayer's texture content (thus, the currently rendering buffer) into the
+     * provided bitmap.
+     */
+    static CopyResult copyTextureLayerInto(renderthread::RenderThread& renderThread,
+            Layer& layer, SkBitmap* bitmap);
 };
 
 } // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 582c3ea..9aa0dea 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -25,6 +25,7 @@
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "Properties.h"
+#include "Readback.h"
 #include "RenderThread.h"
 #include "hwui/Canvas.h"
 #include "renderstate/RenderState.h"
@@ -219,6 +220,45 @@
     return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
 }
 
+bool CanvasContext::isSwapChainStuffed() {
+    if (mSwapHistory.size() != mSwapHistory.capacity()) {
+        // We want at least 3 frames of history before attempting to
+        // guess if the queue is stuffed
+        return false;
+    }
+    nsecs_t frameInterval = mRenderThread.timeLord().frameIntervalNanos();
+    auto& swapA = mSwapHistory[0];
+
+    // Was there a happy queue & dequeue time? If so, don't
+    // consider it stuffed
+    if (swapA.dequeueDuration < 3_ms
+            && swapA.queueDuration < 3_ms) {
+        return false;
+    }
+
+    for (size_t i = 1; i < mSwapHistory.size(); i++) {
+        auto& swapB = mSwapHistory[i];
+
+        // If there's a frameInterval gap we effectively already dropped a frame,
+        // so consider the queue healthy.
+        if (swapA.swapCompletedTime - swapB.swapCompletedTime > frameInterval) {
+            return false;
+        }
+
+        // Was there a happy queue & dequeue time? If so, don't
+        // consider it stuffed
+        if (swapB.dequeueDuration < 3_ms
+                && swapB.queueDuration < 3_ms) {
+            return false;
+        }
+
+        swapA = swapB;
+    }
+
+    // All signs point to a stuffed swap chain
+    return true;
+}
+
 void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
         int64_t syncQueued, RenderNode* target) {
     mRenderThread.removeFrameCallback(this);
@@ -264,7 +304,12 @@
 
     if (CC_LIKELY(mSwapHistory.size() && !Properties::forceDrawFrame)) {
         nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
-        const SwapHistory& lastSwap = mSwapHistory.back();
+        SwapHistory& lastSwap = mSwapHistory.back();
+        int durationUs;
+        mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs);
+        lastSwap.dequeueDuration = us2ns(durationUs);
+        mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs);
+        lastSwap.queueDuration = us2ns(durationUs);
         nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
         // The slight fudge-factor is to deal with cases where
         // the vsync was estimated due to being slow handling the signal.
@@ -274,15 +319,12 @@
             // Already drew for this vsync pulse, UI draw request missed
             // the deadline for RT animations
             info.out.canDrawThisFrame = false;
-        } else if (lastSwap.swapTime < latestVsync) {
+        } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos()) {
+            // It's been at least an entire frame interval, assume
+            // the buffer queue is fine
             info.out.canDrawThisFrame = true;
         } else {
-            // We're maybe behind? Find out for sure
-            int runningBehind = 0;
-            // TODO: Have this method be on Surface, too, not just ANativeWindow...
-            ANativeWindow* window = mNativeSurface.get();
-            window->query(window, NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
-            info.out.canDrawThisFrame = !runningBehind;
+            info.out.canDrawThisFrame = !isSwapChainStuffed();
         }
     } else {
         info.out.canDrawThisFrame = true;
@@ -537,7 +579,7 @@
         }
         SwapHistory& swap = mSwapHistory.next();
         swap.damage = screenDirty;
-        swap.swapTime = systemTime(CLOCK_MONOTONIC);
+        swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC);
         swap.vsyncTime = mRenderThread.timeLord().latestVsync();
         mHaveNewSurface = false;
         mFrameNumber = -1;
@@ -670,7 +712,8 @@
 
 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
     layer->apply();
-    return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
+    return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap)
+            == CopyResult::Success;
 }
 
 void CanvasContext::destroyHardwareResources(TreeObserver* observer) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 3e3c6e6..a5f8562 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -182,6 +182,8 @@
 
     void waitOnFences();
 
+    bool isSwapChainStuffed();
+
     EGLint mLastFrameWidth = 0;
     EGLint mLastFrameHeight = 0;
 
@@ -200,7 +202,9 @@
     struct SwapHistory {
         SkRect damage;
         nsecs_t vsyncTime;
-        nsecs_t swapTime;
+        nsecs_t swapCompletedTime;
+        nsecs_t dequeueDuration;
+        nsecs_t queueDuration;
     };
 
     RingBuffer<SwapHistory, 3> mSwapHistory;
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 531ea5f..da04934 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -147,6 +147,9 @@
     <!-- Margin start of the system icons super container -->
     <dimen name="system_icons_super_container_margin_start">16dp</dimen>
 
+    <!-- Margin end of the system icons super container when the avatar is missing. -->
+    <dimen name="system_icons_super_container_avatarless_margin_end">6dp</dimen>
+
     <!-- Width for the notification panel and related windows -->
     <dimen name="match_parent">-1px</dimen>
     <dimen name="standard_notification_panel_width">416dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 88fbe5f..d4f98c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.TypedValue;
@@ -59,6 +60,7 @@
     private UserSwitcherController mUserSwitcherController;
 
     private int mSystemIconsSwitcherHiddenExpandedMargin;
+    private int mSystemIconsBaseMargin;
     private View mSystemIconsContainer;
 
     public KeyguardStatusBarView(Context context, AttributeSet attrs) {
@@ -137,8 +139,11 @@
     }
 
     private void loadDimens() {
-        mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
+        Resources res = getResources();
+        mSystemIconsSwitcherHiddenExpandedMargin = res.getDimensionPixelSize(
                 R.dimen.system_icons_switcher_hidden_expanded_margin);
+        mSystemIconsBaseMargin = res.getDimensionPixelSize(
+                R.dimen.system_icons_super_container_avatarless_margin_end);
     }
 
     private void updateVisibilities() {
@@ -166,7 +171,13 @@
     private void updateSystemIconsLayoutParams() {
         RelativeLayout.LayoutParams lp =
                 (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
-        int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin : 0;
+        // If the avatar icon is gone, we need to have some end margin to display the system icons
+        // correctly.
+        int baseMarginEnd = mMultiUserSwitch.getVisibility() == View.GONE
+                ? mSystemIconsBaseMargin
+                : 0;
+        int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin :
+                baseMarginEnd;
         if (marginEnd != lp.getMarginEnd()) {
             lp.setMarginEnd(marginEnd);
             mSystemIconsSuperContainer.setLayoutParams(lp);
@@ -301,6 +312,7 @@
             mMultiUserSwitch.setAlpha(1f);
         } else {
             updateVisibilities();
+            updateSystemIconsLayoutParams();
         }
     }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index bc511ae..2c33a83 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1905,7 +1905,7 @@
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = widget.host;
         args.arg2 = widget.host.callbacks;
-        args.arg3 = updateViews;
+        args.arg3 = updateViews.clone();
         args.arg4 = requestTime;
         args.argi1 = widget.appWidgetId;
 
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index e63f536..eaf317a 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -28,6 +28,10 @@
 import android.util.Slog;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
 import android.provider.MediaStore;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -35,6 +39,7 @@
 import android.system.StructStat;
 
 import com.android.internal.app.ResolverActivity;
+import com.android.internal.os.BackgroundThread;
 
 import dalvik.system.VMRuntime;
 
@@ -64,6 +69,8 @@
 
     private final long MAX_CAMERA_PIN_SIZE = 50 * (1 << 20); //50MB max
 
+    private PinnerHandler mPinnerHandler = null;
+
 
     public PinnerService(Context context) {
         super(context);
@@ -71,6 +78,7 @@
         mContext = context;
         mShouldPinCamera = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerCameraApp);
+        mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
     }
 
     @Override
@@ -80,22 +88,8 @@
         }
         mBinderService = new BinderService();
         publishBinderService("pinner", mBinderService);
-
-        // Files to pin come from the overlay and can be specified per-device config
-        String[] filesToPin = mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_defaultPinnerServiceFiles);
-        // Continue trying to pin remaining files even if there is a failure
-        for (int i = 0; i < filesToPin.length; i++){
-            PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0);
-            if (pf != null) {
-                mPinnedFiles.add(pf);
-                if (DEBUG) {
-                    Slog.i(TAG, "Pinned file = " + pf.mFilename);
-                }
-            } else {
-                Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
-            }
-        }
+        mPinnerHandler.sendMessage(
+                mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG));
     }
 
     /**
@@ -106,27 +100,57 @@
      */
     @Override
     public void onUnlockUser(int userHandle) {
-        handlePin(userHandle);
+        mPinnerHandler.sendMessage(
+                mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0));
     }
 
     /**
-    * Pin camera on user switch.
-    * If more than one user is using the device
-    * each user may set a different preference for the camera app.
-    * Make sure that user's preference is pinned into memory.
-    */
+     * Pin camera on user switch.
+     * If more than one user is using the device
+     * each user may set a different preference for the camera app.
+     * Make sure that user's preference is pinned into memory.
+     */
     @Override
     public void onSwitchUser(int userHandle) {
-        handlePin(userHandle);
+        mPinnerHandler.sendMessage(
+                mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0));
     }
 
-    private void handlePin(int userHandle) {
+    /**
+     * Handler for on start pinning message
+     */
+    private void handlePinOnStart() {
+         // Files to pin come from the overlay and can be specified per-device config
+        String[] filesToPin = mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_defaultPinnerServiceFiles);
+        synchronized(this) {
+            // Continue trying to pin remaining files even if there is a failure
+            for (int i = 0; i < filesToPin.length; i++){
+                PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0);
+                if (pf != null) {
+                    mPinnedFiles.add(pf);
+                    if (DEBUG) {
+                        Slog.i(TAG, "Pinned file = " + pf.mFilename);
+                    }
+                } else {
+                    Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Handler for camera pinning message
+     */
+    private void handlePinCamera(int userHandle) {
         if (mShouldPinCamera) {
-            boolean success = pinCamera(userHandle);
-            if (!success) {
-                //this is not necessarily an error
-                if (DEBUG) {
-                    Slog.v(TAG, "Failed to pin camera.");
+            synchronized(this) {
+                boolean success = pinCamera(userHandle);
+                if (!success) {
+                    //this is not necessarily an error
+                    if (DEBUG) {
+                        Slog.v(TAG, "Failed to pin camera.");
+                    }
                 }
             }
         }
@@ -316,11 +340,13 @@
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
             pw.println("Pinned Files:");
-            for (int i = 0; i < mPinnedFiles.size(); i++) {
-                pw.println(mPinnedFiles.get(i).mFilename);
-            }
-            for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
-                pw.println(mPinnedCameraFiles.get(i).mFilename);
+            synchronized(this) {
+                for (int i = 0; i < mPinnedFiles.size(); i++) {
+                    pw.println(mPinnedFiles.get(i).mFilename);
+                }
+                for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
+                    pw.println(mPinnedCameraFiles.get(i).mFilename);
+                }
             }
         }
     }
@@ -336,4 +362,35 @@
              mFilename = filename;
         }
     }
+
+    final class PinnerHandler extends Handler {
+        static final int PIN_CAMERA_MSG  = 4000;
+        static final int PIN_ONSTART_MSG = 4001;
+
+        public PinnerHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+
+                case PIN_CAMERA_MSG:
+                {
+                    handlePinCamera(msg.arg1);
+                }
+                break;
+
+                case PIN_ONSTART_MSG:
+                {
+                    handlePinOnStart();
+                }
+                break;
+
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6547c45..80070d5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6678,8 +6678,7 @@
     @Override
     public void showBootMessage(final CharSequence msg, final boolean always) {
         if (Binder.getCallingUid() != Process.myUid()) {
-            // These days only the core system can call this, so apps can't get in
-            // the way of what we show about running them.
+            throw new SecurityException();
         }
         mWindowManager.showBootMessage(msg, always);
     }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 622614c..264a910 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -591,25 +591,6 @@
             mDisplayManagerInternal.initPowerManagement(
                     mDisplayPowerCallbacks, mHandler, sensorManager);
 
-            // Register for broadcasts from other components of the system.
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-            mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
-
-            filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_DREAMING_STARTED);
-            filter.addAction(Intent.ACTION_DREAMING_STOPPED);
-            mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
-
-            filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_USER_SWITCHED);
-            mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
-
-            filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_DOCK_EVENT);
-            mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
-
             // Register for settings changes.
             final ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(Settings.Secure.getUriFor(
@@ -667,6 +648,25 @@
             mDirty |= DIRTY_BATTERY_STATE;
             updatePowerStateLocked();
         }
+
+        // Register for broadcasts from other components of the system.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
+
+        filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_DREAMING_STARTED);
+        filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+        mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
+
+        filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
+
+        filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_DOCK_EVENT);
+        mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
     }
 
     private void readConfigurationLocked() {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6045694..5d85761 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -727,14 +727,6 @@
         }
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
 
-        try {
-            ActivityManagerNative.getDefault().showBootMessage(
-                    context.getResources().getText(
-                            com.android.internal.R.string.android_upgrading_starting_apps),
-                    false);
-        } catch (RemoteException e) {
-        }
-
         if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
             if (!disableNonCoreServices) {
                 traceBeginAndSlog("StartLockSettingsService");
diff --git a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
new file mode 100644
index 0000000..8480170
--- /dev/null
+++ b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.retaildemo;
+
+import android.app.AppGlobals;
+import android.app.PackageInstallObserver;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Helper class for installing preloaded APKs
+ */
+class PreloadAppsInstaller {
+    private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
+    private static String TAG = PreloadAppsInstaller.class.getSimpleName();
+    private static final String PRELOAD_APK_EXT = ".apk.preload";
+    private static boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final IPackageManager mPackageManager;
+    private final File preloadsAppsDirectory;
+
+    private final Map<String, String> mApkToPackageMap;
+
+    PreloadAppsInstaller() {
+        this(AppGlobals.getPackageManager(), Environment.getDataPreloadsAppsDirectory());
+    }
+
+    @VisibleForTesting
+    PreloadAppsInstaller(IPackageManager packageManager, File preloadsAppsDirectory) {
+        mPackageManager = packageManager;
+        mApkToPackageMap = Collections.synchronizedMap(new ArrayMap<>());
+        this.preloadsAppsDirectory = preloadsAppsDirectory;
+    }
+
+    void installApps(int userId) {
+        File[] files = preloadsAppsDirectory.listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            return;
+        }
+        for (File file : files) {
+            String apkName = file.getName();
+            if (apkName.endsWith(PRELOAD_APK_EXT) && file.isFile()) {
+                String packageName = mApkToPackageMap.get(apkName);
+                if (packageName != null) {
+                    try {
+                        installExistingPackage(packageName, userId);
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Failed to install existing package " + packageName, e);
+                    }
+                } else {
+                    try {
+                        installPackage(file, userId);
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Failed to install package from " + file, e);
+                    }
+                }
+            }
+        }
+    }
+
+    private void installExistingPackage(String packageName, int userId) {
+        if (DEBUG) {
+            Log.d(TAG, "installExistingPackage " + packageName + " u" + userId);
+        }
+        try {
+            mPackageManager.installExistingPackageAsUser(packageName, userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private void installPackage(File file, final int userId) throws IOException, RemoteException {
+        final String apkName = file.getName();
+        if (DEBUG) {
+            Log.d(TAG, "installPackage " + apkName + " u" + userId);
+        }
+        mPackageManager.installPackageAsUser(file.getPath(), new PackageInstallObserver() {
+            @Override
+            public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+                    Bundle extras) {
+                if (DEBUG) {
+                    Log.d(TAG, "Package " + basePackageName + " installed u" + userId
+                            + " returnCode: " + returnCode + " msg: " + msg);
+                }
+                if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                    mApkToPackageMap.put(apkName, basePackageName);
+                    // Install on user 0 so that the package is cached when demo user is re-created
+                    installExistingPackage(basePackageName, UserHandle.USER_SYSTEM);
+                } else if (returnCode == PackageManager.INSTALL_FAILED_ALREADY_EXISTS) {
+                    installExistingPackage(basePackageName, userId);
+                }
+            }
+        }.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME, userId);
+    }
+
+}
\ No newline at end of file
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index 2529564..04049a1 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -110,6 +110,7 @@
     private CameraManager mCameraManager;
     private String[] mCameraIdsWithFlash;
     private Configuration mSystemUserConfiguration;
+    private PreloadAppsInstaller mPreloadAppsInstaller;
 
     final Object mActivityLock = new Object();
     // Whether the newly created demo user has interacted with the screen yet
@@ -203,6 +204,7 @@
         synchronized (mActivityLock) {
             mFirstUserActivityTime = mLastUserActivityTime = SystemClock.uptimeMillis();
         }
+        mPreloadAppsInstaller = new PreloadAppsInstaller();
     }
 
     private Notification createResetNotification() {
@@ -253,6 +255,8 @@
         um.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
         Settings.Secure.putIntForUser(getContext().getContentResolver(),
                 Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id);
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Global.PACKAGE_VERIFIER_ENABLE, 0, userInfo.id);
 
         grantRuntimePermissionToCamera(userInfo.getUserHandle());
     }
@@ -458,6 +462,12 @@
         }
         MetricsLogger.count(getContext(), DEMO_SESSION_COUNT, 1);
         mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT);
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mPreloadAppsInstaller.installApps(userId);
+            }
+        });
     }
 
     private RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 794a73e..43d2a1f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -86,6 +86,9 @@
     final TreeSet<Integer> mLoadedKeyphraseIds;
     SoundTriggerInternal mSoundTriggerInternal;
 
+    private final RemoteCallbackList<IVoiceInteractionSessionListener>
+            mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
+
     public VoiceInteractionManagerService(Context context) {
         super(context);
         mContext = context;
@@ -1043,7 +1046,42 @@
         public void registerVoiceInteractionSessionListener(
                 IVoiceInteractionSessionListener listener) {
             enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
-            mImpl.registerVoiceInteractionSessionListener(listener);
+            synchronized (this) {
+                mVoiceInteractionSessionListeners.register(listener);
+            }
+        }
+
+        public void onSessionShown() {
+            synchronized (this) {
+                final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+                for (int i = 0; i < size; ++i) {
+                    final IVoiceInteractionSessionListener listener =
+                            mVoiceInteractionSessionListeners.getBroadcastItem(i);
+                    try {
+                        listener.onVoiceSessionShown();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Error delivering voice interaction open event.", e);
+                    }
+                }
+                mVoiceInteractionSessionListeners.finishBroadcast();
+            }
+        }
+
+        public void onSessionHidden() {
+            synchronized (this) {
+                final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+                for (int i = 0; i < size; ++i) {
+                    final IVoiceInteractionSessionListener listener =
+                            mVoiceInteractionSessionListeners.getBroadcastItem(i);
+                    try {
+                        listener.onVoiceSessionHidden();
+
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Error delivering voice interaction closed event.", e);
+                    }
+                }
+                mVoiceInteractionSessionListeners.finishBroadcast();
+            }
         }
 
         @Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 52e1a9b..a46ccee 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -30,7 +30,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -60,7 +59,7 @@
 
     final Context mContext;
     final Handler mHandler;
-    final Object mLock;
+    final VoiceInteractionManagerService.VoiceInteractionManagerServiceStub mServiceStub;
     final int mUser;
     final ComponentName mComponent;
     final IActivityManager mAm;
@@ -73,16 +72,13 @@
     VoiceInteractionSessionConnection mActiveSession;
     int mDisabledShowContext;
 
-    private final RemoteCallbackList<IVoiceInteractionSessionListener>
-            mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
-
     final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
                 String reason = intent.getStringExtra("reason");
                 if (!CLOSE_REASON_VOICE_INTERACTION.equals(reason) && !"dream".equals(reason)) {
-                    synchronized (mLock) {
+                    synchronized (mServiceStub) {
                         if (mActiveSession != null && mActiveSession.mSession != null) {
                             try {
                                 mActiveSession.mSession.closeSystemDialogs();
@@ -98,7 +94,7 @@
     final ServiceConnection mConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            synchronized (mLock) {
+            synchronized (mServiceStub) {
                 mService = IVoiceInteractionService.Stub.asInterface(service);
                 try {
                     mService.ready();
@@ -113,11 +109,12 @@
         }
     };
 
-    VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock,
+    VoiceInteractionManagerServiceImpl(Context context, Handler handler,
+            VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub,
             int userHandle, ComponentName service) {
         mContext = context;
         mHandler = handler;
-        mLock = lock;
+        mServiceStub = stub;
         mUser = userHandle;
         mComponent = service;
         mAm = ActivityManagerNative.getDefault();
@@ -153,8 +150,9 @@
     public boolean showSessionLocked(Bundle args, int flags,
             IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
         if (mActiveSession == null) {
-            mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName,
-                    mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid, mHandler);
+            mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
+                    mSessionComponentName, mUser, mContext, this,
+                    mInfo.getServiceInfo().applicationInfo.uid, mHandler);
         }
         List<IBinder> activityTokens = null;
         if (activityToken == null) {
@@ -358,52 +356,20 @@
         }
     }
 
-    public void registerVoiceInteractionSessionListener(
-            IVoiceInteractionSessionListener listener) {
-        synchronized (mLock) {
-            mVoiceInteractionSessionListeners.register(listener);
-        }
-    }
-
     @Override
     public void sessionConnectionGone(VoiceInteractionSessionConnection connection) {
-        synchronized (mLock) {
+        synchronized (mServiceStub) {
             finishLocked(connection.mToken, false);
         }
     }
 
     @Override
     public void onSessionShown(VoiceInteractionSessionConnection connection) {
-        synchronized (mLock) {
-            final int size = mVoiceInteractionSessionListeners.beginBroadcast();
-            for (int i = 0; i < size; ++i) {
-                final IVoiceInteractionSessionListener listener =
-                        mVoiceInteractionSessionListeners.getBroadcastItem(i);
-                try {
-                    listener.onVoiceSessionShown();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Error delivering voice interaction open event.", e);
-                }
-            }
-            mVoiceInteractionSessionListeners.finishBroadcast();
-        }
+        mServiceStub.onSessionShown();
     }
 
     @Override
     public void onSessionHidden(VoiceInteractionSessionConnection connection) {
-        synchronized (mLock) {
-            final int size = mVoiceInteractionSessionListeners.beginBroadcast();
-            for (int i = 0; i < size; ++i) {
-                final IVoiceInteractionSessionListener listener =
-                        mVoiceInteractionSessionListeners.getBroadcastItem(i);
-                try {
-                    listener.onVoiceSessionHidden();
-
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Error delivering voice interaction closed event.", e);
-                }
-            }
-            mVoiceInteractionSessionListeners.finishBroadcast();
-        }
+        mServiceStub.onSessionHidden();
     }
 }
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index 0ee9bab..318d841 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -28,6 +29,67 @@
  */
 @SystemApi
 public class ParcelableCallAnalytics implements Parcelable {
+    public static final class VideoEvent implements Parcelable {
+        public static final int SEND_LOCAL_SESSION_MODIFY_REQUEST = 0;
+        public static final int SEND_LOCAL_SESSION_MODIFY_RESPONSE = 1;
+        public static final int RECEIVE_REMOTE_SESSION_MODIFY_REQUEST = 2;
+        public static final int RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE = 3;
+
+        public static final Parcelable.Creator<VideoEvent> CREATOR =
+                new Parcelable.Creator<VideoEvent> () {
+
+                    @Override
+                    public VideoEvent createFromParcel(Parcel in) {
+                        return new VideoEvent(in);
+                    }
+
+                    @Override
+                    public VideoEvent[] newArray(int size) {
+                        return new VideoEvent[size];
+                    }
+                };
+
+        private int mEventName;
+        private long mTimeSinceLastEvent;
+        private int mVideoState;
+
+        public VideoEvent(int eventName, long timeSinceLastEvent, int videoState) {
+            mEventName = eventName;
+            mTimeSinceLastEvent = timeSinceLastEvent;
+            mVideoState = videoState;
+        }
+
+        VideoEvent(Parcel in) {
+            mEventName = in.readInt();
+            mTimeSinceLastEvent = in.readLong();
+            mVideoState = in.readInt();
+        }
+
+        public int getEventName() {
+            return mEventName;
+        }
+
+        public long getTimeSinceLastEvent() {
+            return mTimeSinceLastEvent;
+        }
+
+        public int getVideoState() {
+            return mVideoState;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(mEventName);
+            out.writeLong(mTimeSinceLastEvent);
+            out.writeInt(mVideoState);
+        }
+    }
+
     public static final class AnalyticsEvent implements Parcelable {
         public static final int SET_SELECT_PHONE_ACCOUNT = 0;
         public static final int SET_ACTIVE = 1;
@@ -250,6 +312,12 @@
     // A map from event-pair names to their durations.
     private final List<EventTiming> eventTimings;
 
+    // Whether the call has ever been a video call.
+    private boolean isVideoCall = false;
+
+    // A list of video events that have occurred.
+    private List<VideoEvent> videoEvents;
+
     public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType,
             boolean isAdditionalCall, boolean isInterrupted, int callTechnologies,
             int callTerminationCode, boolean isEmergencyCall, String connectionService,
@@ -284,6 +352,9 @@
         in.readTypedList(analyticsEvents, AnalyticsEvent.CREATOR);
         eventTimings = new ArrayList<>();
         in.readTypedList(eventTimings, EventTiming.CREATOR);
+        isVideoCall = readByteAsBoolean(in);
+        videoEvents = new LinkedList<>();
+        in.readTypedList(videoEvents, VideoEvent.CREATOR);
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -299,6 +370,15 @@
         writeBooleanAsByte(out, isCreatedFromExistingConnection);
         out.writeTypedList(analyticsEvents);
         out.writeTypedList(eventTimings);
+        writeBooleanAsByte(out, isVideoCall);
+        out.writeTypedList(videoEvents);
+    }
+    public void setIsVideoCall(boolean isVideoCall) {
+        this.isVideoCall = isVideoCall;
+    }
+
+    public void setVideoEvents(List<VideoEvent> videoEvents) {
+        this.videoEvents = videoEvents;
     }
 
     public long getStartTimeMillis() {
@@ -349,6 +429,14 @@
         return eventTimings;
     }
 
+    public boolean isVideoCall() {
+        return isVideoCall;
+    }
+
+    public List<VideoEvent> getVideoEvents() {
+        return videoEvents;
+    }
+
     @Override
     public int describeContents() {
         return 0;