Merge "Title is a CharSequence." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index c13c435..c808e04 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5632,7 +5632,6 @@
   public final class UiAutomation {
     method public void clearWindowAnimationFrameStats();
     method public boolean clearWindowContentFrameStats(int);
-    method public void destroy();
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
     method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
@@ -22881,7 +22880,7 @@
 
   public static final class TvInputInfo.Builder {
     ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
-    method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public android.media.tv.TvInputInfo build();
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
     method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
     method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 61cd00a..fa9bdb8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5779,7 +5779,6 @@
   public final class UiAutomation {
     method public void clearWindowAnimationFrameStats();
     method public boolean clearWindowContentFrameStats(int);
-    method public void destroy();
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
     method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
@@ -24583,7 +24582,7 @@
 
   public static final class TvInputInfo.Builder {
     ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
-    method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public android.media.tv.TvInputInfo build();
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
     method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
     method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo);
@@ -31780,6 +31779,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle);
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
+    method public int getUserRestrictionSource(java.lang.String, android.os.UserHandle);
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
@@ -31817,6 +31817,13 @@
     field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
     field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
     field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
+    field public static final int RESTRICTION_NOT_SET = 0; // 0x0
+    field public static final int RESTRICTION_SOURCE_SYSTEM = 1; // 0x1
+    field public static final int RESTRICTION_SOURCE_DEVICE_OWNER = 2; // 0x2
+    field public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 4; // 0x4
+  }
+
+  public static abstract class UserManager.UserRestrictionSource implements java.lang.annotation.Annotation {
   }
 
   public abstract class Vibrator {
diff --git a/api/test-current.txt b/api/test-current.txt
index 9165cb0..d572c8d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -22949,7 +22949,7 @@
 
   public static final class TvInputInfo.Builder {
     ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName);
-    method public android.media.tv.TvInputInfo build() throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public android.media.tv.TvInputInfo build();
     method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean);
     method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle);
     method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 19cb927..4470eda 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -301,14 +301,13 @@
 
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
-            public int send(int code, Intent intent, String resolvedType,
+            public void send(int code, Intent intent, String resolvedType,
                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                 try {
                     mResult.offer(intent, 5, TimeUnit.SECONDS);
                 } catch (InterruptedException e) {
                     throw new RuntimeException(e);
                 }
-                return 0;
             }
         };
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a4f404f..6ba6b0f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2977,6 +2977,22 @@
             reply.writeNoException();
             return true;
         }
+        case SEND_INTENT_SENDER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IIntentSender sender = IIntentSender.Stub.asInterface(data.readStrongBinder());
+            int scode = data.readInt();
+            Intent intent = data.readInt() != 0 ? Intent.CREATOR.createFromParcel(data) : null;
+            String resolvedType = data.readString();
+            IIntentReceiver finishedReceiver = IIntentReceiver.Stub.asInterface(
+                    data.readStrongBinder());
+            String requiredPermission = data.readString();
+            Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null;
+            int result = sendIntentSender(sender, scode, intent, resolvedType, finishedReceiver,
+                    requiredPermission, options);
+            reply.writeNoException();
+            reply.writeInt(result);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -6973,5 +6989,37 @@
         reply.recycle();
     }
 
