Merge "Dump user id in dumpsys activity starter" into oc-dev
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 394bd0a..a80ef03 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -19,24 +19,230 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.RemoteException;
+import android.provider.Settings;
+
 import com.android.internal.os.HandlerCaller;
 import android.annotation.SdkConstant;
-import android.app.Activity;
-import android.app.Service;
-import android.content.Intent;
+import android.app.Service;import android.content.Intent;
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.Looper;
 import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillValue;
 
 import com.android.internal.os.SomeArgs;
 
 /**
- * Top-level service of the current autofill service for a given user.
+ * An {@code AutofillService} is a service used to automatically fill the contents of the screen
+ * on behalf of a given user - for more information about autofill, read
+ * <a href="{@docRoot}preview/features/autofill.html">Autofill Framework</a>.
  *
- * <p>Apps providing autofill capabilities must extend this service.
+ * <p>An {@code AutofillService} is only bound to the Android System for autofill purposes if:
+ * <ol>
+ *   <li>It requires the {@code android.permission.BIND_AUTOFILL_SERVICE} permission in its
+ *       manifest.
+ *   <li>The user explicitly enables it using Android Settings (the
+ *       {@link Settings#ACTION_REQUEST_SET_AUTOFILL_SERVICE} intent can be used to launch such
+ *       Settings screen).
+ * </ol>
+ *
+ * <h3>Basic usage</h3>
+ *
+ * <p>The basic autofill process is defined by the workflow below:
+ * <ol>
+ *   <li>User focus an editable {@link View}.
+ *   <li>View calls {@link AutofillManager#notifyViewEntered(android.view.View)}.
+ *   <li>A {@link ViewStructure} representing all views in the screen is created.
+ *   <li>The Android System binds to the service and calls {@link #onConnected()}.
+ *   <li>The service receives the view structure through the
+ *       {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
+ *   <li>The service replies through {@link FillCallback#onSuccess(FillResponse)}.
+ *   <li>The Android System calls {@link #onDisconnected()} and unbinds from the
+ *       {@code AutofillService}.
+ *   <li>The Android System displays an UI affordance with the options sent by the service.
+ *   <li>The user picks an option.
+ *   <li>The proper views are autofilled.
+ * </ol>
+ *
+ * <p>This workflow was designed to minimize the time the Android System is bound to the service;
+ * for each call, it: binds to service, waits for the reply, and unbinds right away. Furthermore,
+ * those calls are considered stateless: if the service needs to keep state between calls, it must
+ * do its own state management (keeping in mind that the service's process might be killed by the
+ * Android System when unbound; for example, if the device is running low in memory).
+ *
+ * <p>Typically, the
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} will:
+ * <ol>
+ *   <li>Parse the view structure looking for autofillable views (for example, using
+ *       {@link android.app.assist.AssistStructure.ViewNode#getAutofillHints()}.
+ *   <li>Match the autofillable views with the user's data.
+ *   <li>Create a {@link Dataset} for each set of user's data that match those fields.
+ *   <li>Fill the dataset(s) with the proper {@link AutofillId}s and {@link AutofillValue}s.
+ *   <li>Add the dataset(s) to the {@link FillResponse} passed to
+ *       {@link FillCallback#onSuccess(FillResponse)}.
+ * </ol>
+ *
+ * <p>For example, for a login screen with username and password views where the user only has one
+ * account in the service, the response could be:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ *     .addDataset(new Dataset.Builder()
+ *         .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
+ *         .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
+ *         .build())
+ *     .build();
+ * </pre>
+ *
+ * <p>But if the user had 2 accounts instead, the response could be:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ *     .addDataset(new Dataset.Builder()
+ *         .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
+ *         .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
+ *         .build())
+ *     .addDataset(new Dataset.Builder()
+ *         .setValue(id1, AutofillValue.forText("flanders"), createPresentation("flanders"))
+ *         .setValue(id2, AutofillValue.forText("OkelyDokelyDo"), createPresentation("password for flanders"))
+ *         .build())
+ *     .build();
+ * </pre>
+ *
+ * <p>If the service does not find any autofillable view in the view structure, it should pass
+ * {@code null} to {@link FillCallback#onSuccess(FillResponse)}; if the service encountered an error
+ * processing the request, it should call {@link FillCallback#onFailure(CharSequence)}. For
+ * performance reasons, it's paramount that the service calls either
+ * {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)} for
+ * each {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} received - if it
+ * doesn't, the request will eventually time out and be discarded by the Android System.
+ *
+ * <h3>Saving user data</h3>
+ *
+ * <p>If the service is also interested on saving the data filled by the user, it must set a
+ * {@link SaveInfo} object in the {@link FillResponse}. See {@link SaveInfo} for more details and
+ * examples.
+ *
+ * <h3>User authentication</h3>
+ *
+ * <p>The service can provide an extra degree of security by requiring the user to authenticate
+ * before an app can be autofilled. The authentication is typically required in 2 scenarios:
+ * <ul>
+ *   <li>To unlock the user data (for example, using a master password or fingerprint
+ *       authentication) - see
+ * {@link FillResponse.Builder#setAuthentication(AutofillId[], android.content.IntentSender, android.widget.RemoteViews)}.
+ *   <li>To unlock a specific dataset (for example, by providing a CVC for a credit card) - see
+ *       {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}.
+ * </ul>
+ *
+ * <p>When using authentication, it is recommended to encrypt only the sensitive data and leave
+ * labels unencrypted, so they can be used on presentation views. For example, if the user has a
+ * home and a work address, the {@code Home} and {@code Work} labels should be stored unencrypted
+ * (since they don't have any sensitive data) while the address data per se could be stored in an
+ * encrypted storage. Then when the user chooses the {@code Home} dataset, the platform starts
+ * the authentication flow, and the service can decrypt the sensitive data.
+ *
+ * <p>The authentication mechanism can also be used in scenarios where the service needs multiple
+ * steps to determine the datasets that can fill a screen. For example, when autofilling a financial
+ * app where the user has accounts for multiple banks, the workflow could be:
+ *
+ * <ol>
+ *   <li>The first {@link FillResponse} contains datasets with the credentials for the financial
+ *       app, plus a "fake" dataset whose presentation says "Tap here for banking apps credentials".
+ *   <li>When the user selects the fake dataset, the service displays a dialog with available
+ *       banking apps.
+ *   <li>When the user select a banking app, the service replies with a new {@link FillResponse}
+ *       containing the datasets for that bank.
+ * </ol>
+ *
+ * <p>Another example of multiple-steps dataset selection is when the service stores the user
+ * credentials in "vaults": the first response would contain fake datasets with the vault names,
+ * and the subsequent response would contain the app credentials stored in that vault.
+ *
+ * <h3>Data partitioning</h3>
+ *
+ * <p>The autofillable views in a screen should be grouped in logical groups called "partitions".
+ * Typical partitions are:
+ * <ul>
+ *   <li>Credentials (username/email address, password).
+ *   <li>Address (street, city, state, zip code, etc).
+ *   <li>Payment info (credit card number, expiration date, and verification code).
+ * </ul>
+ * <p>For security reasons, when a screen has more than one partition, it's paramount that the
+ * contents of a dataset do not spawn multiple partitions, specially when one of the partitions
+ * contains data that is not specific to the application being autofilled. For example, a dataset
+ * should not contain fields for username, password, and credit card information. The reason for
+ * this rule is that a malicious app could draft a view structure where the credit card fields
+ * are not visible, so when the user selects a dataset from the username UI, the credit card info is
+ * released to the application without the user knowledge. Similar, it's recommended to always
+ * protect a dataset that contains sensitive information by requiring dataset authentication
+ * (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}).
+ *
+ * <p>When the service detects that a screen have multiple partitions, it should return a
+ * {@link FillResponse} with just the datasets for the partition that originated the request (i.e.,
+ * the partition that has the {@link android.app.assist.AssistStructure.ViewNode} whose
+ * {@link android.app.assist.AssistStructure.ViewNode#isFocused()} returns {@code true}); then if
+ * the user selects a field from a different partition, the Android System will make another
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} call for that partition,
+ * and so on.
+ *
+ * <p>Notice that when the user autofill a partition with the data provided by the service and the
+ * user did not change these fields, the autofilled value is sent back to the service in the
+ * subsequent calls (and can be obtained by calling
+ * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}). This is useful in the
+ * cases where the service must create datasets for a partition based on the choice made in a
+ * previous partition. For example, the 1st response for a screen that have credentials and address
+ * partitions could be:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ *     .addDataset(new Dataset.Builder() // partition 1 (credentials)
+ *         .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
+ *         .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
+ *         .build())
+ *     .addDataset(new Dataset.Builder() // partition 1 (credentials)
+ *         .setValue(id1, AutofillValue.forText("flanders"), createPresentation("flanders"))
+ *         .setValue(id2, AutofillValue.forText("OkelyDokelyDo"), createPresentation("password for flanders"))
+ *         .build())
+ *     .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_PASSWORD,
+ *         new AutofillId[] { id1, id2 })
+ *             .build())
+ *     .build();
+ * </pre>
+ *
+ * <p>Then if the user selected {@code flanders}, the service would get a new
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} call, with the values of
+ * the fields {@code id1} and {@code id2} prepopulated, so the service could then fetch the address
+ * for the Flanders account and return the following {@link FillResponse} for the address partition:
+ *
+ * <pre class="prettyprint">
+ * new FillResponse.Builder()
+ *     .addDataset(new Dataset.Builder() // partition 2 (address)
+ *         .setValue(id3, AutofillValue.forText("744 Evergreen Terrace"), createPresentation("744 Evergreen Terrace")) // street
+ *         .setValue(id4, AutofillValue.forText("Springfield"), createPresentation("Springfield")) // city
+ *         .build())
+ *     .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_PASSWORD | SaveInfo.SAVE_DATA_TYPE_ADDRESS,
+ *         new AutofillId[] { id1, id2 }) // username and password
+ *              .setOptionalIds(new AutofillId[] { id3, id4 }) // state and zipcode
+ *             .build())
+ *     .build();
+ * </pre>
+ *
+ * <p>When the service returns multiple {@link FillResponse}, the last one overrides the previous;
+ * that's why the {@link SaveInfo} in the 2nd request above has the info for both partitions.
+ *
+ * <h3>Ignoring views</h3>
+ *
+ * <p>If the service find views that cannot be autofilled (for example, a text field representing
+ * the response to a Captcha challenge), it should mark those views as ignored by
+ * calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger
+ * a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are
+ * focused.
  */
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
@@ -132,11 +338,6 @@
 
     private HandlerCaller mHandlerCaller;
 