+    public int sendIntentSender(IIntentSender target, int code, Intent intent, String resolvedType,
+            IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(target.asBinder());
+        data.writeInt(code);
+        if ((intent!=null)) {
+            data.writeInt(1);
+            intent.writeToParcel(data, 0);
+        }
+        else {
+            data.writeInt(0);
+        }
+        data.writeString(resolvedType);
+        data.writeStrongBinder((((finishedReceiver!=null))?(finishedReceiver.asBinder()):(null)));
+        data.writeString(requiredPermission);
+        if ((options!=null)) {
+            data.writeInt(1);
+            options.writeToParcel(data, 0);
+        }
+        else {
+            data.writeInt(0);
+        }
+        mRemote.transact(SEND_INTENT_SENDER_TRANSACTION, data, reply, 0);
+        final int res = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 2fcad0d..81788da 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -653,6 +653,10 @@
 
     public void startConfirmDeviceCredentialIntent(Intent intent) throws RemoteException;
 
+    public int sendIntentSender(IIntentSender target, int code, Intent intent, String resolvedType,
+            IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
+            throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -1038,4 +1042,5 @@
     int NOTIFY_LOCKED_PROFILE = IBinder.FIRST_CALL_TRANSACTION + 373;
     int START_CONFIRM_DEVICE_CREDENTIAL_INTENT = IBinder.FIRST_CALL_TRANSACTION + 374;
     int SEND_IDLE_JOB_TRIGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 375;
+    int SEND_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 376;
 }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 412b098..cb15392 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -803,7 +803,8 @@
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
-            int res = mTarget.send(code, intent, resolvedType,
+            int res = ActivityManagerNative.getDefault().sendIntentSender(
+                    mTarget, code, intent, resolvedType,
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
                             : null,
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 5ec59b4..0d4d007 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -322,7 +322,10 @@
     /**
      * Destroy this UiAutomation. After calling this method, attempting to use the object will
      * result in errors.
+     *
+     * @hide
      */
+    @TestApi
     public void destroy() {
         disconnect();
         mIsDestroyed = true;
diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl
index f3affa7..45c62d4 100644
--- a/core/java/android/content/IIntentSender.aidl
+++ b/core/java/android/content/IIntentSender.aidl
@@ -21,7 +21,7 @@
 import android.os.Bundle;
 
 /** @hide */
-interface IIntentSender {
-    int send(int code, in Intent intent, String resolvedType,
+oneway interface IIntentSender {
+    void send(int code, in Intent intent, String resolvedType,
             IIntentReceiver finishedReceiver, String requiredPermission, in Bundle options);
 }
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 13a767e..32ca6c2 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -17,10 +17,6 @@
 package android.content;
 
 import android.app.ActivityManagerNative;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IIntentSender;
-import android.content.IIntentReceiver;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.Handler;
@@ -191,7 +187,8 @@
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
-            int res = mTarget.send(code, intent, resolvedType,
+            int res = ActivityManagerNative.getDefault().sendIntentSender(mTarget,
+                    code, intent, resolvedType,
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
                             : null,
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index 812f1fe..93463b4 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -23,5 +23,6 @@
     void onMeteredIfacesChanged(in String[] meteredIfaces);
     void onRestrictBackgroundChanged(boolean restrictBackground);
     void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted);
+    void onRestrictBackgroundBlacklistChanged(int uid, boolean blacklisted);
 
 }
diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java
index 2e586b3..fd465d9 100644
--- a/core/java/android/net/UidRange.java
+++ b/core/java/android/net/UidRange.java
@@ -48,6 +48,17 @@
         return start / PER_USER_RANGE;
     }
 
+    public boolean contains(int uid) {
+        return start <= uid && uid <= stop;
+    }
+
+    /**
+     * @return {@code true} if this range contains every UID contained by the {@param other} range.
+     */
+    public boolean containsRange(UidRange other) {
+        return start <= other.start && other.stop <= stop;
+    }
+
     @Override
     public int hashCode() {
         int result = 17;
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 8406bcf..41ff9fd 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -392,6 +392,7 @@
 
     /**
      * A callback that is invoked when a tag is removed from the field.
+     * @see NfcAdapter#ignore
      */
     public interface OnTagRemovedListener {
         void onTagRemoved();
diff --git a/core/java/android/nfc/cardemulation/HostNfcFService.java b/core/java/android/nfc/cardemulation/HostNfcFService.java
index 1d4baf4..27c4976 100644
--- a/core/java/android/nfc/cardemulation/HostNfcFService.java
+++ b/core/java/android/nfc/cardemulation/HostNfcFService.java
@@ -42,8 +42,13 @@
  * exactly one System Code and one NFCID2. For details about the use of
  * System Code and NFCID2, see the NFC Forum Digital specification.</p>
  * <p>To statically register a System Code and NFCID2 with the service, a {@link #SERVICE_META_DATA}
- * entry must be included in the declaration of the service. An example of a HostNfcFService
- * manifest declaration is shown below:
+ * entry must be included in the declaration of the service.
+ *
+ * <p>All {@link HostNfcFService HostNfcFService} declarations in the manifest must require the
+ * {@link android.Manifest.permission#BIND_NFC_SERVICE} permission
+ * in their &lt;service&gt; tag, to ensure that only the platform can bind to your service.</p>
+ *
+ * <p>An example of a HostNfcFService manifest declaration is shown below:
  *
  * <pre> &lt;service android:name=".MyHostNfcFService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"&gt;
  *     &lt;intent-filter&gt;
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 55b0d2a..b27cb32 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -58,6 +58,7 @@
     boolean canHaveRestrictedProfile(int userHandle);
     int getUserSerialNumber(int userHandle);
     int getUserHandle(int userSerialNumber);
+    int getUserRestrictionSource(String restrictionKey, int userHandle);
     Bundle getUserRestrictions(int userHandle);
     boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
     boolean hasUserRestriction(in String restrictionKey, int userHandle);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1948df9..dcec982 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.accounts.AccountManager;
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -43,6 +44,8 @@
 import com.android.internal.R;
 
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -63,6 +66,41 @@
     private final Context mContext;
 
     /**
+     * @hide
+     * No user restriction.
+     */
+    @SystemApi
+    public static final int RESTRICTION_NOT_SET = 0x0;
+
+    /**
+     * @hide
+     * User restriction set by system/user.
+     */
+    @SystemApi
+    public static final int RESTRICTION_SOURCE_SYSTEM = 0x1;
+
+    /**
+     * @hide
+     * User restriction set by a device owner.
+     */
+    @SystemApi
+    public static final int RESTRICTION_SOURCE_DEVICE_OWNER = 0x2;
+
+    /**
+     * @hide
+     * User restriction set by a profile owner.
+     */
+    @SystemApi
+    public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 0x4;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag=true, value={RESTRICTION_NOT_SET, RESTRICTION_SOURCE_SYSTEM,
+            RESTRICTION_SOURCE_DEVICE_OWNER, RESTRICTION_SOURCE_PROFILE_OWNER})
+    @SystemApi
+    public @interface UserRestrictionSource {}
+
+    /**
      * Specifies if a user is disallowed from adding and removing accounts, unless they are
      * {@link android.accounts.AccountManager#addAccountExplicitly programmatically} added by
      * Authenticator.
@@ -1023,6 +1061,27 @@
     }
 
     /**
+     * @hide
+     *
+     * Returns who set a user restriction on a user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * @param restrictionKey the string key representing the restriction
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @return The source of user restriction. Any combination of {@link #RESTRICTION_NOT_SET},
+     *         {@link #RESTRICTION_SOURCE_SYSTEM}, {@link #RESTRICTION_SOURCE_DEVICE_OWNER}
+     *         and {@link #RESTRICTION_SOURCE_PROFILE_OWNER}
+     */
+    @SystemApi
+    @UserRestrictionSource
+    public int getUserRestrictionSource(String restrictionKey, UserHandle userHandle) {
+        try {
+            return mService.getUserRestrictionSource(restrictionKey, userHandle.getIdentifier());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the user-wide restrictions imposed on this user.
      * @return a Bundle containing all the restrictions.
      */
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 8cc8509..594581a 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -899,6 +899,8 @@
                 mOverflowPanel.setAlpha(1);
                 mOverflowPanel.setVisibility(View.VISIBLE);
                 mOverflowButton.setImageDrawable(mArrow);
+                mOverflowButton.setContentDescription(mContext.getString(
+                        R.string.floating_toolbar_close_overflow_description));
 
                 // Update x-coordinates depending on RTL state.
                 if (isRTL()) {
@@ -940,6 +942,8 @@
                 mOverflowPanel.setAlpha(0);
                 mOverflowPanel.setVisibility(View.INVISIBLE);
                 mOverflowButton.setImageDrawable(mOverflow);
+                mOverflowButton.setContentDescription(mContext.getString(
+                        R.string.floating_toolbar_open_overflow_description));
 
                 if (hasOverflow()) {
                     // Update x-coordinates depending on RTL state.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ac36a2f..9b7fbd8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2322,6 +2322,8 @@
   <java-symbol type="layout" name="floating_popup_overflow_list_item" />
   <java-symbol type="layout" name="floating_popup_overflow_image_list_item" />
   <java-symbol type="layout" name="floating_popup_overflow_button" />
+  <java-symbol type="string" name="floating_toolbar_open_overflow_description" />
+  <java-symbol type="string" name="floating_toolbar_close_overflow_description" />
   <java-symbol type="dimen" name="floating_toolbar_height" />
   <java-symbol type="dimen" name="floating_toolbar_menu_button_side_padding" />
   <java-symbol type="dimen" name="floating_toolbar_overflow_side_padding" />
diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd
index 2203f71..a1135bf 100644
--- a/docs/html/google/play/billing/billing_admin.jd
+++ b/docs/html/google/play/billing/billing_admin.jd
@@ -26,193 +26,164 @@
 </div>
 
 <p>In-app billing frees you from processing financial transactions, but you still need to perform a
-few administrative tasks, including setting up and maintaining your product list on the Google Play
-Developer Console, registering test accounts, and handling refunds when necessary.</p>
+few administrative tasks. These tasks include the following:</p>
+<ul>
+  <li>Setting up and maintaining your product list on the Google Play Developer Console.</li>
+  <li>Registering test accounts.</li>
+  <li>Handling refunds when necessary.</li>
+</ul>
+</p>
 
-<p>You must have a Google Play publisher account to register test accounts. And you must have a
-Google payments merchant account to create a product list and issue refunds to your users. If you
+<p>To register a test account, you must have a Google Play publisher account. If you
 already have a publisher account on Google Play, you can use your existing account. You do not
-need to register for a new account to support in-app billing.</p>
+need to register for a new account to support in-app billing. If you don't have a publisher
+account, you can register as a Google Play developer and set up a publisher account through the <a
+href="http://play.google.com/apps/publish">Google Play Developer Console</a>.</p>
 
-<p>If you do not have a publisher account, you can register as a Google Play
-developer and set up a publisher account at the <a
-href="http://play.google.com/apps/publish">Google Play Developer Console</a>. If you do not
-have a Google payments merchant account, you can register for one through the
-Developer Console.</p>
+<p>If you want to create a product list and issue refunds to your users, you must have a
+Google payments merchant account. If you don't have a merchant account, you can
+register for one through the Developer Console.</p>
 
 <h2 id="billing-list-setup">Creating a Product List</h2>
 
 <p>The Google Play Developer Console provides a product list for each of your published
-applications. You can sell an item using Google Play's in-app billing feature only if the item is
-listed on an application's product list. Each application has its own product list; you cannot sell
-items that appear on another application's product list.</p>
+apps. You can sell an item using Google Play's in-app billing feature only if the item is
+listed on an app's product list. Each app has its own product list; you cannot sell
+items that appear on another app's product list.</p>
 
-<div class="figure-right">
-  <figure id="fig-iap">
-    <img src="{@docRoot}images/in-app-billing/in_app_products.png" width="700"
-    alt="The Mythical Journey app lists two in-app products, Invisibility Potion and Sleeping Potion.">
-    <figcaption>
-      <b>Figure 1. </b>You can access an application's product list by
-      selecting the <strong>In-app Products</strong> link in the main Apps
-      navigation.
-    </figcaption>
-  </figure>
-</div>
+<p>You can access an app's product list by opening the <strong>In-app Products</strong>
+page for an app that is listed in your developer account. The link to the
+<strong>In-app Products</strong> page appears only if you have a Google payments merchant
+account and the app's manifest includes the
+<code>com.android.vending.BILLING</code> permission. For more information about this
+permission, see <a href="{@docRoot}google/play/billing/billing_integrate.html#billing-permission">
+Updating Your App's Manifest</a>.</p>
 
-<p>You can access an application's product list by clicking the <strong>In-App Products</strong>
-link in applications listed in your developer account (see
-figure 1). The <strong>In-App Products</strong> link appears only if you have a Google payments
-merchant account and the application's manifest includes the <code>com.android.vending.BILLING</code>
-permission.</p>
+<p>A product list specifies items you are selling in an app: in-app products,
+subscriptions, or a combination of both. For each item, the product list contains information
+such as product ID, product description, and price. You can create a product list for any
+published app, including apps published to the alpha and beta channels.</p>
 
-<p>A product list specifies items you are selling in an application &mdash; in-app products,
-subscriptions, or a combination of both. For each item, the product list contains information such as a product id,
-product description, and price. The product list stores only metadata about the items
-you are selling in your application. It does not store any digital content. You are responsible for
-storing and delivering the digital content that you sell in your applications.</p>
-
-<p>You can create a product list for any published application, or any
-application in the alpha or beta channels, that's been
-uploaded and saved to the Developer Console. However, you must have a Google payments merchant
-account and the application's manifest must include the <code>com.android.vending.BILLING</code>
-permission. If an application's manifest does not include this permission, you will be able to edit
-existing items in the product list, but you won't be able to add new items to the list. For more
-information about this permission, see
-<a href="{@docRoot}google/play/billing/billing_integrate.html#billing-permission">Updating Your
-Application's Manifest</a>.</p>
+<p>The product list stores only metadata about the items you are selling in your app.
+It does not store any digital content. You are responsible for storing and delivering
+the digital content that you sell in your apps.</p>
 
 <p class="note"><strong>Note:</strong> Previously, you could test an app by
-uploading an unpublished "draft" version. This functionality is no longer
+uploading an unpublished draft version. This functionality is no longer
 supported; instead, you must publish it to the alpha or beta distribution
 channel. For more information, see <a
 href="{@docRoot}google/play/billing/billing_testing.html#draft_apps">Draft Apps
 are No Longer Supported</a>.
 
-<p>In addition, an application package can have only one product list. If you create a product
-list for an application, and you use the <a
+<p>In addition, an app package can have only one product list. If you create a product
+list for an app, and you use the <a
 href="{@docRoot}google/play/publishing/multiple-apks.html">multiple APK feature</a> to distribute
-more than one APK for that application, the product list applies to all APK versions that are
-associated with the application listing. You cannot create individual product lists for each APK if
+more than one APK for that app, the product list applies to all APK versions that are
+associated with the app listing. You cannot create individual product lists for each APK if
 you are using the multiple APK feature.</p>
 
-<p>You can add items to a product list two ways: you can add items one at a time by using the In-app
-Products UI (see figure 2), or you can add a batch of items by importing the items from a
+<p>You can add items to a product list two ways: you can add items one at a time on the <strong>In-app
+Products</strong> page, or you can add a batch of items by importing the items from a
 comma-separated values (CSV) file. Adding items one at a time is useful if your
-application has only a few in-app items or you are adding only a few items to a
-product list for testing purposes. The CSV file method is useful if your application has a large
+app has only a few in-app items or you are adding only a few items to a
+product list for testing purposes. The CSV file method is useful if your app has a large
 number of in-app items.</p>
 
-<p class="note"><strong>Note:</strong> Batch upload of product lists containing subscriptions is not yet supported.
-Also, you cannot perform a batch upload containing changes to in-app products that are linked to a
+<p class="note"><strong>Note:</strong> Batch upload of product lists containing
+  subscriptions is not supported. Also, when updating existing items in a batch upload,
+  you cannot include changes to in-app products that are linked to a
 <a href="#pricing-template">pricing template</a>.</p>
 
 <h3 id="billing-form-add">Adding items one at a time to a product list</h3>
 
-<p>To add an item to a product list using the In-app Products UI, follow these steps:</p>
+<p>To add an item to a product list using the Developer Console UI, follow these steps:</p>
 
 <ol>
   <li><a href="http://play.google.com/apps/publish">Log in</a> to your publisher account.</li>
-  <li>In the <strong>All Applications</strong> panel, click on the
-  app name, then select <strong>In-app Products</strong>.</li>
-  <li>Click <strong>Add new product</strong> (see figure 2) and provide the product type and ID for the item you are
-  selling. Click <strong>Continue</strong>.</li>
-  <li>Enter additional information about the item, then click <strong>Save</strong> or <strong>Publish</strong>.
+  <li>In the <strong>All applications</strong> panel, click on the
+  app name, then open the <strong>In-app Products</strong> page.</li>
+  <li><p>Click <strong>Add new product</strong>. After you provide the product type and ID for the item you are
+  selling, click <strong>Continue</strong>.</p>
+  <dl>
+      <dt>Product Type</dt>
+      <dd>
+        <p>The product type can be <strong>Managed product</strong> or <strong>Subscription</strong>. You cannot
+        change an item's product type after you create the item. For more information, see
+        <a href="#billing-purchase-type">Choosing a Product Type</a>.</p>
+        <p class="note"><strong>Note: </strong>For subscription items, you cannot change the
+        item's price once you have published the item.</p>
+      </dd>
+      <dt>Product ID</dt>
+      <dd>
+        <p>Product IDs are unique across an app's namespace. A product ID must start with a
+        lowercase letter or a number and must be composed of only lowercase letters (a-z), numbers
+        (0-9), underscores (_), and periods (.). The product ID <code>android.test</code> is reserved, as are all
+        product IDs that start with <code>android.test</code>.</p>
+        <p class="note"><strong>Note: </strong>Be sure to plan your product ID namespace carefully. You
+        cannot modify an item's product ID after the item is created, and you cannot reuse
+        a product ID within an app.</p>
+      </dd>
+    </dl>
+  </li>
+  <li><p>Enter additional information about the item, then click <strong>Save</strong>.</p>
+    <dl>
+      <dt>Publishing State</dt>
+      <dd>
+        <p>An item's publishing state can be <strong>Active</strong> or
+        <strong>Inactive</strong>. To be visible to a user during checkout, an item's publishing state must be set to
+        <strong>Active</strong>, and the item's app must be published on Google Play.</p>
+        <p class="note"><strong>Note:</strong> If you're using a test account, users can see active items
+        within unpublished apps, as well. For more information, see <a
+        href="{@docRoot}google/play/billing/billing_testing.html#billing-testing-real">Testing In-app
+        Billing</a>.</p>
+      </dd>
+      <dt>Languages and Translations</dt>
+      <dd>
+        <p>By default, in-app products inherit their default language from the parent app.</p>
+        <p>You can provide localized titles and descriptions for your in-app
+        products by selecting <strong>Add Translations</strong>. If you want Google
+        Play to translate your title and description for you, based on the title and
+        description in the default language, just choose the languages that you
+        want to offer. You can also provide custom translations in specific
+        languages.</p>
+      </dd>
+      <dt>Title</dt>
+      <dd>
+        The title is a short descriptor for the item. An example of a title is: "Sleeping potion."
+        Every item must have a title. The title is visible to users during checkout. For optimum appearance,
+        titles should be no longer than 25 characters; however, titles can be up to 55 characters in length.
+      </dd>
+      <dt>Description</dt>
+      <dd>
+        The description is a long descriptor for the item. An example of a description is:
+        "Instantly puts creatures to sleep. Does not work on angry elves." Every item must have a description.
+        Descriptions can be up to 80 characters in length.
+      </dd>
+      <dt>Price</dt>
+      <dd>
+        <p>Provide a price in your home currency, or link the price to an existing
+        pricing template. Based on the price you enter or the prices
+        from the pricing template, the system autofills country-specific prices for
+        different currencies. These generated prices use current exchange rates and
+        locally relevant pricing patterns (see figure 1).</p>
+        <p>You can also change prices for other currencies manually, but you can do
+          this only if a currency is used in one of the target countries for your
+          app. You can specify target countries for your app on the
+          <strong>Pricing &amp; Distribution</strong> page in the Google Play
+          Developer Console.</p>
+      </dd>
+    </dl>
+  </li>
 </ol>
 
-<div class="figure-right">
-  <figure id="fig-anp">
-    <img src="{@docRoot}images/in-app-billing/add_new_product.png" width="300"
-    alt="Adding a managed product with a Product ID of basic_sleeping_potion.">
-    <figcaption>
-      <b>Figure 2. </b>The <em>Add New Product</em> page lets you
-      provide basic information about a paid app or in-app product.
-    </figcaption>
-  </figure>
-</div>
-
-<div class="figure-right">
-  <figure id="fig-nmp">
-    <img src="{@docRoot}images/in-app-billing/new_managed_product.png" width="700"
-    alt="">
-    <figcaption>
-      <b>Figure 3. </b>The <em>New Managed Product</em> page lets you finish
-      adding items to an app’s product list.
-    </figcaption>
-  </figure>
-</div>
-
-<div class="figure-right">
-  <figure id="fig-elp">
-    <img src="{@docRoot}images/in-app-billing/edit_local_prices.png" width="700"
-    alt="An item that costs 1.99 in USD usually costs a different amount in AUD,
-    EUR, or BOB. Some countries also add tax to the price.">
-    <figcaption>
-      <b>Figure 4. </b>Specifying additional currencies for an in-app product.
-    </figcaption>
-  </figure>
-</div>
-
-<p>You must enter the following information for each item in a product list (see
-  figures 2 and 3):</p>
-<ul>
-  <li><strong>In-app Product ID</strong>
-    <p>Product IDs are unique across an application's namespace. A product ID must start with a
-    lowercase letter or a number, and must be composed using only lowercase letters (a-z), numbers
-    (0-9), underscores (_), and dots (.). The product ID <code>"android.test"</code> is reserved, as are all
-    product IDs that start with <code>"android.test"</code>.</p>
-    <p class="note"><strong>Note: </strong>Be sure to plan your product ID namespace carefully. You
-    cannot modify an item's product ID after it is created, and you cannot reuse
-    a product ID.</p>
-  </li>
-  <li><strong>Product Type</strong>
-    <p>The product type can be <strong>Managed product</strong> or <strong>Subscription</strong>. You cannot
-    change an item's product type after you set it. For more information, see
-    <a href="#billing-purchase-type">Choosing a product type</a>.</p>
-    <p class="note"><strong>Note: </strong>For subscription items, note that you cannot change the
-    item's price once you have published it.</p>
-  </li>
-  <li><strong>Publishing State</strong>
-    <p>An item's publishing state can be <strong>Published</strong> or <strong>Unpublished
-    </strong>. To be visible to a user during checkout, an item's publishing state must be set to
-    <strong>Published</strong>, and the item's application must be published on Google Play.</p>
-    <p class="note"><strong>Note:</strong> This is not true for test accounts. An item is visible to
-    a test account if the application is not published and the item is published. See <a
-    href="{@docRoot}google/play/billing/billing_testing.html#billing-testing-real">Testing In-app
-    Billing</a> for more information.</p>
-  </li>
-  <li><strong>Languages and Translations</strong>
-    <p>You can provide localized titles and descriptions for your in-app
-    products by selecting <strong>Add Translations</strong>. If you want Google
-    Play to translate your title and description for you, based on the title and
-    description in the default language, just choose the languages that you
-    want to offer. If you want to provide custom translations in specific
-    languages, you can also do that. By default, an in-app product inherits its
-    default language from the parent application.</p>
-  </li>
-  <li><strong>Title</strong>
-    <p>The title is a short descriptor for the item. For example, "Sleeping potion."
-    Every item must have a title. The title is visible to
-    users during checkout. For optimum appearance, titles should be no longer than 25 characters;
-    however, titles can be up to 55 characters in length.</p>
-  </li>
-  <li><strong>Description</strong>
-    <p>The description is a long descriptor for the item. For example, "Instantly puts creatures to
-    sleep. Does not work on angry elves." Every item must have a description. Descriptions can be
-    up to 80 characters in length.</p>
-  </li>
-  <li><strong>Price</strong>
-    <p>Provide a price in your home currency, or link the price to an existing
-    pricing template (see figure 4). Based on the price you enter or the prices
-    from the pricing template, the system autofills country-specific prices for
-    different currencies. These generated prices use today's exchange rates and
-    locally-relevant pricing patterns.</p>
-    <p>You can also change prices for other currencies manually, but you can do
-      this only if a currency is used in one of the target countries for your
-      application. You can specify target countries for your app on the
-      <strong>Pricing &amp; Distribution</strong> page in the Google Play
-      Developer Console.</p>
-  </li>
-</ul>
+<figure id="fig-elp">
+  <img class="border-img" src="{@docRoot}images/in-app-billing/edit_local_prices.png"
+  width="700" alt="An item that costs 1.99 in USD usually costs a different
+  amount in AUD, EUR, or BOB. Some countries also add tax to the price.">
+  <figcaption>
+    <b>Figure 1. </b>Specifying additional currencies for an in-app product.
+  </figcaption>
+</figure>
 
 <h3 id="billing-bulk-add">Adding a batch of items to a product list</h3>
 
@@ -227,18 +198,16 @@
 do not use auto-fill, prices you provide must include tax.</p>
 
 <p class="note"><strong>Note:</strong> Batch upload of product lists containing
-subscriptions is not yet supported. Also, you cannot perform a batch upload
-containing changes to in-app products that are linked to a
+subscriptions is not supported. Also, when updating existing items in a batch
+upload, you cannot include changes to in-app products that are linked to a
 <a href="#pricing-template">pricing template</a>.</p>
 
-
-
 <p>To import the items that are specified in your CSV file, do the following:</p>
 
 <ol>
   <li><a href="http://play.google.com/apps/publish">Log in</a> to your publisher account.</li>
-  <li>In the <strong>All Applications</strong> panel, click on the app
-  name, then select <strong>In-app Products</strong>.</li>
+  <li>In the <strong>All applications</strong> panel, select the app
+  name, then open the <strong>In-app Products</strong> page.</li>
   <li>On the In-app Products List page, click <strong>Import/Export</strong>
   &gt; <strong>Import in-app products from CSV file</strong>, then select your
   CSV file.
@@ -260,8 +229,8 @@
 
 <h4 id="billing-bulk-format">Formatting batches of items</h4>
 
-<p>The CSV file uses commas (,) and semi-colons (;) to separate data values.
-Commas are used to separate primary data values, and semi-colons are used to
+<p>The CSV file uses commas (,) and semicolons (;) to separate data values.
+Commas are used to separate primary data values, and semicolons are used to
 separate subvalues. For example, the syntax for the CSV file is as follows:</p>
 
 <p>"<em>product_id</em>","<em>publish_state</em>","<em>purchase_type</em>","<em>autotranslate</em>
@@ -271,86 +240,108 @@
 
 <p>Descriptions and usage details are provided below.</p>
 
-<ul>
-  <li><em>product_id</em>
-    <p>This is equivalent to the In-app Product ID setting in the In-app Products UI. If you specify
+<dl>
+  <dt>product_id</dt>
+  <dd>
+    This is equivalent to the In-app Product ID setting in the In-app Products UI. If you specify
     a <em>product_id</em> that already exists in a product list, and you choose to overwrite
     the product list while importing the CSV file, the data for the existing item is overwritten with
     the values specified in the CSV file. The overwrite feature does not delete items that are on a
-    product list but not present in the CSV file.</p>
-  </li>
-  <li><em>publish_state</em>
-    <p>This is equivalent to the Publishing State setting in the In-app Products UI. Can be <code>
-    published</code> or <code>unpublished</code>.</p>
-  </li>
-  <li><em>purchase_type</em>
-    <p>This is equivalent to the Product Type setting in the In-app Products UI. Can be <code>
+    product list but not present in the CSV file.
+  </dd>
+  <dt>publish_state</dt>
+  <dd>
+    This is equivalent to the Publishing State setting in the In-app Products UI. Can be <code>
+    published</code> or <code>unpublished</code>.
+  </dd>
+  <dt>purchase_type</dt>
+  <dd>
+    This is equivalent to the Product Type setting in the In-app Products UI. Can be <code>
     managed_by_android</code>, which is equivalent to <strong>Managed per user account
     </strong> in the In-app Products UI, or <code>managed_by_publisher</code>, which is equivalent
-    to <strong>Unmanaged</strong> in the In-app Products UI.</p>
-  </li>
-  <li><em>autotranslate</em>
-    <p>This is equivalent to selecting the <strong>Fill fields with auto translation</strong>
-    checkbox in the In-app Products UI. Can be <code>true</code> or <code>false</code>.</p>
-  </li>
-  <li><em>locale</em>
+    to <strong>Unmanaged</strong> in the In-app Products UI.
+  </dd>
+  <dt>autotranslate</dt>
+  <dd>
+    This is equivalent to selecting the <strong>Fill fields with auto translation</strong>
+    checkbox in the In-app Products UI. Can be <code>true</code> or <code>false</code>.
+  </dd>
+  <dt>locale</dt>
+  <dd>
     <p>This is equivalent to the Language setting in the In-app Products UI. You must have an entry
     for the default locale. The default locale must be the first entry in the list of
     locales, and it must include a <em>title</em> and <em>description</em>. If you want to provide
     translated versions of the <em>title</em> and <em>description</em> in addition to the default,
     you must use the following syntax rules:</p>
-    <p>If <em>autotranslate</em> is <code>true</code>, you must specify the default locale,
-    default title, default description, and other locales using the following format:</p>
-    <p>"true,"<em>default_locale</em>; <em>default_locale_title</em>;
-    <em>default_locale_description</em>; <em>locale_2</em>;    <em>locale_3</em>, ..."</p>
-    <p>If <em>autotranslate</em> is <code>false</code>, you must specify the default locale,
-    default title, and default description as well as the translated titles and descriptions using
-    the following format:</p>
-    <p>"false,"<em>default_locale</em>; <em>default_locale_title</em>;
-    <em>default_locale_description</em>; <em>locale_2</em>; <em>locale_2_title</em>;
-    <em>local_2_description</em>; <em>locale_3</em>; <em>locale_3_title</em>;
-     <em>locale_3_description</em>; ..."</p>
+    <ul>
+      <li>
+      <p>If <em>autotranslate</em> is <code>true</code>, you must specify the default locale,
+      default title, default description, and other locales using the following format:</p>
+      <p>"true,"<em>default_locale</em>; <em>default_locale_title</em>;
+      <em>default_locale_description</em>; <em>locale_2</em>;    <em>locale_3</em>, ..."</p>
+      </li>
+      <li>
+      <p>If <em>autotranslate</em> is <code>false</code>, you must specify the default locale,
+      default title, and default description as well as the translated titles and descriptions using
+      the following format:</p>
+      <p>"false,"<em>default_locale</em>; <em>default_locale_title</em>;
+      <em>default_locale_description</em>; <em>locale_2</em>; <em>locale_2_title</em>;
+      <em>local_2_description</em>; <em>locale_3</em>; <em>locale_3_title</em>;
+       <em>locale_3_description</em>; ..."</p>
+      </li>
+    </ul>
     <p>See table 1 for a list of the language codes you can use with the <em>locale</em> field.</p>
-  </li>
-  <li><em>title</em>
-    <p>This is equivalent to the Title setting in the In-app Products UI. If the <em>title</em>
-    contains a semicolon, it must be escaped with a backslash (for example, "\;"). A backslash
-    should also be escaped with a backslash (for example, "\\").</p>
-  </li>
-  <li><em>description</em>
-    <p>This is equivalent to the Description in the In-app Products UI. If the <em>description</em>
-    contains a semicolon, it must be escaped with a backslash (for example, "\;"). A backslash
-    should also be escaped with a backslash (for example, "\\").</p>
-  </li>
-  <li><em>autofill</em>
+  </dd>
+  <dt>title</dt>
+  <dd>
+    This is equivalent to the Title setting in the In-app Products UI. If the <em>title</em>
+    contains a semicolon, it must be escaped with a backslash (for example, <code>\;</code>). Also, a backslash
+    must be escaped with a backslash (for example, <code>\\</code>).
+  </dd>
+  <dt>description</dt>
+  <dd>
+    This is equivalent to the Description in the In-app Products UI. If the <em>description</em>
+    contains a semicolon, it must be escaped with a backslash (for example, <code>\;</code>). Also, a backslash
+    must be escaped with a backslash (for example, <code>\\</code>).
+  </dd>
+  <dt>autofill</dt>
+  <dd>
     <p>This is equivalent to clicking <strong>Auto Fill</strong> in the In-app Products UI. Can be
     <code>true</code> or <code>false</code>. The syntax for specifying the <em>country</em>
-    and <em>price</em> varies depending on which <em>autofill</em> setting you use.</p>
-    <p>If <em>autofill</em> is set to <code>true</code>, you need to specify only the default
-    price in your home currency, and you must use this syntax:</p>
-    <p>"true","<em>default_price_in_home_currency</em>"
-    <p>If <em>autofill</em> is set to <code>false</code>, you need to specify a <em>country</em>
-    and a <em>price</em> for each currency, and you must use the following syntax:</p>
-    <p>"false", "<em>home_country</em>; <em>default_price_in_home_currency</em>; <em>country_2</em>;
-    <em>country_2_price</em>; <em>country_3</em>; <em>country_3_price</em>; ..."</p>
+    and <em>price</em> varies depending on which <em>autofill</em> setting you use:</p>
+    <ul>
+      <li>
+        <p>If <em>autofill</em> is set to <code>true</code>, you need to specify only the default
+        price in your home currency, and you must use this syntax:</p>
+        <p>"true","<em>default_price_in_home_currency</em>"
+      </li>
+      <li>
+        <p>If <em>autofill</em> is set to <code>false</code>, you need to specify a <em>country</em>
+        and a <em>price</em> for each currency, and you must use the following syntax:</p>
+        <p>"false", "<em>home_country</em>; <em>default_price_in_home_currency</em>; <em>country_2</em>;
+        <em>country_2_price</em>; <em>country_3</em>; <em>country_3_price</em>; ..."</p>
+      </li>
+    </ul>
     <p class="note"><strong>Note: </strong>If you use an <em>autofill</em> value of <code>false</code>
     and set country prices manually, you must incorporate country-specific
     pricing patterns, including tax rates, into the prices you provide.</p>
-  </li>
-  <li><em>country</em>
-    <p>The country for which you are specifying a price. You can only list countries that your
-    application is targeting. The country codes are two-letter uppercase
+  </dd>
+  <dt>country</dt>
+  <dd>
+    The country for which you are specifying a price. You can only list countries that your
+    app is targeting. The country codes are two-letter uppercase
     ISO country codes (such as "US"), as defined by
-    <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-2</a>.</p>
-  </li>
-  <li><em>price</em>
-    <p>This is equivalent to the Price in the In-app Products UI. The price must be specified in
+    <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-2</a>.
+  </dd>
+  <dt>price</dt>
+  <dd>
+    This is equivalent to the Price in the In-app Products UI. The price must be specified in
     micro-units. To convert a currency value to micro-units, you multiply the real value by
     1,000,000.
     For example, if you want to sell an in-app item for $1.99, you specify <code>1990000</code> in the
-    <em>price</em> field.</p>
-  </li>
-</ul>
+    <em>price</em> field.
+  </dd>
+</dl>
 
 <p class="table-caption" id="language-table"><strong>Table 1.</strong> Language codes you can use
 with the <em>locale</em> field.</p>
@@ -430,8 +421,8 @@
 </h2>
 
 <p>
-  If you sell multiple apps at the same price—or multiple in-app products at
-  the same price across one or more apps—you can add <em>pricing
+  If you sell multiple apps at the same price, or if you sell multiple in-app
+  products at the same price across one or more apps, you can add <em>pricing
   templates</em>. These templates make it easier to manage shared prices.
 </p>
 
@@ -440,25 +431,9 @@
 </h3>
 
 <p>
-  When creating a template, you can provide new pricing information, or you can
-  apply pricing information from an existing paid app or in-app product.
-</p>
-
-<div class="figure-right">
-  <figure id="fig-npt">
-    <img src="{@docRoot}images/in-app-billing/new_pricing_template.png"
-    srcset="{@docRoot}images/in-app-billing/new_pricing_template.png 1x, {@docRoot}images/in-app-billing/new_pricing_template_2x.png 2x"
-    width="400" alt="A template with the name Basic inventory uses a price of
-    USD 0.99.">
-    <figcaption>
-      <b>Figure 5. </b>The <em>Pricing template</em> page, where you add pricing
-      details for the new template you're creating.
-    </figcaption>
-  </figure>
-</div>
-
-<p>
-  To add a pricing template, do the following:
+  When creating a pricing template, you provide new pricing information that you
+  can apply to paid apps and in-app products. To add a pricing template, do the
+  following:
 </p>
 
 <ol>
@@ -467,21 +442,20 @@
     account.
   </li>
 
-  <li>In the <strong>Settings</strong> panel, select <strong>Pricing
-  template</strong>.
+  <li>In the <strong>Settings</strong> panel, open the <strong>Pricing
+  template</strong> page.
   </li>
 
   <li>
     <p>
       If you are adding your first pricing template, the <strong>Add a Pricing
       Template</strong> banner appears. Select <strong>Add template</strong> to
-      create a new template. The <strong>Pricing Template</strong> page
-      appears.
+      create a new template. The new template's <em>Pricing</em> tab appears.
     </p>
 
     <p>
       Otherwise, you see a list of your pricing templates. Select <strong>New
-      pricing template</strong>. The <strong>Pricing Template</strong> page
+      pricing template</strong>. The new template's <em>Pricing</em> tab
       appears.
     </p>
   </li>
@@ -494,7 +468,7 @@
     </p>
     <p>
       Based on the price and tax option you provide, the Developer Console
-      generates prices for international currencies using today's exchange
+      generates prices for international currencies using current exchange
       rates and country-specific pricing patterns.
     </p>
   </li>
@@ -507,44 +481,26 @@
 </h3>
 
 <p>
-  You can link shared prices across paid apps or in-app products to a pricing
-  template. To complete the linking process, use either the template's
-  <em>Linked Items</em> tab or the Price section within a paid app or in-app
-  product's pricing page.
+  You can create links between pricing templates and sets of paid apps and
+  in-app products that share the same price. After completing this linking
+  process, any changes you make to the pricing template are applied to the
+  prices of items that you've linked to the template. To complete the linking
+  process, use either the pricing template's <em>Linked Items</em> tab or the
+  Price section within a paid app or in-app product's pricing page.
 </p>
 
 <p class="note">
   <strong>Note:</strong> Since a subscription within your app has a constant
   price, you cannot link a subscription with a pricing template. You can,
-  however, import the prices from a template and apply them to a new
+  however, import the prices from a pricing template and apply them to a new
   subscription.
 </p>
 
 <h4>
-  Linking a pricing template or paid app to an in-app product
+  Linking a pricing template to in-app products and paid apps
 </h4>
 
 <p>
-  After you create a pricing template, you can link the prices of in-app
-  products and paid apps to that template. After completing this linking
-  process, any changes you make to the pricing template are applied to the
-  prices of items that you've linked to the template.
-</p>
-
-<div class="figure-right">
-  <figure id="fig-lpt">
-    <img src="{@docRoot}images/in-app-billing/link_pricing_template.png"
-    width="700" alt="The Sleeping Potion in-app product is linked to the Basic
-    Inventory item, but the Invisibility Potion is not.">
-    <figcaption>
-      <b>Figure 6. </b>Use the Linked Items tab of the
-      <em>Pricing Template</em> page to change which in-app products and paid
-      apps are linked to a pricing template.
-    </figcaption>
-  </figure>
-</div>
-
-<p>
   To link a pricing template to an in-app product, do the following:
 </p>
 
@@ -554,23 +510,24 @@
     account.
   </li>
 
-  <li>In the <strong>Settings</strong> panel, select <strong>Pricing
-  template</strong>. The <strong>Pricing Template</strong> page appears,
-  showing the list of pricing templates you have created for your account.
+  <li>In the <strong>Settings</strong> panel, open the <strong>Pricing
+  template</strong> page. This page shows the list of pricing templates you have
+  created for your account.
   </li>
 
-  <li>Choose the pricing template that you want to link to an in-app product,
-  then select the <em>Linked Items</em> tab. A page appears, showing options to
-  link your pricing template to in-app products and paid apps.
+  <li>Choose an existing pricing template that you want to link to an in-app
+  product, then select the template's <em>Linked Items</em> tab. This tab shows
+  options to link your pricing template to in-app products and paid apps
+  (see figure 2).
   </li>
 
-  <li>In the Link In-App Products section of the page, enter or choose the name
+  <li>In the Link In-App Products section of the tab, enter or choose the name
   of an app. This app should contain the in-app product that you want to link
   to your pricing template.
   </li>
 
   <li>Based on the app that you selected, you see a list of in-app products
-  that are active and are not yet linked to a pricing template. Select the
+  that are active and are not yet linked to a pricing template. Choose the
   in-app product that you want to link to the pricing template by selecting the
   <strong>Link</strong> button that appears in the same row as the in-app
   product.
@@ -588,27 +545,23 @@
   app in the Link Paid Apps section.
 </p>
 
+<figure id="fig-lpt">
+  <img class="border-img"
+  src="{@docRoot}images/in-app-billing/link_pricing_template.png" width="700"
+  alt="The Sleeping Potion in-app product is linked to the Basic Inventory item,
+  but the Invisibility Potion is not.">
+  <figcaption>
+    <b>Figure 2. </b>On a pricing template's <em>Linked Items</em> tab, you can
+    change which in-app products and paid apps are linked to the pricing
+    template.
+  </figcaption>
+</figure>
+
 <h4>
-  Linking an in-app product or paid app with a pricing template
+  Linking an in-app product or paid app to a pricing template
 </h4>
 
 <p>
-  After you create a paid app or in-app product, you can link its pricing
-  information to a pricing template.
-</p>
-
-<div class="figure-right">
-  <figure id="fig-spt">
-    <img src="{@docRoot}images/in-app-billing/select_pricing_template.png"
-    width="700" alt="">
-    <figcaption>
-      <b>Figure 7. </b>Choosing a pricing template to link to a particular
-      in-app product or paid app.
-    </figcaption>
-  </figure>
-</div>
-
-<p>
   To link an in-app product to a pricing template, do the following:
 </p>
 
@@ -618,20 +571,16 @@
     account.
   </li>
 
-  <li>In the <strong>All Applications</strong> panel, choose the app that
-  contains the in-app product that you want to link to a pricing template.
-  </li>
-
-  <li>Within the app's panel, choose the <strong>In-app Products</strong>
-  sub-panel.
+  <li>In the <strong>All applications</strong> panel, select the app name, then
+  open the <strong>In-app Products</strong> page.
   </li>
 
   <li>Choose the in-app product that you want to link to a pricing template.
-  The <em>Managed Product Details</em> page appears.
+  The item's details page appears.
   </li>
 
   <li>In the Pricing section, choose the pricing template that you want to link
-  to the price of this in-app product (see figure 7).
+  to the price of this in-app product.
   </li>
 
   <li>The price of the in-app product is now linked to the pricing template you
@@ -642,8 +591,7 @@
 
 <p>
   To link the price of a paid app to a pricing template, you follow a similar
-  process within the app's <strong>Pricing &amp; Distribution</strong>
-  sub-panel.
+  process on the app's <strong>Pricing &amp; Distribution</strong> page.
 </p>
 
 <h3 id="delete-linked-item">
@@ -652,27 +600,16 @@
 
 <p>
   As your app evolves, you may find it useful to remove older versions of
-  in-app products or apps, some of which may be linked to pricing templates. To
-  delete an in-app product or app that is linked to a pricing template, simply
-  remove it by completing the following steps. You don't need to unlink the
-  in-app product or app from the pricing template beforehand.
+  in-app products or unpublish paid apps, some of which may be linked to pricing
+  templates. To delete an in-app product or unpublish a paid app that is linked
+  to a pricing template, complete the following steps. You don't need to unlink
+  the in-app product or paid app from the pricing template beforehand.
 </p>
 
 <h4>
   Deleting an in-app product that is linked to a template
 </h4>
 
-<div class="figure-right">
-  <figure id="fig-diap">
-    <img src="{@docRoot}images/in-app-billing/delete_iap.png"
-    width="700" alt="">
-    <figcaption>
-      <b>Figure 8. </b>Deleting an in-app product that is linked to a pricing
-      template.
-    </figcaption>
-  </figure>
-</div>
-
 <p>
   To delete an in-app product that is linked to a template, do the following:
 </p>
@@ -683,8 +620,7 @@
     account.
   </li>
 
-  <li>In the Google Play Developer Console, navigate to the app that contains
-  the in-app product you want to delete.
+  <li>Select the app that contains the in-app product you want to delete.
   </li>
 
   <li>Open the app's <strong>In-app Products</strong> page.
@@ -694,16 +630,25 @@
   </li>
 
   <li>Select the button that indicates whether the in-app product is active or
-  inactive (enclosed in a box within figure 8). The drop-down menu includes a
+  inactive (enclosed in a box within figure 3). The drop-down menu includes a
   <strong>Delete</strong> option.
   </li>
-  <li>Select <strong>Delete</strong>, then select <strong>Yes</strong> in the
+  <li>Select <strong>Delete</strong>, then choose <strong>Yes</strong> in the
   confirmation dialog that appears.
   </li>
 </ol>
 
+<figure id="fig-diap">
+  <img class="border-img" src="{@docRoot}images/in-app-billing/delete_iap.png"
+  width="500" alt="">
+  <figcaption>
+    <b>Figure 3. </b>Deleting an in-app product that is linked to a pricing
+    template.
+  </figcaption>
+</figure>
+
 <h4>
-  Deleting a paid app that is linked to a template
+  Unpublishing a paid app that is linked to a template
 </h4>
 
 <div class="figure-right">
@@ -711,14 +656,15 @@
     <img src="{@docRoot}images/in-app-billing/unpublish_paid_app.png"
     width="700" alt="">
     <figcaption>
-      <b>Figure 9. </b>Unpublishing an app that has already been published and is
+      <b>Figure 4. </b>Unpublishing an app that has already been published and is
       linked to a pricing template.
     </figcaption>
   </figure>
 </div>
 
 <p>
-  To delete a paid app that is linked to a template, do the following:
+  To unpublish a paid app that is already published and is linked to a template,
+  do the following:
 </p>
 
 <ol>
@@ -727,15 +673,12 @@
     account.
   </li>
 
-  <li>In the Google Play Developer Console, choose the app that you want to
-  delete.
+  <li>Select the app that you want to unpublish.
   </li>
 
-  <li>Choose either <strong>Unpublish app</strong> (enclosed in a box within
-  figure 9) if you have already published the app, or
-  <strong>Delete app</strong> if your app is still in the "draft" state.
-  </li>
-  <li>Confirm your choice in the dialog that appears.
+  <li>Select <strong>Unpublish app</strong> (enclosed in a box within figure 4),
+  then choose <strong>Unpublish</strong> in the confirmation dialog that
+  appears.
   </li>
 </ol>
 
@@ -754,17 +697,16 @@
     account.
   </li>
 
-  <li>In the <strong>Settings</strong> panel, select <strong>Pricing
-  template</strong>. The <strong>Pricing Template</strong> page appears,
-  showing the list of pricing templates you have created for your account.
+  <li>In the <strong>Settings</strong> panel, open the <strong>Pricing
+  template</strong> page, which shows the list of pricing templates you have
+  created for your account.
   </li>
 
   <li>Select the pricing template that you wish to delete.
   </li>
 
-  <li>In the <em>Linked Items</em> tab on the pricing template details page,
-  unlink the pricing template from all in-app products and paid apps.
-  </li>
+  <li>On the pricing template's <em>Linked Items</em> tab, unlink all in-app
+  products that are linked to the template.</li>
 
   <li>Select <strong>Delete template</strong>.
   </li>
@@ -772,23 +714,21 @@
 
 <h2 id="billing-purchase-type">Choosing a Product Type</h3>
 
-<p>An item's product type controls how Google Play manages the purchase of the item. There are
-several product types, including "managed per user account", "unmanaged," and "subscription." However,
-note that the product types supported vary
-across In-app Billing Version, so you should always choose a product type that's valid for the
-version of In-app BIlling that your app uses. </p>
+<p>An item's product type controls how Google Play manages the purchase of the item. The supported
+product types include "managed product" and "subscription." Since support for different product
+types can vary among versions of the In-app Billing API, make sure that you choose a product
+type that's valid for the version of the In-app Billing API that your app uses. </p>
 
-<p>For details, refer to the documentation for <a
-href="{@docRoot}google/play/billing/api.html#producttype">In-app Billing Version
-3</a>.
+<p>For details, refer to the documentation for the <a
+href="{@docRoot}google/play/billing/api.html#producttype">In-app Billing API</a>.
 
 <h2 id="billing-refunds">Handling Refunds</h2>
 
 <p>In-app billing does not allow users to send a refund request to Google Play. Refunds for
-in-app purchases must be directed to you (the application developer). You can then process the
+in-app purchases must be directed to you (the app developer). You can then process the
 refund through your Google payments merchant account. When you do this, Google Play receives a
 refund notification from Google payments, and Google Play sends a refund message to your
-application. For more information, see <a
+app. For more information, see <a
 href="{@docRoot}google/play/billing/v2/api.html#billing-action-notify">Handling
 IN_APP_NOTIFY messages</a> and <a
 href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=1153485">
@@ -834,22 +774,22 @@
 <p>The Google Play Developer Console lets you set up one or more test accounts.
 A test account is a regular Google account that you register on the Developer
 Console as a test account. Test accounts are authorized to make in-app purchases
-from applications that you have uploaded to the Google Play Developer Console
+from apps that you have uploaded to the Google Play Developer Console
 but have not yet published.</p>
 
 <p>You can use any Google account as a test account. Test accounts are useful if you want to let
-multiple people test In-app Billing on applications without giving them access to your publisher
+multiple people test In-app Billing on apps without giving them access to your publisher
 account's sign-in credentials. If you want to own and control the test accounts, you can create the
 accounts yourself and distribute the credentials to your developers or testers.</p>
 
 <p>Test accounts have three limitations:</p>
 
 <ul>
-  <li>Test account users can make purchase requests only within applications that are already
-  uploaded to your publisher account (although the application doesn't need to be published).</li>
+  <li>Test account users can make purchase requests only within apps that are already
+  uploaded to your publisher account (although the app doesn't need to be published).</li>
   <li>Test accounts can only be used to purchase items that are listed (and published) in an
-  application's product list.</li>
-  <li>Test account users do not have access to your publisher account and cannot upload applications
+  app's product list.</li>
+  <li>Test account users do not have access to your publisher account and cannot upload apps
   to your publisher account.</li>
 </ul>
 
@@ -869,29 +809,28 @@
 <p>The Google Play Developer Console provides a public licensing key for each
 app.</p>
 
-<div class="figure-right">
-  <figure id="fig-bak">
-    <img src="{@docRoot}images/in-app-billing/billing_app_key.png"
-    width="700" alt="">
-    <figcaption>
-      <b>Figure 10. </b>You can find the license key for each app in the
-      <strong>Services &amp; APIs</strong> panel.
-    </figcaption>
-  </figure>
-</div>
-
-<p>To get the key for an app, follow these steps:</p>
+<p>To locate the key for an app, follow these steps:</p>
 <ol>
-  <li>Open the <strong>All Applications</strong> panel.</li>
-  <li>Click on the app name, then select <strong>Services &amp; APIs</strong>.</li>
-  <li>Scroll down to the <strong>Your License Key for this Application</strong>
-field to locate the key for the app, as shown in figure 10.</li>
+  <li>Open the <strong>All applications</strong> panel.</li>
+  <li>Click on the app name, then open the <strong>Services &amp; APIs</strong>
+  page.</li>
+  <li>Scroll down to the section of the page labeled Your License Key for This
+  Application, as shown in figure 5.</li>
 </ol>
 <p>Previously, the Developer Console provided a single public key per developer
 account. To transition apps to the new per-app public key, the Developer Console
-set the app-specific key as the former developer key. This ensures compatibility
+sets the app-specific key as the former developer key. This ensures compatibility
 for apps that depend on the (former) developer key. </p>
 
+<figure id="fig-bak">
+  <img class="border-img" src="{@docRoot}images/in-app-billing/billing_app_key.png"
+  width="700" alt="">
+  <figcaption>
+    <b>Figure 5. </b>You can find the license key for each app on the
+    <strong>Services &amp; APIs</strong> page.
+  </figcaption>
+</figure>
+
 <h2 id="billing-support">Where to Get Support</h2>
 
 <p>If you have questions or encounter problems while implementing In-app Billing, contact the
diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd
index c658f70..8ffb45c 100755
--- a/docs/html/google/play/billing/billing_integrate.jd
+++ b/docs/html/google/play/billing/billing_integrate.jd
@@ -111,13 +111,13 @@
           <li>Select <strong>File > New > Directory</strong> and enter {@code aidl} in the
           <em>New Directory</em> window, then select <strong>OK</strong>.
 
-          <li>Select <strong>File > New > Package</strong> and enter 
+          <li>Select <strong>File > New > Package</strong> and enter
           {@code com.android.vending.billing} in the <em>New Package</em> window, then select
           <strong>OK</strong>.</li>
 
-          <li>Using your operating system file explorer, navigate to 
-          {@code &lt;sdk&gt;/extras/google/play_billing/}, copy the 
-          {@code IInAppBillingService.aidl} file, and paste it into the 
+          <li>Using your operating system file explorer, navigate to
+          {@code &lt;sdk&gt;/extras/google/play_billing/}, copy the
+          {@code IInAppBillingService.aidl} file, and paste it into the
           {@code com.android.vending.billing} package in your project.
           </li>
         </ol>
@@ -137,7 +137,7 @@
   </li>
 </ol>
 
-<h2 id="billing-permission">Updating Your Application's Manifest</h2>
+<h2 id="billing-permission">Updating Your App's Manifest</h2>
 
 <p>
   In-app billing relies on the Google Play application, which handles all
diff --git a/docs/html/google/play/billing/billing_overview.jd b/docs/html/google/play/billing/billing_overview.jd
index 2954a83..a05cc8d 100644
--- a/docs/html/google/play/billing/billing_overview.jd
+++ b/docs/html/google/play/billing/billing_overview.jd
@@ -132,11 +132,11 @@
 purchases for that product.</p>
 <p>If you sell several of your apps or in-app products at the same price, you
 can add <em>pricing templates</em> to manage these price points from a
-centralized location. When using pricing templates, you can include the local
-tax within the prices you provide, or you can provide prices and have the system
+centralized location. When using pricing templates, you can include local taxes
+within the prices you provide, or you can provide prices and have the system
 add local taxes to these prices. You can make changes to the prices in your
-templates&mdash;such as refreshing the exchange rates for certain
-countries&mdash;and your changes are applied to the apps and in-app products
+pricing templates, such as refreshing the exchange rates for certain
+countries, and your changes are applied to the apps and in-app products
 that you link to the template.</p>
 <p>You can also create test accounts to authorize
 access for testing applications that are unpublished.</p>
diff --git a/docs/html/google/play/billing/index.jd b/docs/html/google/play/billing/index.jd
index 795aceb..80934ae 100644
--- a/docs/html/google/play/billing/index.jd
+++ b/docs/html/google/play/billing/index.jd
@@ -18,7 +18,8 @@
     apps and in-app products that they distribute to multiple countries, the
     system automatically sets local prices for different currencies using
     today’s exchange rates and country-specific pricing patterns. To satisfy
-    specific pricing needs, developers can also adjust these prices manually.</li>
+    particular pricing needs, developers can also adjust these prices manually.
+  </li>
   <li><strong>Pricing Templates</strong>&mdash;Developers can add pricing
     templates and link these templates to app prices or in-app product prices.
     These templates include local prices across all markets. By using a
diff --git a/docs/html/images/in-app-billing/add_new_product.png b/docs/html/images/in-app-billing/add_new_product.png
deleted file mode 100644
index 2281ec0..0000000
--- a/docs/html/images/in-app-billing/add_new_product.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/in-app-billing/billing_app_key.png b/docs/html/images/in-app-billing/billing_app_key.png
index 4c5300e..5adca9c 100644
--- a/docs/html/images/in-app-billing/billing_app_key.png
+++ b/docs/html/images/in-app-billing/billing_app_key.png
Binary files differ
diff --git a/docs/html/images/in-app-billing/delete_iap.png b/docs/html/images/in-app-billing/delete_iap.png
index bbaea9e..4603452 100644
--- a/docs/html/images/in-app-billing/delete_iap.png
+++ b/docs/html/images/in-app-billing/delete_iap.png
Binary files differ
diff --git a/docs/html/images/in-app-billing/in_app_products.png b/docs/html/images/in-app-billing/in_app_products.png
deleted file mode 100644
index 04031cc..0000000
--- a/docs/html/images/in-app-billing/in_app_products.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/in-app-billing/new_managed_product.png b/docs/html/images/in-app-billing/new_managed_product.png
deleted file mode 100644
index bdccc96..0000000
--- a/docs/html/images/in-app-billing/new_managed_product.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/in-app-billing/new_pricing_template.png b/docs/html/images/in-app-billing/new_pricing_template.png
deleted file mode 100644
index 8525787..0000000
--- a/docs/html/images/in-app-billing/new_pricing_template.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/in-app-billing/new_pricing_template_2x.png b/docs/html/images/in-app-billing/new_pricing_template_2x.png
deleted file mode 100644
index ce4094b..0000000
--- a/docs/html/images/in-app-billing/new_pricing_template_2x.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/in-app-billing/select_pricing_template.png b/docs/html/images/in-app-billing/select_pricing_template.png
deleted file mode 100644
index fa8c7b6..0000000
--- a/docs/html/images/in-app-billing/select_pricing_template.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/in-app-billing/unpublish_paid_app.png b/docs/html/images/in-app-billing/unpublish_paid_app.png
index a36d8ce..64bbd0d 100644
--- a/docs/html/images/in-app-billing/unpublish_paid_app.png
+++ b/docs/html/images/in-app-billing/unpublish_paid_app.png
Binary files differ
diff --git a/docs/html/wear/preview/_book.yaml b/docs/html/wear/preview/_book.yaml
index a687294..a4acad0 100644
--- a/docs/html/wear/preview/_book.yaml
+++ b/docs/html/wear/preview/_book.yaml
@@ -10,21 +10,21 @@
   section:
   - title: Notification Improvements
     path: /wear/preview/features/notifications.html
-  - title: Input Method Framework 
+  - title: Input Method Framework
     path: /wear/preview/features/ime.html
   - title: Complications
     path: /wear/preview/features/complications.html
-  - title: Wear Navigation and Actions
+  - title: Navigation and Actions
     path: /wear/preview/features/ui-nav-actions.html
   - title: Bridging for Notifications
     path: /wear/preview/features/bridger.html
 
-- title: Downloads
-  path: /wear/preview/downloads.html
-
 - title: Get Started
   path: /wear/preview/start.html
 
+- title: Download and Test
+  path: /wear/preview/downloads.html
+
 - title: License Agreement
   path: /wear/preview/license.html
 
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index a33b219..3b74ee7 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -48,6 +48,7 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Retention;
@@ -875,10 +876,8 @@
          * for the {@link TvInputService} this TV input implements.
          *
          * @return TvInputInfo containing information about this TV input.
-         * @throws IOException If there was an I/O error.
-         * @throws XmlPullParserException If there was an XML parsing error.
          */
-        public TvInputInfo build() throws IOException, XmlPullParserException {
+        public TvInputInfo build() {
             ComponentName componentName = new ComponentName(mResolveInfo.serviceInfo.packageName,
                     mResolveInfo.serviceInfo.name);
             String id;
@@ -925,15 +924,14 @@
                     + tvInputHardwareInfo.getDeviceId();
         }
 
-        private void parseServiceMetadata(int inputType)
-                throws XmlPullParserException, IOException {
+        private void parseServiceMetadata(int inputType) {
             ServiceInfo si = mResolveInfo.serviceInfo;
             PackageManager pm = mContext.getPackageManager();
             try (XmlResourceParser parser =
                          si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA)) {
                 if (parser == null) {
-                    throw new XmlPullParserException("No " + TvInputService.SERVICE_META_DATA
-                            + " meta-data for " + si.name);
+                    throw new IllegalStateException("No " + TvInputService.SERVICE_META_DATA
+                            + " meta-data found for " + si.name);
                 }
 
                 Resources res = pm.getResourcesForApplication(si.applicationInfo);
@@ -946,26 +944,19 @@
 
                 String nodeName = parser.getName();
                 if (!XML_START_TAG_NAME.equals(nodeName)) {
-                    throw new XmlPullParserException(
-                            "Meta-data does not start with tv-input-service tag in " + si.name);
+                    throw new IllegalStateException("Meta-data does not start with "
+                            + XML_START_TAG_NAME + " tag for " + si.name);
                 }
 
                 TypedArray sa = res.obtainAttributes(attrs,
                         com.android.internal.R.styleable.TvInputService);
                 mSetupActivity = sa.getString(
                         com.android.internal.R.styleable.TvInputService_setupActivity);
-                if (DEBUG) {
-                    Log.d(TAG, "Setup activity loaded. [" + mSetupActivity + "] for " + si.name);
-                }
                 if (inputType == TYPE_TUNER && TextUtils.isEmpty(mSetupActivity)) {
-                    throw new XmlPullParserException("Setup activity not found in " + si.name);
+                    throw new IllegalStateException("Setup activity not found for " + si.name);
                 }
                 mSettingsActivity = sa.getString(
                         com.android.internal.R.styleable.TvInputService_settingsActivity);
-                if (DEBUG) {
-                    Log.d(TAG, "Settings activity loaded. [" + mSettingsActivity + "] for "
-                            + si.name);
-                }
                 if (mCanRecord == null) {
                     mCanRecord = sa.getBoolean(
                             com.android.internal.R.styleable.TvInputService_canRecord, false);
@@ -975,8 +966,10 @@
                             com.android.internal.R.styleable.TvInputService_tunerCount, 1);
                 }
                 sa.recycle();
+            } catch (IOException | XmlPullParserException e) {
+                throw new IllegalStateException("Failed reading meta-data for " + si.packageName, e);
             } catch (NameNotFoundException e) {
-                throw new XmlPullParserException("Unable to create context for: " + si.packageName);
+                throw new IllegalStateException("No resources found for " + si.packageName, e);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index a0d9dfe..f76a68c 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -31,6 +31,7 @@
 import android.opengl.GLUtils;
 import android.os.AsyncTask;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.renderscript.Matrix4f;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
@@ -310,8 +311,6 @@
                 Log.d(TAG, "onSurfaceRedrawNeeded");
             }
             super.onSurfaceRedrawNeeded(holder);
-
-            mLastSurfaceHeight = mLastSurfaceWidth = -1;
             drawFrame();
         }
 
@@ -325,6 +324,7 @@
                 return;
             }
             try {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawWallpaper");
                 DisplayInfo displayInfo = getDefaultDisplayInfo();
                 int newRotation = displayInfo.rotation;
 
@@ -419,6 +419,7 @@
                     drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
                 }
             } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                 if (FIXED_SIZED_SURFACE && !mIsHwAccelerated) {
                     // If the surface is fixed-size, we should only need to
                     // draw it once and then we'll let the window manager
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 611f9f2..cfc6c52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -826,7 +826,7 @@
         boolean useThumbnailTransition = (runningTask != null) && !isHomeStackVisible && hasRecentTasks;
 
         // Update the launch state that we need in updateHeaderBarLayout()
-        launchState.launchedFromHome = !useThumbnailTransition;
+        launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking;
         launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
         launchState.launchedViaDockGesture = mLaunchedWhileDocking;
         launchState.launchedViaDragGesture = mDraggingInRecents;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 99c8211..5bb5091 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -73,6 +73,8 @@
     private final static boolean DEBUG = false;
 
     public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
+    private final static String RECENTS_HOME_INTENT_EXTRA =
+            "com.android.systemui.recents.tv.RecentsTvActivity.RECENTS_HOME_INTENT_EXTRA";
 
     private boolean mFinishedOnStartup;
     private RecentsPackageMonitor mPackageMonitor;
@@ -320,6 +322,7 @@
         homeIntent.addCategory(Intent.CATEGORY_HOME);
         homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        homeIntent.putExtra(RECENTS_HOME_INTENT_EXTRA, true);
         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
 
         mPipManager.addListener(mPipListener);
@@ -334,7 +337,23 @@
     @Override
     protected void onStart() {
         super.onStart();
+        updatePipUI();
+    }
 
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+        if(mLaunchedFromHome) {
+            mHomeRecentsEnterExitAnimationHolder.startEnterAnimation(mPipManager.isPipShown());
+        }
+        mTaskStackViewAdapter.setResetAddedCards(true);
+        EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mPipRecentsOverlayManager.onRecentsResumed();
         // Update the recent tasks
         updateRecentsTasks();
 
@@ -365,24 +384,6 @@
         // Notify that recents is now visible
         SystemServicesProxy ssp = Recents.getSystemServices();
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
-
-        updatePipUI();
-    }
-
-    @Override
-    public void onEnterAnimationComplete() {
-        super.onEnterAnimationComplete();
-        if(mLaunchedFromHome) {
-            mHomeRecentsEnterExitAnimationHolder.startEnterAnimation(mPipManager.isPipShown());
-        }
-        mTaskStackViewAdapter.setResetAddedCards(true);
-        EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mPipRecentsOverlayManager.onRecentsResumed();
         if(mTaskStackHorizontalGridView.getStack().getTaskCount() > 1 && !mLaunchedFromHome) {
             // If there are 2 or more tasks, and we are not launching from home
             // set the selected position to the 2nd task to allow for faster app switching
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index cc8e832..f4c13d7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -276,12 +276,13 @@
                     tv.onStartFrontTaskEnterAnimation(mStackView.mScreenPinningEnabled);
                 }
             } else if (launchState.launchedViaDockGesture) {
-                // Animate the tasks up
+                // Animate the tasks up - add some delay to match the divider animation
                 AnimationProps taskAnimation = new AnimationProps()
                         .setDuration(AnimationProps.BOUNDS, dockGestureAnimDuration +
                                 (taskIndexFromBack * DOUBLE_FRAME_OFFSET_MS))
                         .setInterpolator(AnimationProps.BOUNDS,
                                 ENTER_WHILE_DOCKING_INTERPOLATOR)
+                        .setStartDelay(AnimationProps.BOUNDS, 48)
                         .setListener(postAnimationTrigger.decrementOnAnimationEnd());
                 postAnimationTrigger.increment();
                 mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3fc66dd..a220f2ed7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -1075,11 +1075,13 @@
             mAnimateAfterRecentsDrawn = false;
             updateDockSide();
 
-            // Delay switching resizing mode because this might cause jank in recents animation
-            // that's long than this animation.
-            stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(),
-                    mLongPressEntraceAnimDuration, Interpolators.FAST_OUT_SLOW_IN,
-                    200 /* endDelay */);
+            post(() -> {
+                // Delay switching resizing mode because this might cause jank in recents animation
+                // that's longer than this animation.
+                stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(),
+                        mLongPressEntraceAnimDuration, Interpolators.FAST_OUT_SLOW_IN,
+                        200 /* endDelay */);
+            });
         }
         if (mGrowAfterRecentsDrawn) {
             mGrowAfterRecentsDrawn = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
index e6e189f..cc02ece 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -93,6 +93,9 @@
         @Override
         public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) {
         }
+        @Override
+        public void onRestrictBackgroundBlacklistChanged(int uid, boolean blacklisted) {
+        }
     };
 
     public interface Listener {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 53b2942..b5c2b89 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1445,6 +1445,14 @@
                         + whitelisted + ")");
             }
         }
+        @Override
+        public void onRestrictBackgroundBlacklistChanged(int uid, boolean blacklisted) {
+            if (LOGD_RULES) {
+                // caller is NPMS, since we only register with them
+                log("onRestrictBackgroundBlacklistChanged(uid=" + uid + ", blacklisted="
+                        + blacklisted + ")");
+            }
+        }
     };
 
     /**
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 36f51fc..95dee01 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2805,28 +2805,63 @@
     /**
      * Prints a list of ServiceRecords (dumpsys activity services)
      */
-    void dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
-        boolean needSep = false;
-        boolean printedAnything = false;
-
-        ItemMatcher matcher = new ItemMatcher();
-        matcher.build(args, opti);
-
-        pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
-        try {
-            if (mLastAnrDump != null) {
-                pw.println("  Last ANR service:");
-                pw.print(mLastAnrDump);
-                pw.println();
+    List<ServiceRecord> collectServicesToDumpLocked(ItemMatcher matcher, String dumpPackage) {
+        final ArrayList<ServiceRecord> services = new ArrayList<>();
+        final int[] users = mAm.mUserController.getUsers();
+        for (int user : users) {
+            ServiceMap smap = getServiceMap(user);
+            if (smap.mServicesByName.size() > 0) {
+                for (int si=0; si<smap.mServicesByName.size(); si++) {
+                    ServiceRecord r = smap.mServicesByName.valueAt(si);
+                    if (!matcher.match(r, r.name)) {
+                        continue;
+                    }
+                    if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+                        continue;
+                    }
+                    services.add(r);
+                }
             }
-            int[] users = mAm.mUserController.getUsers();
+        }
+
+        return services;
+    }
+
+    final class ServiceDumper {
+        private final FileDescriptor fd;
+        private final PrintWriter pw;
+        private final String[] args;
+        private final int opti;
+        private final boolean dumpAll;
+        private final String dumpPackage;
+        private final ItemMatcher matcher;
+        private final ArrayList<ServiceRecord> services = new ArrayList<>();
+
+        private final long nowReal = SystemClock.elapsedRealtime();
+
+        private boolean needSep = false;
+        private boolean printedAnything = false;
+        private boolean printed = false;
+
+        /**
+         * Note: do not call directly, use {@link #newServiceDumperLocked} instead (this
+         * must be called with the lock held).
+         */
+        ServiceDumper(FileDescriptor fd, PrintWriter pw, String[] args,
+                int opti, boolean dumpAll, String dumpPackage) {
+            this.fd = fd;
+            this.pw = pw;
+            this.args = args;
+            this.opti = opti;
+            this.dumpAll = dumpAll;
+            this.dumpPackage = dumpPackage;
+            matcher = new ItemMatcher();
+            matcher.build(args, opti);
+
+            final int[] users = mAm.mUserController.getUsers();
             for (int user : users) {
                 ServiceMap smap = getServiceMap(user);
-                boolean printed = false;
                 if (smap.mServicesByName.size() > 0) {
-                    long nowReal = SystemClock.elapsedRealtime();
-                    needSep = false;
                     for (int si=0; si<smap.mServicesByName.size(); si++) {
                         ServiceRecord r = smap.mServicesByName.valueAt(si);
                         if (!matcher.match(r, r.name)) {
@@ -2835,215 +2870,325 @@
                         if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
                             continue;
                         }
-                        if (!printed) {
-                            if (printedAnything) {
-                                pw.println();
+                        services.add(r);
+                    }
+                }
+            }
+        }
+
+        private void dumpHeaderLocked() {
+            pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
+            if (mLastAnrDump != null) {
+                pw.println("  Last ANR service:");
+                pw.print(mLastAnrDump);
+                pw.println();
+            }
+        }
+
+        void dumpLocked() {
+            dumpHeaderLocked();
+
+            try {
+                int[] users = mAm.mUserController.getUsers();
+                for (int user : users) {
+                    // Find the first service for this user.
+                    int serviceIdx = 0;
+                    while (serviceIdx < services.size() && services.get(serviceIdx).userId != user) {
+                        serviceIdx++;
+                    }
+                    printed = false;
+                    if (serviceIdx < services.size()) {
+                        needSep = false;
+                        while (serviceIdx < services.size()) {
+                            ServiceRecord r = services.get(serviceIdx);
+                            serviceIdx++;
+                            if (r.userId != user) {
+                                break;
                             }
-                            pw.println("  User " + user + " active services:");
-                            printed = true;
+                            dumpServiceLocalLocked(r);
                         }
-                        printedAnything = true;
-                        if (needSep) {
-                            pw.println();
-                        }
-                        pw.print("  * ");
-                        pw.println(r);
-                        if (dumpAll) {
-                            r.dump(pw, "    ");
-                            needSep = true;
-                        } else {
-                            pw.print("    app=");
-                            pw.println(r.app);
-                            pw.print("    created=");
-                            TimeUtils.formatDuration(r.createTime, nowReal, pw);
-                            pw.print(" started=");
-                            pw.print(r.startRequested);
-                            pw.print(" connections=");
-                            pw.println(r.connections.size());
-                            if (r.connections.size() > 0) {
-                                pw.println("    Connections:");
-                                for (int conni=0; conni<r.connections.size(); conni++) {
-                                    ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
-                                    for (int i = 0; i < clist.size(); i++) {
-                                        ConnectionRecord conn = clist.get(i);
-                                        pw.print("      ");
-                                        pw.print(conn.binding.intent.intent.getIntent()
-                                                .toShortString(false, false, false, false));
-                                        pw.print(" -> ");
-                                        ProcessRecord proc = conn.binding.client;
-                                        pw.println(proc != null ? proc.toShortString() : "null");
-                                    }
-                                }
+                        needSep |= printed;
+                    }
+
+                    dumpUserRemainsLocked(user);
+                }
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception in dumpServicesLocked", e);
+            }
+
+            dumpRemainsLocked();
+        }
+
+        void dumpWithClient() {
+            synchronized(mAm) {
+                dumpHeaderLocked();
+            }
+
+            try {
+                int[] users = mAm.mUserController.getUsers();
+                for (int user : users) {
+                    // Find the first service for this user.
+                    int serviceIdx = 0;
+                    while (serviceIdx < services.size() && services.get(serviceIdx).userId != user) {
+                        serviceIdx++;
+                    }
+                    printed = false;
+                    if (serviceIdx < services.size()) {
+                        needSep = false;
+                        while (serviceIdx < services.size()) {
+                            ServiceRecord r = services.get(serviceIdx);
+                            serviceIdx++;
+                            if (r.userId != user) {
+                                break;
                             }
-                        }
-                        if (dumpClient && r.app != null && r.app.thread != null) {
-                            pw.println("    Client:");
-                            pw.flush();
-                            try {
-                                TransferPipe tp = new TransferPipe();
-                                try {
-                                    r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
-                                            r, args);
-                                    tp.setBufferPrefix("      ");
-                                    // Short timeout, since blocking here can
-                                    // deadlock with the application.
-                                    tp.go(fd, 2000);
-                                } finally {
-                                    tp.kill();
-                                }
-                            } catch (IOException e) {
-                                pw.println("      Failure while dumping the service: " + e);
-                            } catch (RemoteException e) {
-                                pw.println("      Got a RemoteException while dumping the service");
+                            synchronized(mAm) {
+                                dumpServiceLocalLocked(r);
                             }
-                            needSep = true;
+                            dumpServiceClient(r);
+                        }
+                        needSep |= printed;
+                    }
+
+                    synchronized(mAm) {
+                        dumpUserRemainsLocked(user);
+                    }
+                }
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception in dumpServicesLocked", e);
+            }
+
+            synchronized(mAm) {
+                dumpRemainsLocked();
+            }
+        }
+
+        private void dumpUserHeaderLocked(int user) {
+            if (!printed) {
+                if (printedAnything) {
+                    pw.println();
+                }
+                pw.println("  User " + user + " active services:");
+                printed = true;
+            }
+            printedAnything = true;
+            if (needSep) {
+                pw.println();
+            }
+        }
+
+        private void dumpServiceLocalLocked(ServiceRecord r) {
+            dumpUserHeaderLocked(r.userId);
+            pw.print("  * ");
+            pw.println(r);
+            if (dumpAll) {
+                r.dump(pw, "    ");
+                needSep = true;
+            } else {
+                pw.print("    app=");
+                pw.println(r.app);
+                pw.print("    created=");
+                TimeUtils.formatDuration(r.createTime, nowReal, pw);
+                pw.print(" started=");
+                pw.print(r.startRequested);
+                pw.print(" connections=");
+                pw.println(r.connections.size());
+                if (r.connections.size() > 0) {
+                    pw.println("    Connections:");
+                    for (int conni=0; conni<r.connections.size(); conni++) {
+                        ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
+                        for (int i = 0; i < clist.size(); i++) {
+                            ConnectionRecord conn = clist.get(i);
+                            pw.print("      ");
+                            pw.print(conn.binding.intent.intent.getIntent()
+                                    .toShortString(false, false, false, false));
+                            pw.print(" -> ");
+                            ProcessRecord proc = conn.binding.client;
+                            pw.println(proc != null ? proc.toShortString() : "null");
                         }
                     }
-                    needSep |= printed;
                 }
+            }
+        }
+
+        private void dumpServiceClient(ServiceRecord r) {
+            final ProcessRecord proc = r.app;
+            if (proc == null) {
+                return;
+            }
+            final IApplicationThread thread = proc.thread;
+            if (thread == null) {
+                return;
+            }
+            pw.println("    Client:");
+            pw.flush();
+            try {
+                TransferPipe tp = new TransferPipe();
+                try {
+                    thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args);
+                    tp.setBufferPrefix("      ");
+                    // Short timeout, since blocking here can
+                    // deadlock with the application.
+                    tp.go(fd, 2000);
+                } finally {
+                    tp.kill();
+                }
+            } catch (IOException e) {
+                pw.println("      Failure while dumping the service: " + e);
+            } catch (RemoteException e) {
+                pw.println("      Got a RemoteException while dumping the service");
+            }
+            needSep = true;
+        }
+
+        private void dumpUserRemainsLocked(int user) {
+            ServiceMap smap = getServiceMap(user);
+            printed = false;
+            for (int si=0, SN=smap.mDelayedStartList.size(); si<SN; si++) {
+                ServiceRecord r = smap.mDelayedStartList.get(si);
+                if (!matcher.match(r, r.name)) {
+                    continue;
+                }
+                if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+                    continue;
+                }
+                if (!printed) {
+                    if (printedAnything) {
+                        pw.println();
+                    }
+                    pw.println("  User " + user + " delayed start services:");
+                    printed = true;
+                }
+                printedAnything = true;
+                pw.print("  * Delayed start "); pw.println(r);
+            }
+            printed = false;
+            for (int si=0, SN=smap.mStartingBackground.size(); si<SN; si++) {
+                ServiceRecord r = smap.mStartingBackground.get(si);
+                if (!matcher.match(r, r.name)) {
+                    continue;
+                }
+                if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+                    continue;
+                }
+                if (!printed) {
+                    if (printedAnything) {
+                        pw.println();
+                    }
+                    pw.println("  User " + user + " starting in background:");
+                    printed = true;
+                }
+                printedAnything = true;
+                pw.print("  * Starting bg "); pw.println(r);
+            }
+        }
+
+        private void dumpRemainsLocked() {
+            if (mPendingServices.size() > 0) {
                 printed = false;
-                for (int si=0, SN=smap.mDelayedStartList.size(); si<SN; si++) {
-                    ServiceRecord r = smap.mDelayedStartList.get(si);
+                for (int i=0; i<mPendingServices.size(); i++) {
+                    ServiceRecord r = mPendingServices.get(i);
                     if (!matcher.match(r, r.name)) {
                         continue;
                     }
                     if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
                         continue;
                     }
-                    if (!printed) {
-                        if (printedAnything) {
-                            pw.println();
-                        }
-                        pw.println("  User " + user + " delayed start services:");
-                        printed = true;
-                    }
-                    printedAnything = true;
-                    pw.print("  * Delayed start "); pw.println(r);
-                }
-                printed = false;
-                for (int si=0, SN=smap.mStartingBackground.size(); si<SN; si++) {
-                    ServiceRecord r = smap.mStartingBackground.get(si);
-                    if (!matcher.match(r, r.name)) {
-                        continue;
-                    }
-                    if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
-                        continue;
-                    }
-                    if (!printed) {
-                        if (printedAnything) {
-                            pw.println();
-                        }
-                        pw.println("  User " + user + " starting in background:");
-                        printed = true;
-                    }
-                    printedAnything = true;
-                    pw.print("  * Starting bg "); pw.println(r);
-                }
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Exception in dumpServicesLocked", e);
-        }
-
-        if (mPendingServices.size() > 0) {
-            boolean printed = false;
-            for (int i=0; i<mPendingServices.size(); i++) {
-                ServiceRecord r = mPendingServices.get(i);
-                if (!matcher.match(r, r.name)) {
-                    continue;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
-                    continue;
-                }
-                printedAnything = true;
-                if (!printed) {
-                    if (needSep) pw.println();
-                    needSep = true;
-                    pw.println("  Pending services:");
-                    printed = true;
-                }
-                pw.print("  * Pending "); pw.println(r);
-                r.dump(pw, "    ");
-            }
-            needSep = true;
-        }
-
-        if (mRestartingServices.size() > 0) {
-            boolean printed = false;
-            for (int i=0; i<mRestartingServices.size(); i++) {
-                ServiceRecord r = mRestartingServices.get(i);
-                if (!matcher.match(r, r.name)) {
-                    continue;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
-                    continue;
-                }
-                printedAnything = true;
-                if (!printed) {
-                    if (needSep) pw.println();
-                    needSep = true;
-                    pw.println("  Restarting services:");
-                    printed = true;
-                }
-                pw.print("  * Restarting "); pw.println(r);
-                r.dump(pw, "    ");
-            }
-            needSep = true;
-        }
-
-        if (mDestroyingServices.size() > 0) {
-            boolean printed = false;
-            for (int i=0; i< mDestroyingServices.size(); i++) {
-                ServiceRecord r = mDestroyingServices.get(i);
-                if (!matcher.match(r, r.name)) {
-                    continue;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
-                    continue;
-                }
-                printedAnything = true;
-                if (!printed) {
-                    if (needSep) pw.println();
-                    needSep = true;
-                    pw.println("  Destroying services:");
-                    printed = true;
-                }
-                pw.print("  * Destroy "); pw.println(r);
-                r.dump(pw, "    ");
-            }
-            needSep = true;
-        }
-
-        if (dumpAll) {
-            boolean printed = false;
-            for (int ic=0; ic<mServiceConnections.size(); ic++) {
-                ArrayList<ConnectionRecord> r = mServiceConnections.valueAt(ic);
-                for (int i=0; i<r.size(); i++) {
-                    ConnectionRecord cr = r.get(i);
-                    if (!matcher.match(cr.binding.service, cr.binding.service.name)) {
-                        continue;
-                    }
-                    if (dumpPackage != null && (cr.binding.client == null
-                            || !dumpPackage.equals(cr.binding.client.info.packageName))) {
-                        continue;
-                    }
                     printedAnything = true;
                     if (!printed) {
                         if (needSep) pw.println();
                         needSep = true;
-                        pw.println("  Connection bindings to services:");
+                        pw.println("  Pending services:");
                         printed = true;
                     }
-                    pw.print("  * "); pw.println(cr);
-                    cr.dump(pw, "    ");
+                    pw.print("  * Pending "); pw.println(r);
+                    r.dump(pw, "    ");
+                }
+                needSep = true;
+            }
+
+            if (mRestartingServices.size() > 0) {
+                printed = false;
+                for (int i=0; i<mRestartingServices.size(); i++) {
+                    ServiceRecord r = mRestartingServices.get(i);
+                    if (!matcher.match(r, r.name)) {
+                        continue;
+                    }
+                    if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+                        continue;
+                    }
+                    printedAnything = true;
+                    if (!printed) {
+                        if (needSep) pw.println();
+                        needSep = true;
+                        pw.println("  Restarting services:");
+                        printed = true;
+                    }
+                    pw.print("  * Restarting "); pw.println(r);
+                    r.dump(pw, "    ");
+                }
+                needSep = true;
+            }
+
+            if (mDestroyingServices.size() > 0) {
+                printed = false;
+                for (int i=0; i< mDestroyingServices.size(); i++) {
+                    ServiceRecord r = mDestroyingServices.get(i);
+                    if (!matcher.match(r, r.name)) {
+                        continue;
+                    }
+                    if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+                        continue;
+                    }
+                    printedAnything = true;
+                    if (!printed) {
+                        if (needSep) pw.println();
+                        needSep = true;
+                        pw.println("  Destroying services:");
+                        printed = true;
+                    }
+                    pw.print("  * Destroy "); pw.println(r);
+                    r.dump(pw, "    ");
+                }
+                needSep = true;
+            }
+
+            if (dumpAll) {
+                printed = false;
+                for (int ic=0; ic<mServiceConnections.size(); ic++) {
+                    ArrayList<ConnectionRecord> r = mServiceConnections.valueAt(ic);
+                    for (int i=0; i<r.size(); i++) {
+                        ConnectionRecord cr = r.get(i);
+                        if (!matcher.match(cr.binding.service, cr.binding.service.name)) {
+                            continue;
+                        }
+                        if (dumpPackage != null && (cr.binding.client == null
+                                || !dumpPackage.equals(cr.binding.client.info.packageName))) {
+                            continue;
+                        }
+                        printedAnything = true;
+                        if (!printed) {
+                            if (needSep) pw.println();
+                            needSep = true;
+                            pw.println("  Connection bindings to services:");
+                            printed = true;
+                        }
+                        pw.print("  * "); pw.println(cr);
+                        cr.dump(pw, "    ");
+                    }
                 }
             }
-        }
 
-        if (!printedAnything) {
-            pw.println("  (nothing)");
+            if (!printedAnything) {
+                pw.println("  (nothing)");
+            }
         }
     }
 
+    ServiceDumper newServiceDumperLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, String dumpPackage) {
+        return new ServiceDumper(fd, pw, args, opti, dumpAll, dumpPackage);
+    }
+
     /**
      * There are three ways to call this:
      *  - no service specified: dump all the services
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d0ec7ce..a066462 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import com.android.internal.telephony.TelephonyIntents;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 import com.android.internal.R;
@@ -7050,6 +7051,32 @@
     }
 
     @Override
+    public int sendIntentSender(IIntentSender target, int code, Intent intent, String resolvedType,
+            IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+        if (target instanceof PendingIntentRecord) {
+            return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,
+                    finishedReceiver, requiredPermission, options);
+        } else {
+            try {
+                target.send(code, intent, resolvedType, null, requiredPermission, options);
+            } catch (RemoteException e) {
+            }
+            // Platform code can rely on getting a result back when the send is done, but if
+            // this intent sender is from outside of the system we can't rely on it doing that.
+            // So instead we don't give it the result receiver, and instead just directly
+            // report the finish immediately.
+            if (finishedReceiver != null) {
+                try {
+                    finishedReceiver.performReceive(intent, 0,
+                            null, null, false, false, UserHandle.getCallingUserId());
+                } catch (RemoteException e) {
+                }
+            }
+            return 0;
+        }
+    }
+
+    @Override
     public void cancelIntentSender(IIntentSender sender) {
         if (!(sender instanceof PendingIntentRecord)) {
             return;
@@ -13777,8 +13804,18 @@
                     dumpAssociationsLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
                 }
             } else if ("services".equals(cmd) || "s".equals(cmd)) {
-                synchronized (this) {
-                    mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
+                if (dumpClient) {
+                    ActiveServices.ServiceDumper dumper;
+                    synchronized (this) {
+                        dumper = mServices.newServiceDumperLocked(fd, pw, args, opti, true,
+                                dumpPackage);
+                    }
+                    dumper.dumpWithClient();
+                } else {
+                    synchronized (this) {
+                        mServices.newServiceDumperLocked(fd, pw, args, opti, true,
+                                dumpPackage).dumpLocked();
+                    }
                 }
             } else if ("locks".equals(cmd)) {
                 LockGuard.dump(fd, pw, args);
@@ -13800,50 +13837,105 @@
         }
 
         // No piece of data specified, dump everything.
-        synchronized (this) {
-            dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            mServices.dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
-            pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
-            }
-            dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
-            if (mAssociations.size() > 0) {
+        if (dumpClient) {
+            ActiveServices.ServiceDumper sdumper;
+            synchronized (this) {
+                dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                 pw.println();
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
                 }
-                dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+                dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll,
+                        dumpPackage);
             }
+            sdumper.dumpWithClient();
             pw.println();
-            if (dumpAll) {
-                pw.println("-------------------------------------------------------------------------------");
+            synchronized (this) {
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+                if (mAssociations.size() > 0) {
+                    pw.println();
+                    if (dumpAll) {
+                        pw.println("-------------------------------------------------------------------------------");
+                    }
+                    dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+                }
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage);
             }
-            dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+
+        } else {
+            synchronized (this) {
+                dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, dumpPackage)
+                        .dumpLocked();
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+                if (mAssociations.size() > 0) {
+                    pw.println();
+                    if (dumpAll) {
+                        pw.println("-------------------------------------------------------------------------------");
+                    }
+                    dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+                }
+                pw.println();
+                if (dumpAll) {
+                    pw.println("-------------------------------------------------------------------------------");
+                }
+                dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            }
         }
         Binder.restoreCallingIdentity(origId);
     }
@@ -16148,8 +16240,8 @@
             catPw.println();
             dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null);
             catPw.println();
-            mServices.dumpServicesLocked(null, catPw, emptyArgs, 0,
-                    false, false, null);
+            mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0,
+                    false, null).dumpLocked();
             catPw.println();
             dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null);
             catPw.flush();
@@ -17252,7 +17344,8 @@
                     || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
                     || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                     || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
-                    || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
+                    || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
+                    || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)) {
                 // Broadcast is either protected, or it's a public action that
                 // we've relaxed, so it's fine for system internals to send.
             } else {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 843720e..8ade556 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2017,6 +2017,9 @@
     void cancelInitializingActivities() {
         final ActivityRecord topActivity = topRunningActivityLocked();
         boolean aboveTop = true;
+        // We don't want to clear starting window for activities that aren't behind fullscreen
+        // activities as we need to display their starting window until they are done initializing.
+        boolean behindFullscreenActivity = false;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -2025,16 +2028,20 @@
                     if (r == topActivity) {
                         aboveTop = false;
                     }
+                    behindFullscreenActivity |= r.fullscreen;
                     continue;
                 }
 
                 if (r.state == ActivityState.INITIALIZING
-                        && r.mStartingWindowState == STARTING_WINDOW_SHOWN) {
+                        && r.mStartingWindowState == STARTING_WINDOW_SHOWN
+                        && behindFullscreenActivity) {
                     if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY,
                             "Found orphaned starting window " + r);
                     r.mStartingWindowState = STARTING_WINDOW_REMOVED;
                     mWindowManager.removeAppStartingWindow(r.appToken);
                 }
+
+                behindFullscreenActivity |= r.fullscreen;
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 76b404c..64b898a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1122,7 +1122,8 @@
             }
             final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
             if (!mTargetStack.isFocusable()
-                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay)) {
+                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
+                    && mStartActivity != topTaskActivity)) {
                 // If the activity is not focusable, we can't resume it, but still would like to
                 // make sure it becomes visible as it starts (this will also trigger entry
                 // animation). An example of this are PIP activities.
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index b8f45bc..1f8d26b 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -198,16 +198,21 @@
         ref = new WeakReference<PendingIntentRecord>(this);
     }
 
-    public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
-            String requiredPermission, Bundle options) throws TransactionTooLargeException {
+    public void send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
+            String requiredPermission, Bundle options) {
+        sendInner(code, intent, resolvedType, finishedReceiver,
+                requiredPermission, null, null, 0, 0, 0, options, null);
+    }
+
+    public int sendWithResult(int code, Intent intent, String resolvedType,
+            IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
         return sendInner(code, intent, resolvedType, finishedReceiver,
                 requiredPermission, null, null, 0, 0, 0, options, null);
     }
 
     int sendInner(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
             String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
-            int flagsMask, int flagsValues, Bundle options, IActivityContainer container)
-            throws TransactionTooLargeException {
+            int flagsMask, int flagsValues, Bundle options, IActivityContainer container) {
         if (intent != null) intent.setDefusable(true);
         if (options != null) options.setDefusable(true);
 
@@ -253,6 +258,7 @@
                 if (userId == UserHandle.USER_CURRENT) {
                     userId = owner.mUserController.getCurrentOrTargetUserIdLocked();
                 }
+                int res = 0;
                 switch (key.type) {
                     case ActivityManager.INTENT_SENDER_ACTIVITY:
                         if (options == null) {
@@ -312,21 +318,23 @@
                                     resolvedType, key.packageName, userId);
                         } catch (RuntimeException e) {
                             Slog.w(TAG, "Unable to send startService intent", e);
+                        } catch (TransactionTooLargeException e) {
+                            res = ActivityManager.START_CANCELED;
                         }
                         break;
                 }
 
-                if (sendFinish) {
+                if (sendFinish && res != ActivityManager.START_CANCELED) {
                     try {
                         finishedReceiver.performReceive(new Intent(finalIntent), 0,
                                 null, null, false, false, key.userId);
                     } catch (RemoteException e) {
                     }
                 }
-                
+
                 Binder.restoreCallingIdentity(origId);
-                
-                return 0;
+
+                return res;
             }
         }
         return ActivityManager.START_CANCELED;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6c1e1a7..8c4e113 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -22,6 +22,9 @@
 import static android.net.RouteInfo.RTN_UNREACHABLE;
 
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
@@ -67,9 +70,11 @@
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnInfo;
@@ -88,7 +93,10 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -119,9 +127,18 @@
     private final Looper mLooper;
     private final NetworkCapabilities mNetworkCapabilities;
 
-    /* list of users using this VPN. */
+    /**
+     * List of UIDs that are set to use this VPN by default. Normally, every UID in the user is
+     * added to this set but that can be changed by adding allowed or disallowed applications. It
+     * is non-null iff the VPN is connected.
+     *
+     * Unless the VPN has set allowBypass=true, these UIDs are forced into the VPN.
+     *
+     * @see VpnService.Builder#addAllowedApplication(String)
+     * @see VpnService.Builder#addDisallowedApplication(String)
+     */
     @GuardedBy("this")
-    private List<UidRange> mVpnUsers = null;
+    private Set<UidRange> mVpnUsers = null;
 
     // Handle of user initiating VPN.
     private final int mUserHandle;
@@ -467,22 +484,8 @@
             Binder.restoreCallingIdentity(token);
         }
 
-        addVpnUserLocked(mUserHandle);
-        // If the user can have restricted profiles, assign all its restricted profiles to this VPN
-        if (canHaveRestrictedProfile(mUserHandle)) {
-            token = Binder.clearCallingIdentity();
-            List<UserInfo> users;
-            try {
-                users = UserManager.get(mContext).getUsers();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            for (UserInfo user : users) {
-                if (user.isRestricted() && (user.restrictedProfileParentId == mUserHandle)) {
-                    addVpnUserLocked(user.id);
-                }
-            }
-        }
+        mVpnUsers = createUserAndRestrictedProfilesRanges(mUserHandle,
+                mConfig.allowedApplications, mConfig.disallowedApplications);
         mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()]));
 
         mNetworkInfo.setIsAvailable(true);
@@ -568,7 +571,7 @@
         Connection oldConnection = mConnection;
         NetworkAgent oldNetworkAgent = mNetworkAgent;
         mNetworkAgent = null;
-        List<UidRange> oldUsers = mVpnUsers;
+        Set<UidRange> oldUsers = mVpnUsers;
 
         // Configure the interface. Abort if any of these steps fails.
         ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
@@ -601,8 +604,6 @@
             mConfig = config;
 
             // Set up forwarding and DNS rules.
-            mVpnUsers = new ArrayList<UidRange>();
-
             agentConnect();
 
             if (oldConnection != null) {
@@ -657,44 +658,93 @@
         return uids;
     }
 
-    // Note: This function adds to mVpnUsers but does not publish list to NetworkAgent.
-    private void addVpnUserLocked(int userHandle) {
-        if (mVpnUsers == null) {
-            throw new IllegalStateException("VPN is not active");
-        }
+    /**
+     * Creates a {@link Set} of non-intersecting {@link UidRange} objects including all UIDs
+     * associated with one user, and any restricted profiles attached to that user.
+     *
+     * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
+     * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
+     * in each user and profile will be included.
+     *
+     * @param userHandle The userId to create UID ranges for along with any of its restricted
+     *                   profiles.
+     * @param allowedApplications (optional) whitelist of applications to include.
+     * @param disallowedApplications (optional) blacklist of applications to exclude.
+     */
+    @VisibleForTesting
+    Set<UidRange> createUserAndRestrictedProfilesRanges(@UserIdInt int userHandle,
+            @Nullable List<String> allowedApplications,
+            @Nullable List<String> disallowedApplications) {
+        final Set<UidRange> ranges = new ArraySet<>();
 
-        if (mConfig.allowedApplications != null) {
+        // Assign the top-level user to the set of ranges
+        addUserToRanges(ranges, userHandle, allowedApplications, disallowedApplications);
+
+        // If the user can have restricted profiles, assign all its restricted profiles too
+        if (canHaveRestrictedProfile(userHandle)) {
+            final long token = Binder.clearCallingIdentity();
+            List<UserInfo> users;
+            try {
+                users = UserManager.get(mContext).getUsers();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            for (UserInfo user : users) {
+                if (user.isRestricted() && (user.restrictedProfileParentId == userHandle)) {
+                    addUserToRanges(ranges, user.id, allowedApplications, disallowedApplications);
+                }
+            }
+        }
+        return ranges;
+    }
+
+    /**
+     * Updates a {@link Set} of non-intersecting {@link UidRange} objects to include all UIDs
+     * associated with one user.
+     *
+     * <p>If one of {@param allowedApplications} or {@param disallowedApplications} is provided,
+     * the UID ranges will match the app whitelist or blacklist specified there. Otherwise, all UIDs
+     * in the user will be included.
+     *
+     * @param ranges {@link Set} of {@link UidRange}s to which to add.
+     * @param userHandle The userId to add to {@param ranges}.
+     * @param allowedApplications (optional) whitelist of applications to include.
+     * @param disallowedApplications (optional) blacklist of applications to exclude.
+     */
+    @VisibleForTesting
+    void addUserToRanges(@NonNull Set<UidRange> ranges, @UserIdInt int userHandle,
+            @Nullable List<String> allowedApplications,
+            @Nullable List<String> disallowedApplications) {
+        if (allowedApplications != null) {
             // Add ranges covering all UIDs for allowedApplications.
             int start = -1, stop = -1;
-            for (int uid : getAppsUids(mConfig.allowedApplications, userHandle)) {
+            for (int uid : getAppsUids(allowedApplications, userHandle)) {
                 if (start == -1) {
                     start = uid;
                 } else if (uid != stop + 1) {
-                    mVpnUsers.add(new UidRange(start, stop));
+                    ranges.add(new UidRange(start, stop));
                     start = uid;
                 }
                 stop = uid;
             }
-            if (start != -1) mVpnUsers.add(new UidRange(start, stop));
-        } else if (mConfig.disallowedApplications != null) {
+            if (start != -1) ranges.add(new UidRange(start, stop));
+        } else if (disallowedApplications != null) {
             // Add all ranges for user skipping UIDs for disallowedApplications.
             final UidRange userRange = UidRange.createForUser(userHandle);
             int start = userRange.start;
-            for (int uid : getAppsUids(mConfig.disallowedApplications, userHandle)) {
+            for (int uid : getAppsUids(disallowedApplications, userHandle)) {
                 if (uid == start) {
                     start++;
                 } else {
-                    mVpnUsers.add(new UidRange(start, uid - 1));
+                    ranges.add(new UidRange(start, uid - 1));
                     start = uid + 1;
                 }
             }
-            if (start <= userRange.stop) mVpnUsers.add(new UidRange(start, userRange.stop));
+            if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop));
         } else {
             // Add all UIDs for the user.
-            mVpnUsers.add(UidRange.createForUser(userHandle));
+            ranges.add(UidRange.createForUser(userHandle));
         }
-
-        prepareStatusIntent();
     }
 
     // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that
@@ -703,7 +753,7 @@
         final UidRange userRange = UidRange.createForUser(userHandle);
         final List<UidRange> ranges = new ArrayList<UidRange>();
         for (UidRange range : mVpnUsers) {
-            if (range.start >= userRange.start && range.stop <= userRange.stop) {
+            if (userRange.containsRange(range)) {
                 ranges.add(range);
             }
         }
@@ -719,7 +769,6 @@
             mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()]));
         }
         mVpnUsers.removeAll(ranges);
-        mStatusIntent = null;
     }
 
     public void onUserAdded(int userHandle) {
@@ -729,7 +778,8 @@
                 && mVpnUsers != null) {
             synchronized(Vpn.this) {
                 try {
-                    addVpnUserLocked(userHandle);
+                    addUserToRanges(mVpnUsers, userHandle, mConfig.allowedApplications,
+                            mConfig.disallowedApplications);
                     if (mNetworkAgent != null) {
                         final List<UidRange> ranges = uidRangesForUser(userHandle);
                         mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()]));
@@ -902,7 +952,7 @@
             return false;
         }
         for (UidRange uidRange : mVpnUsers) {
-            if (uidRange.start <= uid && uid <= uidRange.stop) {
+            if (uidRange.contains(uid)) {
                 return true;
             }
         }
@@ -1408,7 +1458,7 @@
 
                     // Now INetworkManagementEventObserver is watching our back.
                     mInterface = mConfig.interfaze;
-                    mVpnUsers = new ArrayList<UidRange>();
+                    prepareStatusIntent();
 
                     agentConnect();
 
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 9fd22686..7d28633 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -178,6 +178,11 @@
         public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) {
             updateTrackedJobs(uid);
         }
+
+        @Override
+        public void onRestrictBackgroundBlacklistChanged(int uid, boolean blacklisted) {
+            updateTrackedJobs(uid);
+        }
     };
 
     @Override
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 4bdc237..67cd7c3 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -266,6 +266,7 @@
     private static final int MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED = 9;
     private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
     private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
+    private static final int MSG_RESTRICT_BACKGROUND_BLACKLIST_CHANGED = 12;
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
@@ -1707,9 +1708,14 @@
     private void setUidPolicyUncheckedLocked(int uid, int oldPolicy, int policy, boolean persist) {
         setUidPolicyUncheckedLocked(uid, policy, persist);
 
+        final boolean isBlacklisted = policy == POLICY_REJECT_METERED_BACKGROUND;
+        mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_BLACKLIST_CHANGED, uid,
+                isBlacklisted ? 1 : 0).sendToTarget();
+
+        final boolean wasBlacklisted = oldPolicy == POLICY_REJECT_METERED_BACKGROUND;
         // Checks if app was added or removed to the blacklist.
-        if ((oldPolicy == POLICY_NONE && policy == POLICY_REJECT_METERED_BACKGROUND)
-                || (oldPolicy == POLICY_REJECT_METERED_BACKGROUND && policy == POLICY_NONE)) {
+        if ((oldPolicy == POLICY_NONE && isBlacklisted)
+                || (wasBlacklisted && policy == POLICY_NONE)) {
             mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 1, null)
                     .sendToTarget();
         }
@@ -3096,6 +3102,16 @@
         }
     }
 
+    private void dispatchRestrictBackgroundBlacklistChanged(INetworkPolicyListener listener,
+            int uid, boolean blacklisted) {
+        if (listener != null) {
+            try {
+                listener.onRestrictBackgroundBlacklistChanged(uid, blacklisted);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
     private Handler.Callback mHandlerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message msg) {
@@ -3186,7 +3202,6 @@
                         }
                         mListeners.finishBroadcast();
                     }
-
                     final PackageManager pm = mContext.getPackageManager();
                     final String[] packages = pm.getPackagesForUid(uid);
                     if (changed && packages != null) {
@@ -3202,6 +3217,21 @@
                     }
                     return true;
                 }