-    /**
-     * {@inheritDoc}
-     *
-     * <strong>NOTE: </strong>if overridden, it must call {@code super.onCreate()}.
-     */
     @CallSuper
     @Override
     public void onCreate() {
@@ -162,8 +363,7 @@
     }
 
     /**
-     * Called by the Android system do decide if an {@link Activity} can be autofilled by the
-     * service.
+     * Called by the Android system do decide if a screen can be autofilled by the service.
      *
      * <p>Service must call one of the {@link FillCallback} methods (like
      * {@link FillCallback#onSuccess(FillResponse)}
@@ -181,7 +381,7 @@
             @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
 
     /**
-     * Called when user requests service to save the fields of an {@link Activity}.
+     * Called when user requests service to save the fields of a screen.
      *
      * <p>Service must call one of the {@link SaveCallback} methods (like
      * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
@@ -226,7 +426,7 @@
      * @return The history or {@code null} if there are no events.
      */
     @Nullable public final FillEventHistory getFillEventHistory() {
-        AutofillManager afm = getSystemService(AutofillManager.class);
+        final AutofillManager afm = getSystemService(AutofillManager.class);
 
         if (afm == null) {
             return null;
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index af2eb34..a2ec099 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -31,17 +31,23 @@
 import java.util.ArrayList;
 
 /**
- * A set of data that can be used to autofill an {@link android.app.Activity}.
+ * A dataset object represents a group of key/value pairs used to autofill parts of a screen.
  *
- * <p>It contains:
+ * <p>In its simplest form, a dataset contains one or more key / value pairs (comprised of
+ * {@link AutofillId} and {@link AutofillValue} respectively); and one or more
+ * {@link RemoteViews presentation} for these pairs (a pair could have its own
+ * {@link RemoteViews presentation}, or use the default {@link RemoteViews presentation} associated
+ * with the whole dataset). When an autofill service returns datasets in a {@link FillResponse}
+ * and the screen input is focused in a view that is present in at least one of these datasets,
+ * the Android System displays a UI affordance containing the {@link RemoteViews presentation} of
+ * all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a
+ * dataset from the affordance, all views in that dataset are autofilled.
  *
- * <ol>
- *   <li>A list of values for input fields.
- *   <li>A presentation view to visualize.
- *   <li>An optional intent to authenticate.
- * </ol>
+ * <p>In a more sophisticated form, the dataset value can be protected until the user authenticates
+ * the dataset - see {@link Dataset.Builder#setAuthentication(IntentSender)}.
  *
- * @see android.service.autofill.FillResponse for examples.
+ * @see android.service.autofill.AutofillService for more information and examples about the
+ * role of datasets in the autofill workflow.
  */
 public final class Dataset implements Parcelable {
 
@@ -113,7 +119,7 @@
     }
 
     /**
-     * A builder for {@link Dataset} objects. You must to provide at least
+     * A builder for {@link Dataset} objects. You must provide at least
      * one value for a field or set an authentication intent.
      */
     public static final class Builder {
@@ -175,9 +181,9 @@
          * credit card information without the CVV for the data set in the {@link FillResponse
          * response} then the returned data set should contain the CVV entry.
          *
-         * <p></><strong>Note:</strong> Do not make the provided pending intent
+         * <p><b>NOTE:</b> Do not make the provided pending intent
          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
-         * platform needs to fill in the authentication arguments.</p>
+         * platform needs to fill in the authentication arguments.
          *
          * @param authentication Intent to an activity with your authentication flow.
          * @return This builder.
@@ -191,7 +197,7 @@
         }
 
         /**
-         * Sets the id for the dataset.
+         * Sets the id for the dataset so its usage history can be retrieved later.
          *
          * <p>The id of the last selected dataset can be read from
          * {@link AutofillService#getFillEventHistory()}. If the id is not set it will not be clear
@@ -214,13 +220,12 @@
          *
          * @param id id returned by {@link
          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
-         * @param value value to be auto filled. Pass {@code null} if you do not have the value
+         * @param value value to be autofilled. Pass {@code null} if you do not have the value
          *        but the target view is a logical part of the dataset. For example, if
          *        the dataset needs an authentication and you have no access to the value.
-         *        Filtering matches any user typed string to {@code null} values.
          * @return This builder.
-         * @throws IllegalStateException if the builder was constructed without a presentation
-         * ({@link RemoteViews}).
+         * @throws IllegalStateException if the builder was constructed without a
+         * {@link RemoteViews presentation}.
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
             throwIfDestroyed();
@@ -232,7 +237,8 @@
         }
 
         /**
-         * Sets the value of a field, using a custom presentation to visualize it.
+         * Sets the value of a field, using a custom {@link RemoteViews presentation} to
+         * visualize it.
          *
          * @param id id returned by {@link
          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
@@ -272,10 +278,12 @@
         }
 
         /**
-         * Creates a new {@link Dataset} instance. You should not interact
-         * with this builder once this method is called. It is required
-         * that you specified at least one field. Also it is mandatory to
-         * provide a presentation view to visualize the data set in the UI.
+         * Creates a new {@link Dataset} instance.
+         *
+         * <p>You should not interact with this builder once this method is called.
+         *
+         * <p>It is required that you specify at least one field before calling this method. It's
+         * also mandatory to provide a presentation view to visualize the data set in the UI.
          *
          * @return The built dataset.
          */
diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java
index f8a8751..cda2f4a 100644
--- a/core/java/android/service/autofill/FillContext.java
+++ b/core/java/android/service/autofill/FillContext.java
@@ -30,7 +30,6 @@
 import android.util.SparseIntArray;
 import android.view.autofill.AutofillId;
 
-import java.util.ArrayList;
 import java.util.LinkedList;
 
 /**
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index b1145ee..fd6da05 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -23,6 +23,7 @@
 import android.os.CancellationSignal;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.View;
 
 import com.android.internal.util.Preconditions;
 
@@ -32,7 +33,7 @@
 import java.util.List;
 
 /**
- * This class represents a request to an {@link AutofillService autofill provider}
+ * This class represents a request to an autofill service
  * to interpret the screen and provide information to the system which views are
  * interesting for saving and what are the possible ways to fill the inputs on
  * the screen if applicable.
@@ -40,8 +41,29 @@
  * @see AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
  */
 public final class FillRequest implements Parcelable {
+
     /**
      * Indicates autofill was explicitly requested by the user.
+     *
+     * <p>Users typically make an explicit request to autofill a screen in two situations:
+     * <ul>
+     *   <li>The app disabled autofill (using {@link View#setImportantForAutofill(int)}.
+     *   <li>The service could not figure out how to autofill a screen (but the user knows the
+     *       service has data for that app).
+     * </ul>
+     *
+     * <p>This flag is particularly useful for the second case. For example, the service could offer
+     * a complex UI where the user can map which screen views belong to each user data, or it could
+     * offer a simpler UI where the user picks the data for just the view used to trigger the
+     * request (that would be the view whose
+     * {@link android.app.assist.AssistStructure.ViewNode#isFocused()} method returns {@code true}).
+     *
+     * <p>An explicit autofill request is triggered when the
+     * {@link android.view.autofill.AutofillManager#requestAutofill(View)} or
+     * {@link android.view.autofill.AutofillManager#requestAutofill(View, int, android.graphics.Rect)}
+     * is called. For example, standard {@link android.widget.TextView} views that use
+     * an {@link android.widget.Editor} shows an {@code AUTOFILL} option in the overflow menu that
+     * triggers such request.
      */
     public static final int FLAG_MANUAL_REQUEST = 0x1;
 
@@ -79,14 +101,14 @@
     }
 
     /**
-     * @return The unique id of this request.
+     * Gets the unique id of this request.
      */
     public int getId() {
         return mId;
     }
 
     /**
-     * @return The flags associated with this request.
+     * Gets the flags associated with this request.
      *
      * @see #FLAG_MANUAL_REQUEST
      */
@@ -95,7 +117,7 @@
     }
 
     /**
-     * @return The contexts associated with each previous fill request.
+     * Gets the contexts associated with each previous fill request.
      */
     public @NonNull List<FillContext> getFillContexts() {
         return mContexts;
@@ -104,10 +126,10 @@
     /**
      * Gets the extra client state returned from the last {@link
      * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
-     * fill request}.
-     * <p>
-     * Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
-     * save request} is made the client state is cleared.
+     * fill request}, so the service can use it for state management.
+     *
+     * <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
+     * save request} is made, the client state is cleared.
      *
      * @return The client state.
      */
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index fcf18eb..e13fdf6 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Activity;
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -36,100 +37,7 @@
  * Response for a {@link
  * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
  *
- * <p>The response typically contains one or more {@link Dataset}s, each representing a set of
- * fields that can be autofilled together, and the Android system displays a dataset picker UI
- * affordance that the user must use before the {@link android.app.Activity} is filled with
- * the dataset.
- *
- * <p>For example, for a login page with username/password where the user only has one account in
- * the response could be:
- *
- * <pre class="prettyprint">
- *  new FillResponse.Builder()
- *      .add(new Dataset.Builder(createPresentation())
- *          .setValue(id1, AutofillValue.forText("homer"))
- *          .setValue(id2, AutofillValue.forText("D'OH!"))
- *          .build())
- *      .build();
- * </pre>
- *
- * <p>If the user had 2 accounts, each with its own user-provided names, the response could be:
- *
- * <pre class="prettyprint">
- *  new FillResponse.Builder()
- *      .add(new Dataset.Builder(createFirstPresentation())
- *          .setValue(id1, AutofillValue.forText("homer"))
- *          .setValue(id2, AutofillValue.forText("D'OH!"))
- *          .build())
- *      .add(new Dataset.Builder(createSecondPresentation())
- *          .setValue(id1, AutofillValue.forText("elbarto")
- *          .setValue(id2, AutofillValue.forText("cowabonga")
- *          .build())
- *      .build();
- * </pre>
- *
- * If the service is interested on saving the user-edited data back, it must set a {@link SaveInfo}
- * in the {@link FillResponse}. Typically, the {@link SaveInfo} contains the same ids as the
- * {@link Dataset}, but other combinations are possible - see {@link SaveInfo} for more details
- *
- * <p>If the service has multiple {@link Dataset}s for different sections of the activity,
- * for example, a user section for which there are two datasets followed by an address
- * section for which there are two datasets for each user user, then it should "partition"
- * the activity in sections and populate the response with just a subset of the data that would
- * fulfill the first section (the name in our example); then once the user fills the first
- * section and taps a field from the next section (the address in our example), the Android
- * system would issue another request for that section, and so on. Note that if the user
- * chooses to populate the first section with a service provided dataset, the subsequent request
- * would contain the populated values so you don't try to provide suggestions for the first
- * section but ony for the second one based on the context of what was already filled. For
- * example, the first response could be:
- *
- * <pre class="prettyprint">
- *  new FillResponse.Builder()
- *      .add(new Dataset.Builder(createFirstPresentation())
- *          .setValue(id1, AutofillValue.forText("Homer"))
- *          .setValue(id2, AutofillValue.forText("Simpson"))
- *          .build())
- *      .add(new Dataset.Builder(createSecondPresentation())
- *          .setValue(id1, AutofillValue.forText("Bart"))
- *          .setValue(id2, AutofillValue.forText("Simpson"))
- *          .build())
- *      .build();
- * </pre>
- *
- * <p>Then after the user picks the second dataset and taps the street field to
- * trigger another autofill request, the second response could be:
- *
- * <pre class="prettyprint">
- *  new FillResponse.Builder()
- *      .add(new Dataset.Builder(createThirdPresentation())
- *          .setValue(id3, AutofillValue.forText("742 Evergreen Terrace"))
- *          .setValue(id4, AutofillValue.forText("Springfield"))
- *          .build())
- *      .add(new Dataset.Builder(createFourthPresentation())
- *          .setValue(id3, AutofillValue.forText("Springfield Power Plant"))
- *          .setValue(id4, AutofillValue.forText("Springfield"))
- *          .build())
- *      .build();
- * </pre>
- *
- * <p>The service could require user authentication at the {@link FillResponse} or the
- * {@link Dataset} level, prior to autofilling an activity - see
- * {@link FillResponse.Builder#setAuthentication(AutofillId[], IntentSender, RemoteViews)} and
- * {@link Dataset.Builder#setAuthentication(IntentSender)}.
- *
- * <p>It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
- * which would allow you to provide a dataset presentation views with labels and if the user
- * chooses one of them challenge the user to authenticate. For example, if the user has a
- * home and a work address the Home and Work labels could be stored unencrypted as they don't
- * have any sensitive data while the address data is in an encrypted storage. If the user
- * chooses Home, then the platform will start your authentication flow. If you encrypt all
- * data and require auth at the response level the user will have to interact with the fill
- * UI to trigger a request for the datasets (as they don't see the presentation views for the
- * possible options) which will start your auth flow and after successfully authenticating
- * the user will be presented with the Home and Work options to pick one. Hence, you have
- * flexibility how to implement your auth while storing labels non-encrypted and data
- * encrypted provides a better user experience.
+ * <p>See the main {@link AutofillService} documentation for more details and examples.
  */
 public final class FillResponse implements Parcelable {
 
@@ -221,7 +129,7 @@
         private boolean mDestroyed;
 
         /**
-         * Requires a fill response authentication before autofilling the activity with
+         * Requires a fill response authentication before autofilling the screen with
          * any data set in this response.
          *
          * <p>This is typically useful when a user interaction is required to unlock their
@@ -230,16 +138,16 @@
          * auth on the data set level leading to a better user experience. Note that if you
          * use sensitive data as a label, for example an email address, then it should also
          * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
-         * activity which implements your authentication flow. Also if you provide an auth
+         * {@link Activity} which implements your authentication flow. Also if you provide an auth
          * intent you also need to specify the presentation view to be shown in the fill UI
          * for the user to trigger your authentication flow.
          *
          * <p>When a user triggers autofill, the system launches the provided intent
          * whose extras will have the {@link AutofillManager#EXTRA_ASSIST_STRUCTURE screen
          * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
-         * client state}. Once you complete your authentication flow you should set the activity
-         * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated
-         * {@link FillResponse response} by setting it to the {@link
+         * client state}. Once you complete your authentication flow you should set the
+         * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and provide the fully
+         * populated {@link FillResponse response} by setting it to the {@link
          * AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra.
          * For example, if you provided an empty {@link FillResponse resppnse} because the
          * user's data was locked and marked that the response needs an authentication then
@@ -286,8 +194,8 @@
          * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
          * FillCallback)} requests.
          *
-         * <p>This is typically used when the service cannot autofill the view; for example, an
-         * {@code EditText} representing a captcha.
+         * <p>This is typically used when the service cannot autofill the view; for example, a
+         * text field representing the result of a Captcha challenge.
          */
         public Builder setIgnoredIds(AutofillId...ids) {
             mIgnoredIds = ids;
@@ -316,8 +224,6 @@
         /**
          * Sets the {@link SaveInfo} associated with this response.
          *
-         * <p>See {@link FillResponse} for more info.
-         *
          * @return This builder.
          */
         public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
@@ -335,7 +241,7 @@
          * fill requests and the subsequent save request.
          *
          * <p>If this method is called on multiple {@link FillResponse} objects for the same
-         * activity, just the latest bundle is passed back to the service.
+         * screen, just the latest bundle is passed back to the service.
          *
          * <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
          * save request} is made the client state is cleared.
@@ -350,9 +256,10 @@
         }
 
         /**
-         * Builds a new {@link FillResponse} instance. You must provide at least
-         * one dataset or some savable ids or an authentication with a presentation
-         * view.
+         * Builds a new {@link FillResponse} instance.
+         *
+         * <p>You must provide at least one dataset or some savable ids or an authentication with a
+         * presentation view.
          *
          * @return A built response.
          */
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 6ea7d5e..95d393b 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Activity;
 import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -45,70 +46,91 @@
  * two pieces of information:
  *
  * <ol>
- *   <li>The type of user data that would be saved (like passoword or credit card info).
+ *   <li>The type(s) of user data (like password or credit card info) that would be saved.
  *   <li>The minimum set of views (represented by their {@link AutofillId}) that need to be changed
  *       to trigger a save request.
  * </ol>
  *
- *  Typically, the {@link SaveInfo} contains the same {@code id}s as the {@link Dataset}:
+ * <p>Typically, the {@link SaveInfo} contains the same {@code id}s as the {@link Dataset}:
  *
  * <pre class="prettyprint">
- *  new FillResponse.Builder()
- *      .add(new Dataset.Builder(createPresentation())
- *          .setValue(id1, AutofillValue.forText("homer"))
- *          .setValue(id2, AutofillValue.forText("D'OH!"))
- *          .build())
- *      .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_PASSWORD, new int[] {id1, id2})
- *                  .build())
- *      .build();
+ *   new FillResponse.Builder()
+ *       .addDataset(new Dataset.Builder()
+ *           .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer")) // username
+ *           .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer")) // password
+ *           .build())
+ *       .setSaveInfo(new SaveInfo.Builder(
+ *           SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD,
+ *           new AutofillId[] { id1, id2 }).build())
+ *       .build();
  * </pre>
  *
- * There might be cases where the {@link AutofillService} knows how to fill the
- * {@link android.app.Activity}, but the user has no data for it. In that case, the
- * {@link FillResponse} should contain just the {@link SaveInfo}, but no {@link Dataset}s:
+ * <p>The save type flags are used to display the appropriate strings in the save UI affordance.
+ * You can pass multiple values, but try to keep it short if possible. In the above example, just
+ * {@code SaveInfo.SAVE_DATA_TYPE_PASSWORD} would be enough.
+ *
+ * <p>There might be cases where the {@link AutofillService} knows how to fill the screen,
+ * but the user has no data for it. In that case, the {@link FillResponse} should contain just the
+ * {@link SaveInfo}, but no {@link Dataset Datasets}:
  *
  * <pre class="prettyprint">
- *  new FillResponse.Builder()
- *      .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_PASSWORD, new int[] {id1, id2})
- *                  .build())
- *      .build();
+ *   new FillResponse.Builder()
+ *       .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_PASSWORD,
+ *           new AutofillId[] { id1, id2 }).build())
+ *       .build();
  * </pre>
  *
  * <p>There might be cases where the user data in the {@link AutofillService} is enough
  * to populate some fields but not all, and the service would still be interested on saving the
- * other fields. In this scenario, the service could set the
+ * other fields. In that case, the service could set the
  * {@link SaveInfo.Builder#setOptionalIds(AutofillId[])} as well:
  *
  * <pre class="prettyprint">
  *   new FillResponse.Builder()
- *       .add(new Dataset.Builder(createPresentation())
- *          .setValue(id1, AutofillValue.forText("742 Evergreen Terrace"))  // street
- *          .setValue(id2, AutofillValue.forText("Springfield"))            // city
- *          .build())
- *       .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_ADDRESS, new int[] {id1, id2})
- *                   .setOptionalIds(new int[] {id3, id4}) // state and zipcode
- *                   .build())
+ *       .addDataset(new Dataset.Builder()
+ *           .setValue(id1, AutofillValue.forText("742 Evergreen Terrace"),
+ *               createPresentation("742 Evergreen Terrace")) // street
+ *           .setValue(id2, AutofillValue.forText("Springfield"),
+ *               createPresentation("Springfield")) // city
+ *           .build())
+ *       .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_ADDRESS,
+ *           new AutofillId[] { id1, id2 }) // street and  city
+ *           .setOptionalIds(new AutofillId[] { id3, id4 }) // state and zipcode
+ *           .build())
  *       .build();
  * </pre>
  *