+                case MSG_RESTRICT_BACKGROUND_BLACKLIST_CHANGED: {
+                    final int uid = msg.arg1;
+                    final boolean blacklisted = msg.arg2 == 1;
+
+                    dispatchRestrictBackgroundBlacklistChanged(mConnectivityListener, uid,
+                            blacklisted);
+                    final int length = mListeners.beginBroadcast();
+                    for (int i = 0; i < length; i++) {
+                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                        dispatchRestrictBackgroundBlacklistChanged(listener, uid,
+                                blacklisted);
+                    }
+                    mListeners.finishBroadcast();
+                    return true;
+                }
                 case MSG_ADVISE_PERSIST_THRESHOLD: {
                     final long lowestRule = (Long) msg.obj;
                     try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4de8b57..1eeff14 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1463,14 +1463,13 @@
 
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
-            public int send(int code, Intent intent, String resolvedType,
+            public void send(int code, Intent intent, String resolvedType,
                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                 try {
                     mResult.offer(intent, 5, TimeUnit.SECONDS);
                 } catch (InterruptedException e) {
                     throw new RuntimeException(e);
                 }
-                return 0;
             }
         };
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2d8dcf0..33ccf16 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,6 +22,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -30,7 +31,9 @@
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
 import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -142,6 +145,7 @@
     private static final String TAG_USER = "user";
     private static final String TAG_RESTRICTIONS = "restrictions";
     private static final String TAG_DEVICE_POLICY_RESTRICTIONS = "device_policy_restrictions";
+    private static final String TAG_GLOBAL_RESTRICTION_OWNER_ID = "globalRestrictionOwnerUserId";
     private static final String TAG_ENTRY = "entry";
     private static final String TAG_VALUE = "value";
     private static final String TAG_SEED_ACCOUNT_OPTIONS = "seedAccountOptions";
@@ -275,6 +279,12 @@
     private Bundle mDevicePolicyGlobalUserRestrictions;
 
     /**
+     * Id of the user that set global restrictions.
+     */
+    @GuardedBy("mRestrictionsLock")
+    private int mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL;
+
+    /**
      * User restrictions set by {@link com.android.server.devicepolicy.DevicePolicyManagerService}
      * for each user.
      */
@@ -995,6 +1005,13 @@
                 if (globalChanged) {
                     mDevicePolicyGlobalUserRestrictions = global;
                 }
+                // Remember the global restriction owner userId to be able to make a distinction
+                // in getUserRestrictionSource on who set local policies.
+                mGlobalRestrictionOwnerUserId = userId;
+            } else {
+                // When profile owner sets restrictions it passes null global bundle and we reset 
+                // global restriction owner userId.
+                mGlobalRestrictionOwnerUserId = UserHandle.USER_NULL;
             }
             {
                 // Update local.
@@ -1078,6 +1095,54 @@
     }
 
     /**
+     * @hide
+     *
+     * Returns who set a user restriction on a user.
+     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * @param restrictionKey the string key representing the restriction
+     * @param userId the id of the user for whom to retrieve the restrictions.
+     * @return The source of user restriction. Any combination of
+     *         {@link UserManager#RESTRICTION_NOT_SET},
+     *         {@link UserManager#RESTRICTION_SOURCE_SYSTEM},
+     *         {@link UserManager#RESTRICTION_SOURCE_DEVICE_OWNER}
+     *         and {@link UserManager#RESTRICTION_SOURCE_PROFILE_OWNER}
+     */
+    @Override
+    public int getUserRestrictionSource(String restrictionKey, int userId) {
+        checkManageUsersPermission("getUserRestrictionSource");
+        int result = UserManager.RESTRICTION_NOT_SET;
+
+        // Shortcut for the most common case
+        if (!hasUserRestriction(restrictionKey, userId)) {
+            return result;
+        }
+
+        if (hasBaseUserRestriction(restrictionKey, userId)) {
+            result |= UserManager.RESTRICTION_SOURCE_SYSTEM;
+        }
+
+        synchronized(mRestrictionsLock) {
+            Bundle localRestrictions = mDevicePolicyLocalUserRestrictions.get(userId);
+            if (!UserRestrictionsUtils.isEmpty(localRestrictions)
+                    && localRestrictions.getBoolean(restrictionKey)) {
+                // Local restrictions may have been set by device owner the userId of which is
+                // stored in mGlobalRestrictionOwnerUserId.
+                if (mGlobalRestrictionOwnerUserId == userId) {
+                    result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
+                } else {
+                    result |= UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
+                }
+            }
+            if (!UserRestrictionsUtils.isEmpty(mDevicePolicyGlobalUserRestrictions)
+                    && mDevicePolicyGlobalUserRestrictions.getBoolean(restrictionKey)) {
+                result |= UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
+            }
+        }
+
+        return result;
+    }
+
+    /**
      * @return UserRestrictions that are in effect currently.  This always returns a new
      * {@link Bundle}.
      */
@@ -1474,6 +1539,11 @@
                                 break;
                             }
                         }
+                    } else if (name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) {
+                        String ownerUserId = parser.getAttributeValue(null, ATTR_ID);
+                        if (ownerUserId != null) {
+                            mGlobalRestrictionOwnerUserId = Integer.parseInt(ownerUserId);
+                        }
                     }
                 }
             }
@@ -1733,6 +1803,9 @@
                 UserRestrictionsUtils.writeRestrictions(serializer,
                         mDevicePolicyGlobalUserRestrictions, TAG_DEVICE_POLICY_RESTRICTIONS);
             }
+            serializer.startTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID);
+            serializer.attribute(null, ATTR_ID, Integer.toString(mGlobalRestrictionOwnerUserId));
+            serializer.endTag(null, TAG_GLOBAL_RESTRICTION_OWNER_ID);
             int[] userIdsToWrite;
             synchronized (mUsersLock) {
                 userIdsToWrite = new int[mUsers.size()];
@@ -2972,6 +3045,8 @@
                         .dumpRestrictions(pw, "    ", mDevicePolicyGlobalUserRestrictions);
             }
             pw.println();
+            pw.println("  Global restrictions owner id:" + mGlobalRestrictionOwnerUserId);
+            pw.println();
             pw.println("  Guest restrictions:");
             synchronized (mGuestRestrictions) {
                 UserRestrictionsUtils.dumpRestrictions(pw, "    ", mGuestRestrictions);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 2946bb5..b488297 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -331,7 +331,7 @@
                 try {
                     TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
                     inputList.add(info);
-                } catch (XmlPullParserException | IOException e) {
+                } catch (Exception e) {
                     Slog.e(TAG, "failed to load TV input " + si.name, e);
                     continue;
                 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index acf1689..52273dc 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1298,6 +1298,9 @@
         if (!finalClipRect.equals(mLastFinalClipRect)) {
             mLastFinalClipRect.set(finalClipRect);
             mSurfaceController.setFinalCropInTransaction(finalClipRect);
+            if (mDestroyPreservedSurfaceUponRedraw && mPendingDestroySurface != null) {
+                mPendingDestroySurface.setFinalCropInTransaction(finalClipRect);
+            }
         }
     }
 
@@ -1370,7 +1373,8 @@
 
         // If we are animating, we either apply the clip before applying all the animation
         // transformation or after all the transformation.
-        final boolean useFinalClipRect = isAnimationSet() && stackClip == STACK_CLIP_AFTER_ANIM;
+        final boolean useFinalClipRect = isAnimationSet() && stackClip == STACK_CLIP_AFTER_ANIM
+                || mDestroyPreservedSurfaceUponRedraw;
 
         // We need to do some acrobatics with surface position, because their clip region is
         // relative to the inside of the surface, but the stack bounds aren't.
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/VpnTest.java b/services/tests/servicestests/src/com/android/server/connectivity/VpnTest.java
new file mode 100644
index 0000000..3295bf5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/connectivity/VpnTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.connectivity;
+
+import static android.content.pm.UserInfo.FLAG_ADMIN;
+import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PRIMARY;
+import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static org.mockito.Mockito.*;
+
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.net.UidRange;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link Vpn}.
+ *
+ * Build, install and run with:
+ *  runtest --path src/com/android/server/connectivity/VpnTest.java
+ */
+public class VpnTest extends AndroidTestCase {
+    private static final String TAG = "VpnTest";
+
+    // Mock users
+    static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY);
+    static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN);
+    static final UserInfo restrictedProfileA = new UserInfo(40, "RestrictedA", FLAG_RESTRICTED);
+    static final UserInfo restrictedProfileB = new UserInfo(42, "RestrictedB", FLAG_RESTRICTED);
+    static final UserInfo managedProfileA = new UserInfo(45, "ManagedA", FLAG_MANAGED_PROFILE);
+    static {
+        restrictedProfileA.restrictedProfileParentId = primaryUser.id;
+        restrictedProfileB.restrictedProfileParentId = secondaryUser.id;
+        managedProfileA.profileGroupId = primaryUser.id;
+    }
+
+    @Mock private Context mContext;
+    @Mock private UserManager mUserManager;
+    @Mock private PackageManager mPackageManager;
+    @Mock private INetworkManagementService mNetService;
+
+    @Override
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
+        doNothing().when(mNetService).registerObserver(any());
+    }
+
+    @SmallTest
+    public void testRestrictedProfilesAreAddedToVpn() {
+        setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
+
+        final Vpn vpn = createVpn(primaryUser.id);
+        final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
+                null, null);
+
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
+            UidRange.createForUser(primaryUser.id),
+            UidRange.createForUser(restrictedProfileA.id)
+        })), ranges);
+    }
+
+    @SmallTest
+    public void testManagedProfilesAreNotAddedToVpn() {
+        setMockedUsers(primaryUser, managedProfileA);
+
+        final Vpn vpn = createVpn(primaryUser.id);
+        final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
+                null, null);
+
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
+            UidRange.createForUser(primaryUser.id)
+        })), ranges);
+    }
+
+    @SmallTest
+    public void testAddUserToVpnOnlyAddsOneUser() {
+        setMockedUsers(primaryUser, restrictedProfileA, managedProfileA);
+
+        final Vpn vpn = createVpn(primaryUser.id);
+        final Set<UidRange> ranges = new ArraySet<>();
+        vpn.addUserToRanges(ranges, primaryUser.id, null, null);
+
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
+            UidRange.createForUser(primaryUser.id)
+        })), ranges);
+    }
+
+    @SmallTest
+    public void testUidWhiteAndBlacklist() throws Exception {
+        final Map<String, Integer> packages = new ArrayMap<>();
+        packages.put("com.example", 66);
+        packages.put("org.example", 77);
+        packages.put("net.example", 78);
+        setMockedPackages(packages);
+
+        final Vpn vpn = createVpn(primaryUser.id);
+        final UidRange user = UidRange.createForUser(primaryUser.id);
+
+        // Whitelist
+        final Set<UidRange> allow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
+                new ArrayList<String>(packages.keySet()), null);
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
+            new UidRange(user.start + 66, user.start + 66),
+            new UidRange(user.start + 77, user.start + 78)
+        })), allow);
+
+        // Blacklist
+        final Set<UidRange> disallow = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
+                null, new ArrayList<String>(packages.keySet()));
+        assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
+            new UidRange(user.start, user.start + 65),
+            new UidRange(user.start + 67, user.start + 76),
+            new UidRange(user.start + 79, user.stop)
+        })), disallow);
+    }
+
+    /**
+     * @return A subclass of {@link Vpn} which is reliably:
+     * <ul>
+     *   <li>Associated with a specific user ID</li>
+     *   <li>Not in always-on mode</li>
+     * </ul>
+     */
+    private Vpn createVpn(@UserIdInt int userId) {
+        return new Vpn(Looper.myLooper(), mContext, mNetService, userId);
+    }
+
+    /**
+     * Populate {@link #mUserManager} with a list of fake users.
+     */
+    private void setMockedUsers(UserInfo... users) {
+        final Map<Integer, UserInfo> userMap = new ArrayMap<>();
+        for (UserInfo user : users) {
+            userMap.put(user.id, user);
+        }
+
+        doAnswer(invocation -> {
+            return new ArrayList(userMap.values());
+        }).when(mUserManager).getUsers();
+
+        doAnswer(invocation -> {
+            final int id = (int) invocation.getArguments()[0];
+            return userMap.get(id);
+        }).when(mUserManager).getUserInfo(anyInt());
+
+        doAnswer(invocation -> {
+            final int id = (int) invocation.getArguments()[0];
+            return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
+        }).when(mUserManager).canHaveRestrictedProfile(anyInt());
+    }
+
+    /**
+     * Populate {@link #mPackageManager} with a fake packageName-to-UID mapping.
+     */
+    private void setMockedPackages(final Map<String, Integer> packages) {
+        try {
+            doAnswer(invocation -> {
+                final String appName = (String) invocation.getArguments()[0];
+                final int userId = (int) invocation.getArguments()[1];
+                return UserHandle.getUid(userId, packages.get(appName));
+            }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt());
+        } catch (Exception e) {
+        }
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index a07a5af..46b0fbd 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -426,4 +426,10 @@
      */
     public static final String ACTION_REQUEST_NETWORK_FAILED =
             "android.intent.action.REQUEST_NETWORK_FAILED";
+
+    /**
+     * Broadcast action to trigger CI OMA-DM Session.
+     */
+    public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
+            "com.android.omadm.service.CONFIGURATION_UPDATE";
 }