- * The
- * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
- * is triggered after a call to {@link AutofillManager#commit()}, but only when all conditions
- * below are met:
+ * <p>The {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} can be triggered after
+ * any of the following events:
+ * <ul>
+ *   <li>The {@link Activity} finishes.
+ *   <li>The app explicitly called {@link AutofillManager#commit()}.
+ *   <li>All required views became invisible (if the {@link SaveInfo} was created with the
+ *       {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} flag).
+ * </ul>
  *
- * <ol>
+ * <p>But it is only triggered when all conditions below are met:
+ * <ul>
  *   <li>The {@link SaveInfo} associated with the {@link FillResponse} is not {@code null}.
- *   <li>The {@link AutofillValue} of all required views (as set by the {@code requiredIds} passed
- *       to {@link SaveInfo.Builder} constructor are not empty.
+ *   <li>The {@link AutofillValue}s of all required views (as set by the {@code requiredIds} passed
+ *       to the {@link SaveInfo.Builder} constructor are not empty.
  *   <li>The {@link AutofillValue} of at least one view (be it required or optional) has changed
- *       (i.e., it's not the same value passed in a {@link Dataset}).
- *   <li>The user explicitly tapped the affordance asking to save data for autofill.
- * </ol>
+ *       (i.e., it's neither the same value passed in a {@link Dataset}, nor the initial value
+ *       presented in the view).
+ *   <li>The user explicitly tapped the UI affordance asking to save data for autofill.
+ * </ul>
+ *
+ * <p>The service can also customize some aspects of the save UI affordance:
+ * <ul>
+ *   <li>Add a subtitle by calling {@link Builder#setDescription(CharSequence)}.
+ *   <li>Customize the button used to reject the save request by calling
+ *       {@link Builder#setNegativeAction(int, IntentSender)}.
+ * </ul>
  */
 public final class SaveInfo implements Parcelable {
 
     /**
-     * Type used on when the service can save the contents of an activity, but cannot describe what
+     * Type used when the service can save the contents of a screen, but cannot describe what
      * the content is for.
      */
     public static final int SAVE_DATA_TYPE_GENERIC = 0x0;
@@ -181,8 +203,8 @@
 
     /**
      * Usually {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
-     * is called once the activity finishes. If this flag is set it is called once all saved views
-     * become invisible.
+     * is called once the {@link Activity} finishes. If this flag is set it is called once all
+     * saved views become invisible.
      */
     public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x1;
 
@@ -294,9 +316,9 @@
         }
 
         /**
-         * Set flags changing the save behavior.
+         * Sets flags changing the save behavior.
          *
-         * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or 0.
+         * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or {@code 0}.
          * @return This builder.
          */
         public @NonNull Builder setFlags(@SaveInfoFlags int flags) {
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 2e8faee..0d67615 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -25,6 +25,7 @@
 import android.database.DataSetObserver;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Handler;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -532,8 +533,14 @@
     public void setHeight(int height) {
         if (height < 0 && ViewGroup.LayoutParams.WRAP_CONTENT != height
                 && ViewGroup.LayoutParams.MATCH_PARENT != height) {
-            throw new IllegalArgumentException(
-                   "Invalid height. Must be a positive value, MATCH_PARENT, or WRAP_CONTENT.");
+            if (mContext.getApplicationInfo().targetSdkVersion
+                    < Build.VERSION_CODES.O) {
+                Log.e(TAG, "Negative value " + height + " passed to ListPopupWindow#setHeight"
+                        + " produces undefined results");
+            } else {
+                throw new IllegalArgumentException(
+                        "Invalid height. Must be a positive value, MATCH_PARENT, or WRAP_CONTENT.");
+            }
         }
         mDropDownHeight = height;
     }
diff --git a/packages/SystemUI/res/drawable/pip_icon.xml b/packages/SystemUI/res/drawable/pip_icon.xml
new file mode 100644
index 0000000..bd92ccd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/pip_icon.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="36dp"
+    android:height="36dp"
+    android:viewportWidth="25"
+    android:viewportHeight="25">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M19,7h-8v6h8L19,7zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.98h18v14.03z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index f0745a0..ac41b75 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -174,7 +174,7 @@
     void onConfigurationChanged(Context context) {
         Resources res = context.getResources();
         mDefaultTitle = res.getString(R.string.pip_notification_unknown_title);
-        mDefaultIconResId = R.drawable.pip_expand;
+        mDefaultIconResId = R.drawable.pip_icon;
         if (mNotified) {
             // update notification
             notifyPipNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index bbcbfd6..1a99636 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -454,7 +454,8 @@
                     || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) {
                 iconState.hidden = true;
             }
-            int shelfColor = icon.getStaticDrawableColor();
+            int backgroundColor = getBackgroundColorWithoutTint();
+            int shelfColor = icon.getContrastedStaticDrawableColor(backgroundColor);
             if (!noIcon && shelfColor != StatusBarIconView.NO_COLOR) {
                 int iconColor = row.getVisibleNotificationHeader().getOriginalIconColor();
                 shelfColor = NotificationUtils.interpolateColors(iconColor, shelfColor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 3c7ddb5..1a47e44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -35,6 +35,7 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
+import android.support.v4.graphics.ColorUtils;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -46,6 +47,7 @@
 import android.view.animation.Interpolator;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
@@ -127,6 +129,8 @@
         setColorInternal(newColor);
     };
     private final NotificationIconDozeHelper mDozer;
+    private int mContrastedDrawableColor;
+    private int mCachedContrastBackgroundColor = NO_COLOR;
 
     public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
         this(context, slot, sbn, false);
@@ -528,6 +532,7 @@
     public void setStaticDrawableColor(int color) {
         mDrawableColor = color;
         setColorInternal(color);
+        updateContrastedStaticColor();
         mIconColor = color;
         mDozer.setColor(color);
     }
@@ -580,6 +585,42 @@
         return mDrawableColor;
     }
 
+    /**
+     * A drawable color that passes GAR on a specific background.
+     * This value is cached.
+     *
+     * @param backgroundColor Background to test against.
+     * @return GAR safe version of {@link StatusBarIconView#getStaticDrawableColor()}.
+     */
+    int getContrastedStaticDrawableColor(int backgroundColor) {
+        if (mCachedContrastBackgroundColor != backgroundColor) {
+            mCachedContrastBackgroundColor = backgroundColor;
+            updateContrastedStaticColor();
+        }
+        return mContrastedDrawableColor;
+    }
+
+    private void updateContrastedStaticColor() {
+        if (mCachedContrastBackgroundColor == NO_COLOR) {
+            return;
+        }
+        // We'll modify the color if it doesn't pass GAR
+        int contrastedColor = mDrawableColor;
+        if (!NotificationColorUtil.satisfiesTextContrast(mCachedContrastBackgroundColor,
+                contrastedColor)) {
+            float[] hsl = new float[3];
+            ColorUtils.colorToHSL(mDrawableColor, hsl);
+            // This is basically a light grey, pushing the color will only distort it.
+            // Best thing to do in here is to fallback to the default color.
+            if (hsl[1] < 0.2f) {
+                contrastedColor = Notification.COLOR_DEFAULT;
+            }
+            contrastedColor = NotificationColorUtil.resolveContrastColor(mContext,
+                    contrastedColor, mCachedContrastBackgroundColor);
+        }
+        mContrastedDrawableColor = contrastedColor;
+    }
+
     public void setVisibleState(int state) {
         setVisibleState(state, true /* animate */, null /* endRunnable */);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 3f5f5a0..874f0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -31,8 +31,10 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
 
 import com.android.systemui.R;
+import com.android.systemui.util.Utils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -141,7 +143,8 @@
     /**
      * Returns true if there currently exist active high power location requests.
      */
-    private boolean areActiveHighPowerLocationRequests() {
+    @VisibleForTesting
+    protected boolean areActiveHighPowerLocationRequests() {
         List<AppOpsManager.PackageOps> packages
             = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
         // AppOpsManager can return null when there is no requested data.
@@ -205,16 +208,14 @@
         }
 
         private void locationActiveChanged() {
-            for (LocationChangeCallback cb : mSettingsChangeCallbacks) {
-                cb.onLocationActiveChanged(mAreActiveLocationRequests);
-            }
+            Utils.safeForeach(mSettingsChangeCallbacks,
+                    cb -> cb.onLocationActiveChanged(mAreActiveLocationRequests));
         }
 
         private void locationSettingsChanged() {
             boolean isEnabled = isLocationEnabled();
-            for (LocationChangeCallback cb : mSettingsChangeCallbacks) {
-                cb.onLocationSettingsChanged(isEnabled);
-            }
+            Utils.safeForeach(mSettingsChangeCallbacks,
+                    cb -> cb.onLocationSettingsChanged(isEnabled));
         }
     }
 }
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 612a54a..b12fd1c 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -37,6 +37,7 @@
     <uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" />
     <uses-permission android:name="android.permission.CONTROL_VPN" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 68f9cb05..8e7ffdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -16,8 +16,10 @@
 
 package com.android.systemui.statusbar;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,12 +35,14 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.graphics.drawable.Icon;
 import android.os.UserHandle;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
@@ -100,4 +104,17 @@
 
         assertFalse(mIconView.set(mStatusBarIcon));
     }
+
+    @Test
+    public void testGetContrastedStaticDrawableColor() {
+        mIconView.setStaticDrawableColor(Color.DKGRAY);
+        int color = mIconView.getContrastedStaticDrawableColor(Color.WHITE);
+        assertEquals("Color should not change when we have enough contrast",
+                Color.DKGRAY, color);
+
+        mIconView.setStaticDrawableColor(Color.WHITE);
+        color = mIconView.getContrastedStaticDrawableColor(Color.WHITE);
+        assertTrue("Similar colors should be shifted to satisfy contrast",
+                NotificationColorUtil.satisfiesTextContrast(Color.WHITE, color));
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
new file mode 100644
index 0000000..a10bebf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/LocationControllerImplTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 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.systemui.statusbar.policy;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.location.LocationManager;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class LocationControllerImplTest extends SysuiTestCase {
+
+    private LocationControllerImpl mLocationController;
+
+    @Before
+    public void setup() {
+        mLocationController = spy(new LocationControllerImpl(mContext,
+                TestableLooper.get(this).getLooper()));
+    }
+
+    @Test
+    public void testRemoveSelfActive_DoesNotCrash() {
+        LocationController.LocationChangeCallback callback = new LocationChangeCallback() {
+            @Override
+            public void onLocationActiveChanged(boolean active) {
+                mLocationController.removeCallback(this);
+            }
+        };
+        mLocationController.addCallback(callback);
+        mLocationController.addCallback(mock(LocationChangeCallback.class));
+
+        when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(false);
+        mLocationController.onReceive(mContext, new Intent(
+                LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION));
+        when(mLocationController.areActiveHighPowerLocationRequests()).thenReturn(true);
+        mLocationController.onReceive(mContext, new Intent(
+                LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION));
+
+        TestableLooper.get(this).processAllMessages();
+    }
+
+    @Test
+    public void testRemoveSelfSettings_DoesNotCrash() {
+        LocationController.LocationChangeCallback callback = new LocationChangeCallback() {
+            @Override
+            public void onLocationSettingsChanged(boolean isEnabled) {
+                mLocationController.removeCallback(this);
+            }
+        };
+        mLocationController.addCallback(callback);
+        mLocationController.addCallback(mock(LocationChangeCallback.class));
+
+        TestableLooper.get(this).processAllMessages();
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 27602d3..70f6a04 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7179,16 +7179,13 @@
      */
     private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
             String ephemeralPkgName) {
-        // TODO: When adding on-demand split support for non-instant apps, remove this check
-        // and always apply post filtering
-        if (ephemeralPkgName == null) {
-            return resolveInfos;
-        }
         for (int i = resolveInfos.size() - 1; i >= 0; i--) {
             final ResolveInfo info = resolveInfos.get(i);
             final boolean isEphemeralApp = info.activityInfo.applicationInfo.isInstantApp();
+            // TODO: When adding on-demand split support for non-instant apps, remove this check
+            // and always apply post filtering
             // allow activities that are defined in the provided package
-            if (isEphemeralApp && ephemeralPkgName.equals(info.activityInfo.packageName)) {
+            if (isEphemeralApp) {
                 if (info.activityInfo.splitName != null
                         && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
                                 info.activityInfo.splitName)) {
@@ -7213,6 +7210,10 @@
                 }
                 continue;
             }
+            // caller is a full app, don't need to apply any other filtering
+            if (ephemeralPkgName == null) {
+                continue;
+            }
             // allow activities that have been explicitly exposed to ephemeral apps
             if (!isEphemeralApp
                     && ((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0)) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 6a7123c..7e575bf 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
@@ -37,6 +38,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.util.MergedConfiguration;
 import android.util.Slog;
@@ -85,6 +87,7 @@
     private boolean mClientDead = false;
     private float mLastReportedAnimatorScale;
     private String mPackageName;
+    private String mRelayoutTag;
 
     public Session(WindowManagerService service, IWindowSessionCallback callback,
             IInputMethodClient client, IInputContext inputContext) {
@@ -221,10 +224,12 @@
             MergedConfiguration mergedConfiguration, Surface outSurface) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                 outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
         return res;
@@ -550,6 +555,7 @@
 
     void windowAddedLocked(String packageName) {
         mPackageName = packageName;
+        mRelayoutTag = "relayoutWindow: " + mPackageName;
         if (mSurfaceSession == null) {
             if (WindowManagerService.localLOGV) Slog.v(
                 TAG_WM, "First window added to " + this + ", creating SurfaceSession");
@@ -673,6 +679,7 @@
                 pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
                 pw.print(" mClientDead="); pw.print(mClientDead);
                 pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
+        pw.print(prefix); pw.print("mPackageName="); pw.println(mPackageName);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ec7ab23..30027d5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -30,6 +30,7 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 import static android.os.Process.myPid;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
@@ -884,11 +885,16 @@
     }
 
     void openSurfaceTransaction() {
-        synchronized (mWindowMap) {
-            if (mRoot.mSurfaceTraceEnabled) {
-                mRoot.mRemoteEventTrace.openSurfaceTransaction();
+        try {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
+            synchronized (mWindowMap) {
+                if (mRoot.mSurfaceTraceEnabled) {
+                    mRoot.mRemoteEventTrace.openSurfaceTransaction();
+                }
+                SurfaceControl.openTransaction();
             }
-            SurfaceControl.openTransaction();
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -904,16 +910,21 @@
      *                     blocks and we call it repeatedly, like we do for animations.
      */
     void closeSurfaceTransaction(boolean withLockHeld) {
-        synchronized (mWindowMap) {
-            if (mRoot.mSurfaceTraceEnabled) {
-                mRoot.mRemoteEventTrace.closeSurfaceTransaction();
+        try {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
+            synchronized (mWindowMap) {
+                if (mRoot.mSurfaceTraceEnabled) {
+                    mRoot.mRemoteEventTrace.closeSurfaceTransaction();
+                }
+                if (withLockHeld) {
+                    SurfaceControl.closeTransaction();
+                }
             }
-            if (withLockHeld) {
+            if (!withLockHeld) {
                 SurfaceControl.closeTransaction();
             }
-        }
-        if (!withLockHeld) {
-            SurfaceControl.closeTransaction();
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -1988,6 +1999,8 @@
                     (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                             || !win.mAppToken.isClientHidden())) {
 
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
+
                 // We are about to create a surface, but we didn't run a layout yet. So better run
                 // a layout now that we already know the right size, as a resize call will make the
                 // surface transaction blocking until next vsync and slow us down.
@@ -1999,6 +2012,7 @@
                 }
                 result = win.relayoutVisibleWindow(mergedConfiguration, result, attrChanges,
                         oldVisibility);
+
                 try {
                     result = createSurfaceControl(outSurface, result, win, winAnimator);
                 } catch (Exception e) {
@@ -2018,7 +2032,10 @@
                     imMayMove = true;
                 }
                 win.adjustStartingWindowFlags();
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             } else {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_2");
+
                 winAnimator.mEnterAnimationPending = false;
                 winAnimator.mEnteringAnimation = false;
                 final boolean usingSavedSurfaceBeforeVisible =
@@ -2053,18 +2070,22 @@
                     // We already told the client to go invisible, but the message may not be
                     // handled yet, or it might want to draw a last frame. If we already have a
                     // surface, let the client use that, but don't create new surface at this point.
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
                     winAnimator.mSurfaceController.getSurface(outSurface);
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 } else {
                     if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
 
                     try {
-                        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
+                        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
                                 + win.mAttrs.getTitle());
                         outSurface.release();
                     } finally {
-                        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                     }
                 }
+
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
 
             if (focusMayChange) {
@@ -2101,8 +2122,11 @@
             }
 
             win.setDisplayLayoutNeeded();
-            win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
+            win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                    "relayoutWindow: updateOrientationFromAppTokens");
             configChanged = updateOrientationFromAppTokensLocked(false, displayId);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
             // We may be deferring layout passes at the moment, but since the client is interested
             // in the new out values right now we need to force a layout.
@@ -2155,7 +2179,9 @@
         }
 
         if (configChanged) {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: sendNewConfiguration");
             sendNewConfiguration(displayId);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
         Binder.restoreCallingIdentity(origId);
         return result;
@@ -2216,8 +2242,14 @@
         if (!win.mHasSurface) {
             result |= RELAYOUT_RES_SURFACE_CHANGED;
         }
-        WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked(
-            win.mAttrs.type, win.mOwnerUid);
+
+        WindowSurfaceController surfaceController;
+        try {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
+            surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
         if (surfaceController != null) {
             surfaceController.getSurface(outSurface);
             if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, "  OUT SURFACE " + outSurface + ": copied");
@@ -2227,6 +2259,7 @@
             Slog.w(TAG_WM, "Failed to create surface control for " + win);
             outSurface.release();
         }
+
         return result;
     }
 
@@ -2273,7 +2306,7 @@
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked");
         if (okToDisplay()) {
             final DisplayContent displayContent = atoken.getTask().getDisplayContent();
             final DisplayInfo displayInfo = displayContent.getDisplayInfo();
@@ -2329,7 +2362,7 @@
         } else {
             atoken.mAppAnimator.clearAnimation();
         }
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
         return atoken.mAppAnimator.animation != null;
     }
@@ -3412,7 +3445,7 @@
 
             if (!mBootAnimationStopped) {
                 // Do this one time.
-                Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
+                Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
                 try {
                     IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                     if (surfaceFlinger != null) {
@@ -3435,7 +3468,7 @@
             }
 
             EventLog.writeEvent(EventLogTags.WM_BOOT_ANIMATION_DONE, SystemClock.uptimeMillis());
-            Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
+            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
             mDisplayEnabled = true;
             if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM, "******************** ENABLING SCREEN!");
 
@@ -3666,12 +3699,12 @@
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
         try {
-            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
             return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */,
                     -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */,
                     Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */);
         } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -3857,6 +3890,8 @@
                 + " alwaysSendConfiguration=" + alwaysSendConfiguration
                 + " forceRelayout=" + forceRelayout);
 
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
+
         long origId = Binder.clearCallingIdentity();
 
         try {
@@ -3865,20 +3900,28 @@
             final int displayId;
             synchronized (mWindowMap) {
                 final DisplayContent displayContent = getDefaultDisplayContentLocked();
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
                 rotationChanged = displayContent.updateRotationUnchecked(
                         false /* inTransaction */);
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 if (!rotationChanged || forceRelayout) {
                     displayContent.setLayoutNeeded();
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                            "updateRotation: performSurfacePlacement");
                     mWindowPlacerLocked.performSurfacePlacement();
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 }
                 displayId = displayContent.getDisplayId();
             }
 
             if (rotationChanged || alwaysSendConfiguration) {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration");
                 sendNewConfiguration(displayId);
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -5806,7 +5849,7 @@
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
         WindowState newFocus = mRoot.computeFocusedWindow();
         if (mCurrentFocus != newFocus) {
-            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
             // This check makes sure that we don't already have the focus
             // change message pending.
             mH.removeMessages(H.REPORT_FOCUS_CHANGE);
@@ -5882,7 +5925,7 @@
             // other apps' UI.
             displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
 
-            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             return true;
         }
         return false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index edbdf8b..27927e6 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -33,6 +34,7 @@
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.Debug;
+import android.os.Trace;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -101,8 +103,10 @@
             mSurfaceControl = new SurfaceTrace(
                     s, name, w, h, format, flags, windowType, ownerUid);
         } else {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
             mSurfaceControl = new SurfaceControl(
                     s, name, w, h, format, flags, windowType, ownerUid);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         if (mService.mRoot.mSurfaceTraceEnabled) {