Merge changes I9c2ab16b,I6ac1fd46,I687412fc,I943f3098

* changes:
  Add link to dagger user guide
  Make QSTileImpl a LifecycleOwner and make use of it
  Remove Dependency.get usages from QS tiles
  Switch QS tile creation to use injection
diff --git a/api/current.txt b/api/current.txt
index cd33830..cec80c7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -51901,6 +51901,7 @@
   public abstract class Animation implements java.lang.Cloneable {
     ctor public Animation();
     ctor public Animation(android.content.Context, android.util.AttributeSet);
+    method public void addAnimationListener(android.view.animation.Animation.AnimationListener);
     method protected void applyTransformation(float, android.view.animation.Transformation);
     method public void cancel();
     method protected android.view.animation.Animation clone() throws java.lang.CloneNotSupportedException;
@@ -51925,6 +51926,7 @@
     method public void initialize(int, int, int, int);
     method public boolean isFillEnabled();
     method public boolean isInitialized();
+    method public void removeAnimationListener(android.view.animation.Animation.AnimationListener);
     method public void reset();
     method protected float resolveSize(int, float, int, int);
     method public void restrictDuration(long);
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 7bbeb16..8f28ae1 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -2869,7 +2869,6 @@
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I
 Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfDisconnectDcRetrying(Ljava/lang/String;)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V
@@ -2937,7 +2936,6 @@
 Lcom/android/internal/telephony/DctConstants$State;->FAILED:Lcom/android/internal/telephony/DctConstants$State;
 Lcom/android/internal/telephony/DctConstants$State;->IDLE:Lcom/android/internal/telephony/DctConstants$State;
 Lcom/android/internal/telephony/DctConstants$State;->RETRYING:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->SCANNING:Lcom/android/internal/telephony/DctConstants$State;
 Lcom/android/internal/telephony/DctConstants$State;->values()[Lcom/android/internal/telephony/DctConstants$State;
 Lcom/android/internal/telephony/DefaultPhoneNotifier;->mRegistry:Lcom/android/internal/telephony/ITelephonyRegistry;
 Lcom/android/internal/telephony/DriverCall$State;->ACTIVE:Lcom/android/internal/telephony/DriverCall$State;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 48a767b..3561f71 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -924,6 +924,9 @@
 
     private AutofillPopupWindow mAutofillPopupWindow;
 
+    /** @hide */
+    boolean mEnterAnimationComplete;
+
     private static native String getDlWarning();
 
     /** Return the intent that started this activity. */
@@ -2328,6 +2331,7 @@
             }
             notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
         }
+        mEnterAnimationComplete = false;
     }
 
     /**
@@ -7085,6 +7089,8 @@
      * @hide
      */
     public void dispatchEnterAnimationComplete() {
+        mEnterAnimationComplete = true;
+        mInstrumentation.onEnterAnimationComplete();
         onEnterAnimationComplete();
         if (getWindow() != null && getWindow().getDecorView() != null) {
             getWindow().getDecorView().getViewTreeObserver().dispatchOnEnterAnimationComplete();
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 015bc6c..d7c7f3c 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -110,6 +110,7 @@
     private PerformanceCollector mPerformanceCollector;
     private Bundle mPerfMetrics = new Bundle();
     private UiAutomation mUiAutomation;
+    private final Object mAnimationCompleteLock = new Object();
 
     public Instrumentation() {
     }
@@ -397,6 +398,31 @@
         idler.waitForIdle();
     }
 
+    private void waitForEnterAnimationComplete(Activity activity) {
+        synchronized (mAnimationCompleteLock) {
+            long timeout = 5000;
+            try {
+                // We need to check that this specified Activity completed the animation, not just
+                // any Activity. If it was another Activity, then decrease the timeout by how long
+                // it's already waited and wait for the thread to wakeup again.
+                while (timeout > 0 && !activity.mEnterAnimationComplete) {
+                    long startTime = System.currentTimeMillis();
+                    mAnimationCompleteLock.wait(timeout);
+                    long totalTime = System.currentTimeMillis() - startTime;
+                    timeout -= totalTime;
+                }
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    /** @hide */
+    public void onEnterAnimationComplete() {
+        synchronized (mAnimationCompleteLock) {
+            mAnimationCompleteLock.notifyAll();
+        }
+    }
+
     /**
      * Execute a call on the application's main thread, blocking until it is
      * complete.  Useful for doing things that are not thread-safe, such as
@@ -499,6 +525,7 @@
                 }
             } while (mWaitingActivities.contains(aw));
 
+            waitForEnterAnimationComplete(aw.activity);
             return aw.activity;
         }
     }
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 0e1f767..95a346f 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -30,6 +30,9 @@
 
 import dalvik.system.CloseGuard;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Abstraction for an Animation that can be applied to Views, Surfaces, or
  * other objects. See the {@link android.view.animation animation package
@@ -182,10 +185,14 @@
     Interpolator mInterpolator;
 
     /**
-     * The animation listener to be notified when the animation starts, ends or repeats.
+     * An animation listener to be notified when the animation starts, ends or repeats.
      */
-    @UnsupportedAppUsage
-    AnimationListener mListener;
+    private AnimationListener mListener;
+
+    /**
+     * A list of animation listeners to be notified when the animation starts, ends or repeats.
+     */
+    private List<AnimationListener> mListeners;
 
     /**
      * Desired Z order mode during animation.
@@ -371,23 +378,17 @@
         if (mListenerHandler == null) {
             mOnStart = new Runnable() {
                 public void run() {
-                    if (mListener != null) {
-                        mListener.onAnimationStart(Animation.this);
-                    }
+                    dispatchAnimationStart();
                 }
             };
             mOnRepeat = new Runnable() {
                 public void run() {
-                    if (mListener != null) {
-                        mListener.onAnimationRepeat(Animation.this);
-                    }
+                    dispatchAnimationRepeat();
                 }
             };
             mOnEnd = new Runnable() {
                 public void run() {
-                    if (mListener != null) {
-                        mListener.onAnimationEnd(Animation.this);
-                    }
+                    dispatchAnimationEnd();
                 }
             };
         }
@@ -830,6 +831,10 @@
         return true;
     }
 
+    private boolean hasAnimationListener() {
+        return mListener != null || (mListeners != null && !mListeners.isEmpty());
+    }
+
     /**
      * <p>Binds an animation listener to this animation. The animation listener
      * is notified of animation events such as the end of the animation or the
@@ -842,6 +847,32 @@
     }
 
     /**
+     * <p>Adds an animation listener to this animation. The animation listener
+     * is notified of animation events such as the end of the animation or the
+     * repetition of the animation.</p>
+     *
+     * @param listener the animation listener to be notified
+     */
+    public void addAnimationListener(AnimationListener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<>(1);
+        }
+        mListeners.add(listener);
+    }
+
+    /**
+     * <p>Removes an animation listener that has been added with
+     * {@link #addAnimationListener(AnimationListener)}.</p>
+     *
+     * @param listener the animation listener to be removed
+     */
+    public void removeAnimationListener(AnimationListener listener) {
+        if (mListeners != null) {
+            mListeners.remove(listener);
+        }
+    }
+
+    /**
      * Gurantees that this animation has an interpolator. Will use
      * a AccelerateDecelerateInterpolator is nothing else was specified.
      */
@@ -947,26 +978,59 @@
     }
 
     private void fireAnimationStart() {
-        if (mListener != null) {
-            if (mListenerHandler == null) mListener.onAnimationStart(this);
+        if (hasAnimationListener()) {
+            if (mListenerHandler == null) dispatchAnimationStart();
             else mListenerHandler.postAtFrontOfQueue(mOnStart);
         }
     }
 
     private void fireAnimationRepeat() {
-        if (mListener != null) {
-            if (mListenerHandler == null) mListener.onAnimationRepeat(this);
+        if (hasAnimationListener()) {
+            if (mListenerHandler == null) dispatchAnimationRepeat();
             else mListenerHandler.postAtFrontOfQueue(mOnRepeat);
         }
     }
 
     private void fireAnimationEnd() {
-        if (mListener != null) {
-            if (mListenerHandler == null) mListener.onAnimationEnd(this);
+        if (hasAnimationListener()) {
+            if (mListenerHandler == null) dispatchAnimationEnd();
             else mListenerHandler.postAtFrontOfQueue(mOnEnd);
         }
     }
 
+    void dispatchAnimationStart() {
+        if (mListener != null) {
+            mListener.onAnimationStart(this);
+        }
+        if (mListeners != null && !mListeners.isEmpty()) {
+            for (AnimationListener listener : mListeners) {
+                listener.onAnimationStart(this);
+            }
+        }
+    }
+
+    void dispatchAnimationRepeat() {
+        if (mListener != null) {
+            mListener.onAnimationRepeat(this);
+        }
+        if (mListeners != null && !mListeners.isEmpty()) {
+            for (AnimationListener listener : mListeners) {
+                listener.onAnimationRepeat(this);
+            }
+        }
+    }
+
+    void dispatchAnimationEnd() {
+        if (mListener != null) {
+            mListener.onAnimationEnd(this);
+        }
+        if (mListeners != null && !mListeners.isEmpty()) {
+            for (AnimationListener listener : mListeners) {
+                listener.onAnimationEnd(this);
+            }
+        }
+    }
+
     /**
      * Gets the transformation to apply at a specified point in time. Implementations of this
      * method should always replace the specified Transformation or document they are doing
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 767024e..03c6ca6 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -389,16 +389,12 @@
         }
 
         if (started && !mStarted) {
-            if (mListener != null) {
-                mListener.onAnimationStart(this);
-            }
+            dispatchAnimationStart();
             mStarted = true;
         }
 
         if (ended != mEnded) {
-            if (mListener != null) {
-                mListener.onAnimationEnd(this);
-            }
+            dispatchAnimationEnd();
             mEnded = ended;
         }
 
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index d4c451e..78d366c 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -68,10 +68,10 @@
 
     public boolean wakeLockScreenGestureEnabled(int user) {
         return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user)
-                && wakeLockScreenGestureAvailable();
+                && wakeScreenGestureAvailable();
     }
 
-    public boolean wakeLockScreenGestureAvailable() {
+    public boolean wakeScreenGestureAvailable() {
         return mContext.getResources()
                 .getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable);
     }
@@ -81,10 +81,6 @@
                 && wakeScreenGestureAvailable();
     }
 
-    public boolean wakeScreenGestureAvailable() {
-        return !TextUtils.isEmpty(wakeScreenSensorType());
-    }
-
     public String doubleTapSensorType() {
         return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
     }
@@ -93,10 +89,6 @@
         return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
     }
 
-    public String wakeScreenSensorType() {
-        return mContext.getResources().getString(R.string.config_dozeWakeScreenSensorType);
-    }
-
     public boolean pulseOnLongPressEnabled(int user) {
         return pulseOnLongPressAvailable() && boolSettingDefaultOff(
                 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9f5eab5..cb4f416 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2160,9 +2160,6 @@
     <!-- If the sensor that wakes up the lock screen is available or not. -->
     <bool name="config_dozeWakeLockScreenSensorAvailable">false</bool>
 
-    <!-- Type of the wake up sensor. Empty if not supported. -->
-    <string name="config_dozeWakeScreenSensorType" translatable="false"></string>
-
     <!-- Control whether the always on display mode is available. This should only be enabled on
          devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
          states. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e5ff1aa..a4a97d3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3499,7 +3499,6 @@
 
   <java-symbol type="integer" name="db_wal_truncate_size" />
   <java-symbol type="integer" name="config_wakeUpDelayDoze" />
-  <java-symbol type="string" name="config_dozeWakeScreenSensorType" />
 
   <!-- For Bluetooth AbsoluteVolume -->
   <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" />
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 953cef7d..aa29174 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -210,6 +210,10 @@
                         throw new InvalidAlgorithmParameterException(
                             "HMAC key size must be at least 64 bits.");
                     }
+                    if (mKeySizeBits > 512 && spec.isStrongBoxBacked()) {
+                        throw new InvalidAlgorithmParameterException(
+                            "StrongBox HMAC key size must be smaller than 512 bits.");
+                    }
 
                     // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
                     // implies SHA-256 digest). Because keymaster HMAC key is authorized only for
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 5fc742a..d44c894 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -303,7 +303,7 @@
             if (mKeySizeBits == -1) {
                 mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
             }
-            checkValidKeySize(keymasterAlgorithm, mKeySizeBits);
+            checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked());
 
             if (spec.getKeystoreAlias() == null) {
                 throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
@@ -724,10 +724,18 @@
         }
     }
 
-    private static void checkValidKeySize(int keymasterAlgorithm, int keySize)
+    private static void checkValidKeySize(
+            int keymasterAlgorithm,
+            int keySize,
+            boolean isStrongBoxBacked)
             throws InvalidAlgorithmParameterException {
         switch (keymasterAlgorithm) {
             case KeymasterDefs.KM_ALGORITHM_EC:
+                if (isStrongBoxBacked && keySize != 256) {
+                    throw new InvalidAlgorithmParameterException(
+                            "Unsupported StrongBox EC key size: "
+                            + keySize + " bits. Supported: 256");
+                }
                 if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) {
                     throw new InvalidAlgorithmParameterException("Unsupported EC key size: "
                             + keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES);
diff --git a/media/java/android/media/Session2Command.java b/media/java/android/media/Session2Command.java
new file mode 100644
index 0000000..a5e2ae4
--- /dev/null
+++ b/media/java/android/media/Session2Command.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright 2018 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 android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
+ * <p>
+ * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
+ * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+ * {@link #getCustomCommand()} shouldn't be {@code null}.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ * </p>
+ * @hide
+ */
+public final class Session2Command implements Parcelable {
+    /**
+     * The first version of session commands. This version is for commands introduced in API 29.
+     * <p>
+     * This would be used to specify which commands should be added by
+     * {@link Session2CommandGroup.Builder#addAllPredefinedCommands(int)}
+     *
+     * @see Session2CommandGroup.Builder#addAllPredefinedCommands(int)
+     */
+    public static final int COMMAND_VERSION_1 = 1;
+
+    /**
+     * @hide
+     */
+    public static final int COMMAND_VERSION_CURRENT = COMMAND_VERSION_1;
+
+    /**
+     * @hide
+     */
+    @IntDef({COMMAND_VERSION_1})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CommandVersion {}
+
+    /**
+     * @hide
+     */
+    @IntDef({COMMAND_CODE_CUSTOM,
+            COMMAND_CODE_PLAYER_PLAY,
+            COMMAND_CODE_PLAYER_PAUSE,
+            COMMAND_CODE_PLAYER_PREPARE,
+            COMMAND_CODE_PLAYER_SEEK_TO,
+            COMMAND_CODE_PLAYER_SET_SPEED,
+            COMMAND_CODE_PLAYER_GET_PLAYLIST,
+            COMMAND_CODE_PLAYER_SET_PLAYLIST,
+            COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM,
+            COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM,
+            COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM,
+            COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE,
+            COMMAND_CODE_PLAYER_SET_REPEAT_MODE,
+            COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA,
+            COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM,
+            COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM,
+            COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM,
+            COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM,
+            COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA,
+            COMMAND_CODE_PLAYER_SET_MEDIA_ITEM,
+            COMMAND_CODE_VOLUME_SET_VOLUME,
+            COMMAND_CODE_VOLUME_ADJUST_VOLUME,
+            COMMAND_CODE_SESSION_FAST_FORWARD,
+            COMMAND_CODE_SESSION_REWIND,
+            COMMAND_CODE_SESSION_SKIP_FORWARD,
+            COMMAND_CODE_SESSION_SKIP_BACKWARD,
+            COMMAND_CODE_SESSION_SET_RATING,
+            COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT,
+            COMMAND_CODE_LIBRARY_SUBSCRIBE,
+            COMMAND_CODE_LIBRARY_UNSUBSCRIBE,
+            COMMAND_CODE_LIBRARY_GET_CHILDREN,
+            COMMAND_CODE_LIBRARY_GET_ITEM,
+            COMMAND_CODE_LIBRARY_SEARCH,
+            COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CommandCode {}
+
+    /**
+     * Command code for the custom command which can be defined by string action in the
+     * {@link Session2Command}.
+     */
+    public static final int COMMAND_CODE_CUSTOM = 0;
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Player commands (i.e. commands to {@link Session2Player})
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    static final ArrayMap<Integer, Range> VERSION_PLAYER_COMMANDS_MAP = new ArrayMap<>();
+    static final ArrayMap<Integer, Range> VERSION_PLAYER_PLAYLIST_COMMANDS_MAP = new ArrayMap<>();
+
+    // TODO: check the link tag, and reassign int values properly.
+    /**
+     * Command code for {@link MediaController2#play()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
+     * Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_PLAY = 10000;
+
+    /**
+     * Command code for {@link MediaController2#pause()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
+     * Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_PAUSE = 10001;
+
+    /**
+     * Command code for {@link MediaController2#prepare()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
+     * Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_PREPARE = 10002;
+
+    /**
+     * Command code for {@link MediaController2#seekTo(long)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info,
+     * Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_SEEK_TO = 10003;
+
+    /**
+     * Command code for {@link MediaController2#setPlaybackSpeed(float)}}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link Session22Callback#onCommandRequest(MediaSession2, Controller2Info,
+     * Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_SET_SPEED = 10004;
+
+    /**
+     * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata
+     * information to the controller.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST = 10005;
+
+    /**
+     * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_SET_PLAYLIST = 10006;
+
+    /**
+     * Command code for {@link MediaController2#skipToPlaylistItem(int)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM = 10007;
+
+    /**
+     * Command code for {@link MediaController2#skipToPreviousPlaylistItem()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link Session2Callback#onCommandRequest(
+     * MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM = 10008;
+
+    /**
+     * Command code for {@link MediaController2#skipToNextPlaylistItem()}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the {@link Session2Callback#onCommandRequest(
+     * MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+
+    public static final int COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM = 10009;
+
+    /**
+     * Command code for {@link MediaController2#setShuffleMode(int)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE = 10010;
+
+    /**
+     * Command code for {@link MediaController2#setRepeatMode(int)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_SET_REPEAT_MODE = 10011;
+
+    /**
+     * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose metadata
+     * information to the controller.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA = 10012;
+
+    /**
+     * Command code for {@link MediaController2#addPlaylistItem(int, String)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM = 10013;
+
+    /**
+     * Command code for {@link MediaController2#removePlaylistItem(int, String)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM = 10014;
+
+    /**
+     * Command code for {@link MediaController2#replacePlaylistItem(int, String)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM = 10015;
+
+    /**
+     * Command code for {@link MediaController2#getCurrentMediaItem()}. This will expose metadata
+     * information to the controller.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM = 10016;
+
+    /**
+     * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA = 10017;
+
+    /**
+     * Command code for {@link MediaController2#setMediaItem(String)}.
+     * <p>
+     * Command would be sent directly to the player if the session doesn't reject the request
+     * through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_PLAYER_SET_MEDIA_ITEM = 10018;
+
+    static {
+        VERSION_PLAYER_COMMANDS_MAP.put(COMMAND_VERSION_1,
+                new Range(COMMAND_CODE_PLAYER_PLAY, COMMAND_CODE_PLAYER_SET_MEDIA_ITEM));
+    }
+
+    static {
+        VERSION_PLAYER_PLAYLIST_COMMANDS_MAP.put(COMMAND_VERSION_1,
+                new Range(COMMAND_CODE_PLAYER_GET_PLAYLIST,
+                        COMMAND_CODE_PLAYER_SET_MEDIA_ITEM));
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Volume commands (i.e. commands to {@link AudioManager} or {@link RouteMediaPlayer})
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    static final ArrayMap<Integer, Range> VERSION_VOLUME_COMMANDS_MAP = new ArrayMap<>();
+
+    /**
+     * Command code for {@link MediaController2#setVolumeTo(int, int)}.
+     * <p>
+     * <p>
+     * If the session doesn't reject the request through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)},
+     * command would adjust the device volume. It would send to the player directly only if it's
+     * remote player. See RouteMediaPlayer for a remote player.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_VOLUME_SET_VOLUME = 30000;
+
+    /**
+     * Command code for {@link MediaController2#adjustVolume(int, int)}.
+     * <p>
+     * If the session doesn't reject the request through the
+     * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)},
+     * command would adjust the device volume. It would send to the player directly only if it's
+     * remote player. See RouteMediaPlayer for a remote player.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_VOLUME_ADJUST_VOLUME = 30001;
+
+    static {
+        VERSION_VOLUME_COMMANDS_MAP.put(COMMAND_VERSION_1,
+                new Range(COMMAND_CODE_VOLUME_SET_VOLUME,
+                        COMMAND_CODE_VOLUME_ADJUST_VOLUME));
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Session commands (i.e. commands to {@link MediaSession2#Session2Callback})
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    static final ArrayMap<Integer, Range> VERSION_SESSION_COMMANDS_MAP = new ArrayMap<>();
+
+    /**
+     * Command code for {@link MediaController2#fastForward()}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 40000;
+
+    /**
+     * Command code for {@link MediaController2#rewind()}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_SESSION_REWIND = 40001;
+
+    /**
+     * Command code for {@link MediaController2#skipForward()}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_SESSION_SKIP_FORWARD = 40002;
+
+    /**
+     * Command code for {@link MediaController2#skipBackward()}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_SESSION_SKIP_BACKWARD = 40003;
+
+    /**
+     * Command code for {@link MediaController2#setRating(String, Rating)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_SESSION_SET_RATING = 40010;
+
+    public static final Parcelable.Creator<Session2Command> CREATOR =
+            new Parcelable.Creator<Session2Command>() {
+                @Override
+                public Session2Command createFromParcel(Parcel in) {
+                    return new Session2Command(in);
+                }
+
+                @Override
+                public Session2Command[] newArray(int size) {
+                    return new Session2Command[size];
+                }
+            };
+
+    static {
+        VERSION_SESSION_COMMANDS_MAP.put(COMMAND_VERSION_1,
+                new Range(COMMAND_CODE_SESSION_FAST_FORWARD, COMMAND_CODE_SESSION_SET_RATING));
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Session commands (i.e. commands to {@link MediaLibrarySession#MediaLibrarySessionCallback})
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    static final ArrayMap<Integer, Range> VERSION_LIBRARY_COMMANDS_MAP = new ArrayMap<>();
+
+    /**
+     * Command code for {@link MediaBrowser2#getLibraryRoot(Library2Params)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 50000;
+
+    /**
+     * Command code for {@link MediaBrowser2#subscribe(String, Library2Params)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 50001;
+
+    /**
+     * Command code for {@link MediaBrowser2#unsubscribe(String)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 50002;
+
+    /**
+     * Command code for {@link MediaBrowser2#getChildren(String, int, int, Library2Params)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 50003;
+
+    /**
+     * Command code for {@link MediaBrowser2#getItem(String)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 50004;
+
+    /**
+     * Command code for {@link MediaBrowser2#search(String, LibraryParams)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_LIBRARY_SEARCH = 50005;
+
+    /**
+     * Command code for {@link MediaBrowser2#getSearchResult(String, int, int, Library2Params)}.
+     * <p>
+     * Code version is {@link #COMMAND_VERSION_1}.
+     */
+    public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 50006;
+
+    static {
+        VERSION_LIBRARY_COMMANDS_MAP.put(COMMAND_VERSION_1,
+                new Range(COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT,
+                        COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT));
+    }
+
+    @CommandCode private final int mCommandCode;
+    // Nonnull if it's custom command
+    private final String mCustomCommand;
+    private final Bundle mExtras;
+
+    /**
+     * Constructor for creating a predefined command.
+     *
+     * @param commandCode A command code for predefined command.
+     */
+    public Session2Command(@CommandCode int commandCode) {
+        if (commandCode == COMMAND_CODE_CUSTOM) {
+            throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
+        }
+        mCommandCode = commandCode;
+        mCustomCommand = null;
+        mExtras = null;
+    }
+
+    /**
+     * Constructor for creating a custom command.
+     *
+     * @param action The action of this custom command.
+     * @param extras An extra bundle for this custom command.
+     */
+    public Session2Command(@NonNull String action, @Nullable Bundle extras) {
+        if (action == null) {
+            throw new IllegalArgumentException("action shouldn't be null");
+        }
+        mCommandCode = COMMAND_CODE_CUSTOM;
+        mCustomCommand = action;
+        mExtras = extras;
+    }
+
+    /**
+     * Used by parcelable creator.
+     */
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    Session2Command(Parcel in) {
+        mCommandCode = in.readInt();
+        mCustomCommand = in.readString();
+        mExtras = in.readBundle();
+    }
+
+    /**
+     * Gets the command code of a predefined command.
+     * This will return {@link #COMMAND_CODE_CUSTOM} for a custom command.
+     */
+    public @CommandCode int getCommandCode() {
+        return mCommandCode;
+    }
+
+    /**
+     * Gets the action of a custom command.
+     * This will return {@code null} for a predefined command.
+     */
+    public @Nullable String getCustomCommand() {
+        return mCustomCommand;
+    }
+
+    /**
+     * Gets the extra bundle of a custom command.
+     * This will return {@code null} for a predefined command.
+     */
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCommandCode);
+        dest.writeString(mCustomCommand);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Session2Command)) {
+            return false;
+        }
+        Session2Command other = (Session2Command) obj;
+        return mCommandCode == other.mCommandCode
+                && TextUtils.equals(mCustomCommand, other.mCustomCommand);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCustomCommand, mCommandCode);
+    }
+
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    static final class Range {
+        public final int lower;
+        public final int upper;
+
+        Range(int lower, int upper) {
+            this.lower = lower;
+            this.upper = upper;
+        }
+    }
+}
+
diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java
new file mode 100644
index 0000000..de34fe5
--- /dev/null
+++ b/media/java/android/media/Session2CommandGroup.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2018 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 android.media;
+
+import static android.media.Session2Command.COMMAND_CODE_CUSTOM;
+import static android.media.Session2Command.COMMAND_VERSION_1;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.Session2Command.Range;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A set of {@link Session2Command} which represents a command group.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a>
+ * {@link androidx.media2.SessionCommandGroup} for consistent behavior across all devices.
+ * </p>
+ * @hide
+ */
+public final class Session2CommandGroup implements Parcelable {
+    private static final String TAG = "Session2CommandGroup";
+
+    public static final Parcelable.Creator<Session2CommandGroup> CREATOR =
+            new Parcelable.Creator<Session2CommandGroup>() {
+                @Override
+                public Session2CommandGroup createFromParcel(Parcel in) {
+                    return new Session2CommandGroup(in);
+                }
+
+                @Override
+                public Session2CommandGroup[] newArray(int size) {
+                    return new Session2CommandGroup[size];
+                }
+            };
+
+    Set<Session2Command> mCommands = new HashSet<>();
+
+    /**
+     * Default Constructor.
+     */
+    public Session2CommandGroup() { }
+
+    /**
+     * Creates a new Session2CommandGroup with commands copied from another object.
+     *
+     * @param commands The collection of commands to copy.
+     */
+    public Session2CommandGroup(@Nullable Collection<Session2Command> commands) {
+        if (commands != null) {
+            mCommands.addAll(commands);
+        }
+    }
+
+    /**
+     * Used by parcelable creator.
+     */
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    Session2CommandGroup(Parcel in) {
+        Session2Command[] commands = in.readParcelableArray(
+                Session2Command.class.getClassLoader(), Session2Command.class);
+        if (commands != null) {
+            for (Session2Command command : commands) {
+                mCommands.add(command);
+            }
+        }
+    }
+
+    /**
+     * Adds a command to this command group.
+     *
+     * @param command A command to add. Shouldn't be {@code null}.
+     * @hide TODO remove this method
+     */
+    public void addCommand(@NonNull Session2Command command) {
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
+        }
+        if (!hasCommand(command)) {
+            mCommands.add(command);
+        }
+    }
+
+    /**
+     * Adds a predefined command with given {@code commandCode} to this command group.
+     *
+     * @param commandCode A command code to add.
+     *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
+     * @hide TODO remove this method
+     */
+    public void addCommand(@Session2Command.CommandCode int commandCode) {
+        if (commandCode == COMMAND_CODE_CUSTOM) {
+            throw new IllegalArgumentException(
+                    "Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM.");
+        }
+        if (!hasCommand(commandCode)) {
+            mCommands.add(new Session2Command(commandCode));
+        }
+    }
+
+    /**
+     * Checks whether this command group has a command that matches given {@code command}.
+     *
+     * @param command A command to find. Shouldn't be {@code null}.
+     */
+    public boolean hasCommand(@NonNull Session2Command command) {
+        if (command == null) {
+            throw new IllegalArgumentException("command shouldn't be null");
+        }
+        return mCommands.contains(command);
+    }
+
+    /**
+     * Checks whether this command group has a command that matches given {@code commandCode}.
+     *
+     * @param commandCode A command code to find.
+     *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
+     */
+    public boolean hasCommand(@Session2Command.CommandCode int commandCode) {
+        if (commandCode == COMMAND_CODE_CUSTOM) {
+            throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
+        }
+        for (Session2Command command : mCommands) {
+            if (command.getCommandCode() == commandCode) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gets all commands of this command group.
+     */
+    public @NonNull Set<Session2Command> getCommands() {
+        return new HashSet<>(mCommands);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelableArray((Session2Command[]) mCommands.toArray(), 0);
+    }
+
+    /**
+     * Builds a {@link Session2CommandGroup} object.
+     */
+    public static final class Builder {
+        private Set<Session2Command> mCommands;
+
+        public Builder() {
+            mCommands = new HashSet<>();
+        }
+
+        /**
+         * Creates a new builder for {@link Session2CommandGroup} with commands copied from another
+         * {@link Session2CommandGroup} object.
+         * @param commandGroup
+         */
+        public Builder(@NonNull Session2CommandGroup commandGroup) {
+            mCommands = commandGroup.getCommands();
+        }
+
+        /**
+         * Adds a command to this command group.
+         *
+         * @param command A command to add. Shouldn't be {@code null}.
+         */
+        public @NonNull Builder addCommand(@NonNull Session2Command command) {
+            if (command == null) {
+                throw new IllegalArgumentException("command shouldn't be null");
+            }
+            mCommands.add(command);
+            return this;
+        }
+
+        /**
+         * Adds a predefined command with given {@code commandCode} to this command group.
+         *
+         * @param commandCode A command code to add.
+         *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
+         */
+        public @NonNull Builder addCommand(@Session2Command.CommandCode int commandCode) {
+            if (commandCode == COMMAND_CODE_CUSTOM) {
+                throw new IllegalArgumentException(
+                        "Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM.");
+            }
+            mCommands.add(new Session2Command(commandCode));
+            return this;
+        }
+
+        /**
+         * Adds all predefined session commands except for the commands added after the specified
+         * version without default implementation. This provides convenient way to add commands
+         * with implementation.
+         *
+         * @param version command version
+         * @see Session2Command#COMMAND_VERSION_1
+         * @see
+         * MediaSession2.Session2Callback#onConnect
+         */
+        public @NonNull Builder addAllPredefinedCommands(
+                @Session2Command.CommandVersion int version) {
+            if (version != COMMAND_VERSION_1) {
+                throw new IllegalArgumentException("Unknown command version " + version);
+            }
+            addAllPlayerCommands(version);
+            addAllVolumeCommands(version);
+            addAllSessionCommands(version);
+            addAllLibraryCommands(version);
+            return this;
+        }
+
+        /**
+         * Removes a command from this group which matches given {@code command}.
+         *
+         * @param command A command to find. Shouldn't be {@code null}.
+         */
+        public @NonNull Builder removeCommand(@NonNull Session2Command command) {
+            if (command == null) {
+                throw new IllegalArgumentException("command shouldn't be null");
+            }
+            mCommands.remove(command);
+            return this;
+        }
+
+        /**
+         * Removes a command from this group which matches given {@code commandCode}.
+         *
+         * @param commandCode A command code to find.
+         *                    Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}.
+         */
+        public @NonNull Builder removeCommand(@Session2Command.CommandCode int commandCode) {
+            if (commandCode == COMMAND_CODE_CUSTOM) {
+                throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM");
+            }
+            mCommands.remove(new Session2Command(commandCode));
+            return this;
+        }
+
+        @NonNull Builder addAllPlayerCommands(@Session2Command.CommandVersion int version) {
+            addCommands(version, Session2Command.VERSION_PLAYER_COMMANDS_MAP);
+            return this;
+        }
+
+        @NonNull Builder addAllPlayerCommands(@Session2Command.CommandVersion int version,
+                boolean includePlaylistCommands) {
+            if (includePlaylistCommands) {
+                return addAllPlayerCommands(version);
+            }
+            for (int i = COMMAND_VERSION_1; i <= version; i++) {
+                Range include = Session2Command.VERSION_PLAYER_COMMANDS_MAP.get(i);
+                Range exclude = Session2Command.VERSION_PLAYER_PLAYLIST_COMMANDS_MAP.get(i);
+                for (int code = include.lower; code <= include.upper; code++) {
+                    if (code < exclude.lower && code > exclude.upper) {
+                        addCommand(code);
+                    }
+                }
+            }
+            return this;
+        }
+
+        @NonNull Builder addAllVolumeCommands(@Session2Command.CommandVersion int version) {
+            addCommands(version, Session2Command.VERSION_VOLUME_COMMANDS_MAP);
+            return this;
+        }
+
+        @NonNull Builder addAllSessionCommands(@Session2Command.CommandVersion int version) {
+            addCommands(version, Session2Command.VERSION_SESSION_COMMANDS_MAP);
+            return this;
+        }
+
+        @NonNull Builder addAllLibraryCommands(@Session2Command.CommandVersion int version) {
+            addCommands(version, Session2Command.VERSION_LIBRARY_COMMANDS_MAP);
+            return this;
+        }
+
+        private void addCommands(
+                @Session2Command.CommandVersion int version, ArrayMap<Integer, Range> map) {
+            for (int i = COMMAND_VERSION_1; i <= version; i++) {
+                Range range = map.get(i);
+                for (int code = range.lower; code <= range.upper; code++) {
+                    addCommand(code);
+                }
+            }
+        }
+
+        /**
+         * Builds {@link Session2CommandGroup}.
+         *
+         * @return a new {@link Session2CommandGroup}.
+         */
+        public @NonNull Session2CommandGroup build() {
+            return new Session2CommandGroup(mCommands);
+        }
+    }
+}
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 1dca10a..2fe740d 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -76,7 +76,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:background="@null"
-                systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivity;launchFlags=0x14008000;end"
+                systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivity;launchFlags=0x24000000;end"
             />
             <com.android.systemui.statusbar.policy.Clock
                 android:id="@+id/clock"
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index c039fcc..959f9b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -16,7 +16,6 @@
 package com.android.settingslib.media;
 
 import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.util.Log;
 
@@ -36,12 +35,6 @@
         super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
         mCachedDevice = device;
         initDeviceRecord();
-        buildConnectedState(device);
-    }
-
-    private void buildConnectedState(CachedBluetoothDevice device) {
-        mIsConnected = device.isActiveDevice(BluetoothProfile.A2DP)
-                || device.isActiveDevice(BluetoothProfile.HEARING_AID);
     }
 
     @Override
@@ -61,11 +54,6 @@
     }
 
     @Override
-    public void notifyConnectedChanged() {
-        buildConnectedState(mCachedDevice);
-    }
-
-    @Override
     public void connect() {
         //TODO(b/117129183): add callback to notify LocalMediaManager connection state.
         mIsConnected = mCachedDevice.setActive();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
index a9fc434..ab1cca0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
@@ -18,7 +18,6 @@
 import android.app.Notification;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.util.Log;
 
@@ -31,7 +30,6 @@
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 
 /**
@@ -41,8 +39,8 @@
 
     private static final String TAG = "BluetoothMediaManager";
 
-    private final DeviceProfileNotReadyObserverCallback mObserverCallback =
-            new DeviceProfileNotReadyObserverCallback();
+    private final DeviceAttributeChangeCallback mCachedDeviceCallback =
+            new DeviceAttributeChangeCallback();
 
     private LocalBluetoothManager mLocalBluetoothManager;
     private LocalBluetoothProfileManager mProfileManager;
@@ -50,10 +48,6 @@
     private MediaDevice mLastAddedDevice;
     private MediaDevice mLastRemovedDevice;
 
-    private boolean mIsA2dpProfileReady = false;
-    private boolean mIsHearingAidProfileReady = false;
-    private Collection<CachedBluetoothDevice> mCachedDevices;
-
     BluetoothMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
             Notification notification) {
         super(context, notification);
@@ -68,18 +62,6 @@
         mLocalBluetoothManager.getEventManager().registerCallback(this);
         buildBluetoothDeviceList();
         dispatchDeviceListAdded();
-
-        // The profile may not ready when calling startScan().
-        // Device status are all disconnected since profiles are not ready to connected.
-        // In this case, we observe all devices in CachedDeviceManager.
-        // When one of these device is connected to profile, will call buildBluetoothDeviceList()
-        // again to find the connected devices.
-        if (!mIsA2dpProfileReady && !mIsHearingAidProfileReady) {
-            mCachedDevices = mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy();
-            for (CachedBluetoothDevice device : mCachedDevices) {
-                device.registerCallback(mObserverCallback);
-            }
-        }
     }
 
     private void buildBluetoothDeviceList() {
@@ -114,8 +96,6 @@
                 addMediaDevice(cachedDevice);
             }
         }
-
-        mIsA2dpProfileReady = a2dpProfile.isProfileReady();
     }
 
     private void addConnectedHearingAidDevices() {
@@ -150,14 +130,13 @@
                 addMediaDevice(cachedDevice);
             }
         }
-
-        mIsHearingAidProfileReady = hapProfile.isProfileReady();
     }
 
     private void addMediaDevice(CachedBluetoothDevice cachedDevice) {
         MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
         if (mediaDevice == null) {
             mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice);
+            cachedDevice.registerCallback(mCachedDeviceCallback);
             mLastAddedDevice = mediaDevice;
             mMediaDevices.add(mediaDevice);
         }
@@ -166,6 +145,16 @@
     @Override
     public void stopScan() {
         mLocalBluetoothManager.getEventManager().unregisterCallback(this);
+        unregisterCachedDeviceCallback();
+    }
+
+    private void unregisterCachedDeviceCallback() {
+        for (MediaDevice device : mMediaDevices) {
+            if (device instanceof BluetoothMediaDevice) {
+                ((BluetoothMediaDevice) device).getCachedDevice()
+                        .unregisterCallback(mCachedDeviceCallback);
+            }
+        }
     }
 
     @Override
@@ -177,6 +166,8 @@
             final List<MediaDevice> removeDevicesList = new ArrayList<>();
             for (MediaDevice device : mMediaDevices) {
                 if (device instanceof BluetoothMediaDevice) {
+                    ((BluetoothMediaDevice) device).getCachedDevice()
+                            .unregisterCallback(mCachedDeviceCallback);
                     removeDevicesList.add(device);
                 }
             }
@@ -221,6 +212,7 @@
     private void removeMediaDevice(CachedBluetoothDevice cachedDevice) {
         final MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
         if (mediaDevice != null) {
+            cachedDevice.unregisterCallback(mCachedDeviceCallback);
             mLastRemovedDevice = mediaDevice;
             mMediaDevices.remove(mediaDevice);
         }
@@ -260,30 +252,10 @@
             dispatchDeviceRemoved(cachedDevice);
         }
     }
-
-    @Override
-    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
-        Log.d(TAG, "onActiveDeviceChanged : device : "
-                + activeDevice + ", profile : " + bluetoothProfile);
-        if (BluetoothProfile.HEARING_AID == bluetoothProfile
-                || BluetoothProfile.A2DP == bluetoothProfile) {
-            final String id = activeDevice == null
-                    ? PhoneMediaDevice.ID : MediaDeviceUtils.getId(activeDevice);
-            dispatchActiveDeviceChanged(id);
-        }
-    }
-
-    class DeviceProfileNotReadyObserverCallback implements CachedBluetoothDevice.Callback {
-
+    class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback {
         @Override
         public void onDeviceAttributesChanged() {
-            if (!mIsA2dpProfileReady && !mIsHearingAidProfileReady) {
-                for (CachedBluetoothDevice device : mCachedDevices) {
-                    device.unregisterCallback(mObserverCallback);
-                }
-                buildBluetoothDeviceList();
-                dispatchDeviceListAdded();
-            }
+            dispatchDeviceAttributesChanged();
         }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index a2b161f..21a81e0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,7 +16,6 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
-import android.widget.Toast;
 
 import androidx.mediarouter.media.MediaRouter;
 
@@ -54,22 +53,15 @@
     }
 
     @Override
-    public void notifyConnectedChanged() {
-        //TODO(b/117129183): check mIsConnected state
-    }
-
-    @Override
     public void connect() {
         //TODO(b/117129183): use MediaController2 to transfer media
         mIsConnected = true;
         super.connect();
-        //mIsConnected = true;
-        Toast.makeText(mContext, "This is cast device !", Toast.LENGTH_SHORT).show();
     }
 
     @Override
     public void disconnect() {
         //TODO(b/117129183): disconnected last select device
-        //mIsConnected = false;
+        mIsConnected = false;
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 154834e..c9479d4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -39,9 +39,6 @@
     private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
     private static final String TAG = "LocalMediaManager";
 
-    public static final String NOTIFICATION_EXTRA = "notification_extra";
-    public static final String NOTIFICATION_PACKAGE_NAME = "notification_package_name";
-
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({MediaDeviceState.STATE_CONNECTED,
             MediaDeviceState.STATE_CONNECTING,
@@ -61,6 +58,7 @@
     private InfoMediaManager mInfoMediaManager;
 
     private LocalBluetoothManager mLocalBluetoothManager;
+    private MediaDevice mLastConnectedDevice;
     private MediaDevice mPhoneDevice;
 
     /**
@@ -100,43 +98,29 @@
      * @param connectDevice the MediaDevice
      */
     public void connectDevice(MediaDevice connectDevice) {
-        final MediaDevice currentDevice = getCurrentConnectedDevice();
-        final MediaDevice device =
-                MediaDeviceUtils.findMediaDevice(mMediaDevices, connectDevice.getId());
-        if (device != null && currentDevice != null
-                && device.getId().equals(currentDevice.getId())) {
+        if (connectDevice == mLastConnectedDevice) {
             return;
         }
 
-        //TODO(b/117129183): For demo, will remove check connectDevice is InfoMediaDevice.
-        if (currentDevice != null && !(connectDevice instanceof InfoMediaDevice)) {
-            currentDevice.disconnect();
+        if (mLastConnectedDevice != null) {
+            mLastConnectedDevice.disconnect();
         }
 
-        device.connect();
+        connectDevice.connect();
+        if (connectDevice.isConnected()) {
+            mLastConnectedDevice = connectDevice;
+        }
 
-        final int state = device.isConnected()
+        final int state = connectDevice.isConnected()
                 ? MediaDeviceState.STATE_CONNECTED
                 : MediaDeviceState.STATE_DISCONNECTED;
-        dispatchSelectedDeviceStateChanged(mMediaDevices, device, state);
+        dispatchSelectedDeviceStateChanged(connectDevice, state);
     }
 
-    private MediaDevice getCurrentConnectedDevice() {
-        for (MediaDevice device : mMediaDevices) {
-            if (device.isConnected()) {
-                return device;
-            }
-        }
-        Log.w(TAG, "getCurrentConnectedDevice() cannot find current connected device !");
-        return null;
-    }
-
-    void dispatchSelectedDeviceStateChanged(List<MediaDevice> mMediaDevices, MediaDevice device,
-            @MediaDeviceState int state) {
+    void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) {
         synchronized (mCallbacks) {
             for (DeviceCallback callback : mCallbacks) {
-                callback.onSelectedDeviceStateChanged(new ArrayList<>(mMediaDevices), device,
-                        state);
+                callback.onSelectedDeviceStateChanged(device, state);
             }
         }
     }
@@ -188,6 +172,23 @@
         mInfoMediaManager.stopScan();
     }
 
+    /**
+     * Find the MediaDevice through id.
+     *
+     * @param devices the list of MediaDevice
+     * @param id the unique id of MediaDevice
+     * @return MediaDevice
+     */
+    public MediaDevice getMediaDeviceById(List<MediaDevice> devices, String id) {
+        for (MediaDevice mediaDevice : devices) {
+            if (mediaDevice.getId().equals(id)) {
+                return mediaDevice;
+            }
+        }
+        Log.i(TAG, "getMediaDeviceById() can't found device");
+        return null;
+    }
+
     class MediaDeviceCallback implements MediaManager.MediaDeviceCallback {
         @Override
         public void onDeviceAdded(MediaDevice device) {
@@ -225,25 +226,6 @@
         public void onDeviceAttributesChanged() {
             dispatchDeviceListUpdate();
         }
-
-        @Override
-        public void onActiveDeviceChanged(String id) {
-            final MediaDevice currentDevice = getCurrentConnectedDevice();
-            final MediaDevice connectDevice = MediaDeviceUtils.findMediaDevice(mMediaDevices, id);
-
-            if (connectDevice != null && currentDevice != null
-                    && connectDevice.getId().equals(currentDevice.getId())) {
-                return;
-            }
-            if (currentDevice != null) {
-                currentDevice.notifyConnectedChanged();
-            }
-            if (connectDevice != null) {
-                connectDevice.notifyConnectedChanged();
-            }
-
-            dispatchDeviceListUpdate();
-        }
     }
 
 
@@ -267,7 +249,6 @@
          * {@link MediaDeviceState#STATE_CONNECTING},
          * {@link MediaDeviceState#STATE_DISCONNECTED}
          */
-        void onSelectedDeviceStateChanged(List<MediaDevice> devices, MediaDevice device,
-                @MediaDeviceState int state);
+        void onSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state);
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index e4aeda6..33b621c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -17,7 +17,6 @@
 
 import android.content.Context;
 import android.text.TextUtils;
-import android.util.Log;
 
 import androidx.annotation.IntDef;
 
@@ -55,7 +54,6 @@
         ConnectionRecordManager.getInstance().fetchLastSelectedDevice(mContext);
         mConnectedRecord = ConnectionRecordManager.getInstance().fetchConnectionRecord(mContext,
                 getId());
-        Log.d("ttttt", getName() + " used: " + mConnectedRecord);
     }
 
     /**
@@ -88,11 +86,6 @@
     public abstract String getId();
 
     /**
-     * Notify MediaDevice to change their connected state.
-     */
-    public abstract void notifyConnectedChanged();
-
-    /**
      * Transfer MediaDevice for media
      */
     public void connect() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
index 8066eb0..bdddaf3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
@@ -15,21 +15,14 @@
  */
 package com.android.settingslib.media;
 
-import android.util.Log;
-
 import androidx.mediarouter.media.MediaRouter;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
-import java.util.List;
-
 /**
  * MediaDeviceUtils provides utility function for MediaDevice
  */
 public class MediaDeviceUtils {
-
-    private static final String TAG = "MediaDeviceUtils";
-
     /**
      * Use CachedBluetoothDevice address to represent unique id
      *
@@ -49,21 +42,4 @@
     public static String getId(MediaRouter.RouteInfo route) {
         return route.getId();
     }
-
-    /**
-     * Find the MediaDevice through id.
-     *
-     * @param devices the list of MediaDevice
-     * @param id the unique id of MediaDevice
-     * @return MediaDevice
-     */
-    public static MediaDevice findMediaDevice(List<MediaDevice> devices, String id) {
-        for (MediaDevice mediaDevice : devices) {
-            if (mediaDevice.getId().equals(id)) {
-                return mediaDevice;
-            }
-        }
-        Log.e(TAG, "findMediaDevice() can't found device");
-        return null;
-    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
index ee11070..72b6b09 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
@@ -17,6 +17,7 @@
 
 import android.app.Notification;
 import android.content.Context;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -67,7 +68,13 @@
     public abstract void stopScan();
 
     protected MediaDevice findMediaDevice(String id) {
-        return MediaDeviceUtils.findMediaDevice(mMediaDevices, id);
+        for (MediaDevice mediaDevice : mMediaDevices) {
+            if (mediaDevice.getId().equals(id)) {
+                return mediaDevice;
+            }
+        }
+        Log.e(TAG, "findMediaDevice() can't found device");
+        return null;
     }
 
     protected void dispatchDeviceAdded(MediaDevice mediaDevice) {
@@ -89,7 +96,7 @@
     protected void dispatchDeviceListAdded() {
         synchronized (mCallbacks) {
             for (MediaDeviceCallback callback : mCallbacks) {
-                callback.onDeviceListAdded(new ArrayList<>(mMediaDevices));
+                callback.onDeviceListAdded(mMediaDevices);
             }
         }
     }
@@ -110,14 +117,6 @@
         }
     }
 
-    protected void dispatchActiveDeviceChanged(String id) {
-        synchronized (mCallbacks) {
-            for (MediaDeviceCallback callback : mCallbacks) {
-                callback.onActiveDeviceChanged(id);
-            }
-        }
-    }
-
     /**
      * Callback for notifying device is added, removed and attributes changed.
      */
@@ -154,12 +153,5 @@
          * Callback for notifying MediaDevice attributes is changed.
          */
         void onDeviceAttributesChanged();
-
-        /**
-         * Callback for notifying active MediaDevice is changed.
-         *
-         * @param id the id of MediaDevice
-         */
-        void onActiveDeviceChanged(String id);
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 59150f1..e0f3c2f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -15,7 +15,6 @@
  */
 package com.android.settingslib.media;
 
-import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.util.Log;
 
@@ -25,8 +24,6 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
-import java.util.List;
-
 /**
  * PhoneMediaDevice extends MediaDevice to represents Phone device.
  */
@@ -45,29 +42,6 @@
         mLocalBluetoothManager = localBluetoothManager;
         mProfileManager = mLocalBluetoothManager.getProfileManager();
         initDeviceRecord();
-
-        mIsConnected = isPhoneActive();
-    }
-
-    private boolean isPhoneActive() {
-        boolean isActive = true;
-
-        final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
-        final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
-
-        if (a2dpProfile.getActiveDevice() == null) {
-            final List<BluetoothDevice> activeDevices = hapProfile.getActiveDevices();
-            for (BluetoothDevice btDevice : activeDevices) {
-                if (btDevice != null) {
-                    isActive = false;
-                    break;
-                }
-            }
-        } else {
-            isActive = false;
-        }
-
-        return isActive;
     }
 
     @Override
@@ -88,11 +62,6 @@
     }
 
     @Override
-    public void notifyConnectedChanged() {
-        mIsConnected = isPhoneActive();
-    }
-
-    @Override
     public void connect() {
         final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
         final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 089f773..871e248 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -101,6 +101,9 @@
     }
 
     public void handleBroadcast(Intent intent) {
+        if (mWifiManager == null) {
+            return;
+        }
         String action = intent.getAction();
         if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
             updateWifiState();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 2f082b9..e47ca32 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -212,7 +212,7 @@
         mConnectivityManager = connectivityManager;
 
         // check if verbose logging developer option has been turned on or off
-        sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
+        sVerboseLogging = mWifiManager != null && (mWifiManager.getVerboseLoggingLevel() > 0);
 
         mFilter = filter;
 
@@ -283,7 +283,7 @@
             mScanner = new Scanner();
         }
 
-        if (mWifiManager.isWifiEnabled()) {
+        if (isWifiEnabled()) {
             mScanner.resume();
         }
     }
@@ -413,7 +413,7 @@
     }
 
     public boolean isWifiEnabled() {
-        return mWifiManager.isWifiEnabled();
+        return mWifiManager != null && mWifiManager.isWifiEnabled();
     }
 
     /**
@@ -638,7 +638,7 @@
     private void updateNetworkInfo(NetworkInfo networkInfo) {
 
         /* Sticky broadcasts can call this when wifi is disabled */
-        if (!mWifiManager.isWifiEnabled()) {
+        if (!isWifiEnabled()) {
             clearAccessPointsAndConditionallyUpdate();
             return;
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 1cbf277..9bbdd01 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -25,10 +25,8 @@
 
 import androidx.mediarouter.media.MediaRouter;
 
-import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,8 +59,6 @@
             new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO);
 
     @Mock
-    private A2dpProfile mA2dpProfile;
-    @Mock
     private BluetoothDevice mDevice1;
     @Mock
     private BluetoothDevice mDevice2;
@@ -77,8 +73,6 @@
     @Mock
     private LocalBluetoothManager mLocalBluetoothManager;
     @Mock
-    private LocalBluetoothProfileManager mProfileManager;
-    @Mock
     private MediaRouter.RouteInfo mRouteInfo1;
     @Mock
     private MediaRouter.RouteInfo mRouteInfo2;
@@ -115,9 +109,6 @@
         when(mRouteInfo1.getName()).thenReturn(DEVICE_NAME_1);
         when(mRouteInfo2.getName()).thenReturn(DEVICE_NAME_2);
         when(mRouteInfo3.getName()).thenReturn(DEVICE_NAME_3);
-        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
-        when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
-        when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice1);
 
         mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1);
         mBluetoothMediaDevice2 = new BluetoothMediaDevice(mContext, mCachedDevice2);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index ab77e55..50003e3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
     private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
     static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
-    private static final int REASONS = 8;
+    private static final int REASONS = 9;
 
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -46,6 +46,7 @@
     public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
     public static final int PULSE_REASON_DOCKING = 6;
     public static final int REASON_SENSOR_WAKE_UP = 7;
+    public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
 
     private static boolean sRegisterKeyguardCallback = true;
 
@@ -178,15 +179,6 @@
     }
 
     /**
-     * Appends lock screen wake up event to the logs.
-     * @param wake if we're waking up or sleeping.
-     */
-    public static void traceLockScreenWakeUp(boolean wake) {
-        if (!ENABLED) return;
-        log("wakeLockScreen " + wake);
-    }
-
-    /**
      * Appends wake-display event to the logs.
      * @param wake if we're waking up or sleeping.
      */
@@ -213,6 +205,7 @@
             case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
             case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
             case PULSE_REASON_DOCKING: return "docking";
+            case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
             case REASON_SENSOR_WAKE_UP: return "wakeup";
             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index c2676d0..35b64ed 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -17,6 +17,7 @@
 package com.android.systemui.doze;
 
 import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_DISPLAY;
+import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
 
 import android.annotation.AnyThread;
 import android.app.ActivityManager;
@@ -115,10 +116,17 @@
                 new PluginSensor(
                         new SensorManagerPlugin.Sensor(TYPE_WAKE_DISPLAY),
                         Settings.Secure.DOZE_WAKE_SCREEN_GESTURE,
-                        true /* configured */,
+                        mConfig.wakeScreenGestureAvailable(),
                         DozeLog.REASON_SENSOR_WAKE_UP,
                         false /* reports touch coordinates */,
                         false /* touchscreen */),
+                new PluginSensor(
+                        new SensorManagerPlugin.Sensor(TYPE_WAKE_LOCK_SCREEN),
+                        Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
+                        mConfig.wakeScreenGestureAvailable(),
+                        DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
+                        false /* reports touch coordinates */,
+                        false /* touchscreen */),
         };
 
         mProxSensor = new ProxSensor(policy);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1e228f2..6a9b689 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -135,11 +135,12 @@
         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
         boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
         boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
+        boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
         boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
 
         if (isWakeDisplay) {
             onWakeScreen(wakeEvent);
-        } else if (isLongPress) {
+        } else if (isLongPress || isWakeLockScreen) {
             requestPulse(pulseReason, sensorPerformedProxCheck);
         } else {
             proximityCheckThenCall((result) -> {
@@ -156,7 +157,6 @@
                     mDozeHost.extendPulse();
                 }
             }, sensorPerformedProxCheck, pulseReason);
-            return;
         }
 
         if (isPickup) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java b/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java
deleted file mode 100644
index 70143e2..0000000
--- a/packages/SystemUI/src/com/android/systemui/doze/LockScreenWakeUpController.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2018 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.doze;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.util.AsyncSensorManager;
-
-/**
- * Controller responsible for waking up or making the device sleep based on ambient sensors.
- */
-public class LockScreenWakeUpController implements StatusBarStateController.StateListener,
-        SensorManagerPlugin.SensorEventListener {
-
-    private static final String TAG = LockScreenWakeUpController.class.getSimpleName();
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final AsyncSensorManager mAsyncSensorManager;
-    private final SensorManagerPlugin.Sensor mSensor;
-    private final AmbientDisplayConfiguration mAmbientConfiguration;
-    private final PowerManager mPowerManager;
-    private final DozeHost mDozeHost;
-    private final Handler mHandler;
-    private boolean mRegistered;
-    private boolean mDozing;
-
-    public LockScreenWakeUpController(Context context, DozeHost dozeHost) {
-        this(Dependency.get(AsyncSensorManager.class),
-                new SensorManagerPlugin.Sensor(SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN),
-                new AmbientDisplayConfiguration(context),
-                context.getSystemService(PowerManager.class),
-                dozeHost, Dependency.get(StatusBarStateController.class), new Handler());
-    }
-
-    @VisibleForTesting
-    public LockScreenWakeUpController(AsyncSensorManager asyncSensorManager,
-            SensorManagerPlugin.Sensor sensor, AmbientDisplayConfiguration ambientConfiguration,
-            PowerManager powerManager, DozeHost dozeHost,
-            StatusBarStateController statusBarStateController, Handler handler) {
-        mAsyncSensorManager = asyncSensorManager;
-        mSensor = sensor;
-        mAmbientConfiguration = ambientConfiguration;
-        mPowerManager = powerManager;
-        mDozeHost = dozeHost;
-        mHandler = handler;
-        statusBarStateController.addCallback(this);
-    }
-
-    @Override
-    public void onStateChanged(int newState) {
-        boolean isLockScreen = newState == StatusBarState.KEYGUARD
-                || newState == StatusBarState.SHADE_LOCKED;
-
-        if (!mAmbientConfiguration.wakeLockScreenGestureEnabled(UserHandle.USER_CURRENT)) {
-            if (mRegistered) {
-                mAsyncSensorManager.unregisterPluginListener(mSensor, this);
-                mRegistered = false;
-            }
-            return;
-        }
-
-        if (isLockScreen && !mRegistered) {
-            mAsyncSensorManager.registerPluginListener(mSensor, this);
-            mRegistered = true;
-        } else if (!isLockScreen && mRegistered) {
-            mAsyncSensorManager.unregisterPluginListener(mSensor, this);
-            mRegistered = false;
-        }
-    }
-
-    @Override
-    public void onDozingChanged(boolean isDozing) {
-        mDozing = isDozing;
-    }
-
-    @Override
-    public void onSensorChanged(SensorManagerPlugin.SensorEvent event) {
-        mHandler.post(()-> {
-            float[] rawValues = event.getValues();
-            boolean wakeEvent = rawValues != null && rawValues.length > 0 && rawValues[0] != 0;
-
-            DozeLog.traceLockScreenWakeUp(wakeEvent);
-            if (wakeEvent && mDozing) {
-                if (DEBUG) Log.d(TAG, "Wake up.");
-                mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
-            } else if (!wakeEvent && !mDozing) {
-                if (DEBUG) Log.d(TAG, "Nap time.");
-                mPowerManager.goToSleep(SystemClock.uptimeMillis(),
-                        PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
-            } else if (DEBUG) {
-                Log.d(TAG, "Skip sensor event. Wake? " + wakeEvent + " dozing: " + mDozing);
-            }
-        });
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 92a9efe..6f2b63d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -158,8 +158,11 @@
      * @return duration in millis.
      */
     public long getWallpaperAodDuration() {
-        return shouldControlScreenOff() ? DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY
-                : mAlwaysOnPolicy.wallpaperVisibilityDuration;
+        if (mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(UserHandle.USER_CURRENT)
+                || shouldControlScreenOff()) {
+            return DozeScreenState.ENTER_DOZE_HIDE_WALLPAPER_DELAY;
+        }
+        return mAlwaysOnPolicy.wallpaperVisibilityDuration;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 21c506b..2e9d9bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -19,7 +19,6 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
@@ -54,7 +53,6 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.MathUtils;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
@@ -172,8 +170,6 @@
     private boolean mDozing;
     private int mIndicationBottomMargin;
     private float mDarkAmount;
-    private int mBurnInXOffset;
-    private int mBurnInYOffset;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -250,8 +246,6 @@
         mIndicationText = findViewById(R.id.keyguard_indication_text);
         mIndicationBottomMargin = getResources().getDimensionPixelSize(
                 R.dimen.keyguard_indication_margin_bottom);
-        mBurnInYOffset = getResources().getDimensionPixelSize(
-                R.dimen.default_burn_in_prevention_offset);
         updateCameraVisibility();
         mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
         mUnlockMethodCache.addListener(this);
@@ -322,8 +316,6 @@
         super.onConfigurationChanged(newConfig);
         mIndicationBottomMargin = getResources().getDimensionPixelSize(
                 R.dimen.keyguard_indication_margin_bottom);
-        mBurnInYOffset = getResources().getDimensionPixelSize(
-                R.dimen.default_burn_in_prevention_offset);
         MarginLayoutParams mlp = (MarginLayoutParams) mIndicationArea.getLayoutParams();
         if (mlp.bottomMargin != mIndicationBottomMargin) {
             mlp.bottomMargin = mIndicationBottomMargin;
@@ -569,7 +561,15 @@
         mDarkAmount = darkAmount;
         mIndicationController.setDarkAmount(darkAmount);
         mLockIcon.setDarkAmount(darkAmount);
-        dozeTimeTick();
+    }
+
+    /**
+     * When keyguard is in pulsing (AOD2) state.
+     * @param pulsing {@code true} when pulsing.
+     * @param animated if transition should be animated.
+     */
+    public void setPulsing(boolean pulsing, boolean animated) {
+        mLockIcon.setPulsing(pulsing, animated);
     }
 
     private static boolean isSuccessfulLaunch(int result) {
@@ -830,6 +830,7 @@
 
         updateCameraVisibility();
         updateLeftAffordanceIcon();
+        mLockIcon.setDozing(dozing);
 
         if (dozing) {
             mOverlayContainer.setVisibility(INVISIBLE);
@@ -841,21 +842,6 @@
         }
     }
 
-    public void dozeTimeTick() {
-        // Move views every minute to avoid burn-in
-        int burnInYOffset = -getBurnInOffset(mBurnInYOffset, false /* xAxis */);
-        burnInYOffset = (int) MathUtils.lerp(0, burnInYOffset, mDarkAmount);
-        mLockIcon.setTranslationY(burnInYOffset);
-    }
-
-    public void setBurnInXOffset(int burnInXOffset) {
-        if (mBurnInXOffset == burnInXOffset) {
-            return;
-        }
-        mBurnInXOffset = burnInXOffset;
-        mLockIcon.setTranslationX(burnInXOffset);
-    }
-
     private class DefaultLeftButton implements IntentButton {
 
         private IconState mIconState = new IconState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 4a194e0..d934d95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -58,6 +58,8 @@
     private boolean mHasFingerPrintIcon;
     private boolean mHasFaceUnlockIcon;
     private int mDensity;
+    private boolean mPulsing;
+    private boolean mDozing;
 
     private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */);
     private float mDarkAmount;
@@ -157,6 +159,7 @@
             mLastScreenOn = mScreenOn;
         }
 
+        setVisibility(mDozing && !mPulsing ? GONE : VISIBLE);
         updateClickability();
     }
 
@@ -269,6 +272,24 @@
         updateDarkTint();
     }
 
+    /**
+     * When keyguard is in pulsing (AOD2) state.
+     * @param pulsing {@code true} when pulsing.
+     * @param animated if transition should be animated.
+     */
+    public void setPulsing(boolean pulsing, boolean animated) {
+        mPulsing = pulsing;
+        update();
+    }
+
+    /**
+     * Sets the dozing state of the keyguard.
+     */
+    public void setDozing(boolean dozing) {
+        mDozing = dozing;
+        update();
+    }
+
     private void updateDarkTint() {
         Drawable drawable = getDrawable().mutate();
         int color = ColorUtils.blendARGB(Color.TRANSPARENT, Color.WHITE, mDarkAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 242573d..1b18c6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -579,7 +579,6 @@
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
         mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
-        mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
 
         mStackScrollerMeasuringPass++;
         requestScrollerTopPaddingUpdate(animate);
@@ -2806,6 +2805,7 @@
         }
         mNotificationStackScroller.setPulsing(pulsing, animatePulse);
         mKeyguardStatusView.setPulsing(pulsing, animatePulse);
+        mKeyguardBottomArea.setPulsing(pulsing, animatePulse);
     }
 
     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
@@ -2818,7 +2818,6 @@
     public void dozeTimeTick() {
         mKeyguardStatusBar.dozeTimeTick();
         mKeyguardStatusView.dozeTimeTick();
-        mKeyguardBottomArea.dozeTimeTick();
         if (mInterpolatedDarkAmount > 0) {
             positionClockAndNotifications();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 6a4da98..e25c829 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -93,12 +93,12 @@
      * A scrim varies its opacity based on a busyness factor, for example
      * how many notifications are currently visible.
      */
-    public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f;
+    public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.7f;
     /**
-     * A scrim varies its opacity based on a busyness factor, for example
-     * how many notifications are currently visible.
+     * Scrim opacity when a wallpaper doesn't support ambient mode.
      */
-    public static final float GRADIENT_SCRIM_DARK_KEYGUARD = 0.80f;
+    public static final float PULSING_WALLPAPER_SCRIM_ALPHA = 0.6f;
+
     /**
      * The most common scrim, the one under the keyguard.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 226665e..fb3c4aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -141,7 +141,8 @@
 
         @Override
         public float getBehindAlpha(float busyness) {
-            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
+            return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f
+                    : ScrimController.PULSING_WALLPAPER_SCRIM_ALPHA;
         }
     },
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0a8c08e..7f16480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -148,7 +148,6 @@
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.doze.LockScreenWakeUpController;
 import com.android.systemui.fragments.ExtensionFragmentListener;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -587,7 +586,6 @@
     protected NotificationPresenter mPresenter;
     private NotificationActivityStarter mNotificationActivityStarter;
     private boolean mPulsing;
-    private LockScreenWakeUpController mLockScreenWakeUpController;
 
     @Override
     public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
@@ -995,7 +993,6 @@
         for (int i = 0; i < pattern.length; i++) {
             mCameraLaunchGestureVibePattern[i] = pattern[i];
         }
-        mLockScreenWakeUpController = new LockScreenWakeUpController(mContext, mDozeServiceHost);
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index f043a39..b561ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -122,6 +122,9 @@
      * @param shouldListen whether we should start listening to various wifi statuses
      */
     private void updateWifiStateListeners(boolean shouldListen) {
+        if (mWifiManager == null) {
+            return;
+        }
         if (shouldListen) {
             mWifiManager.registerSoftApCallback(
                     this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 693df88..6f63544 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -53,7 +53,9 @@
                 connectivityManager, this::handleStatusUpdated);
         mWifiTracker.setListening(true);
         mHasMobileData = hasMobileData;
-        wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback(),  null);
+        if (wifiManager != null) {
+            wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback(), null);
+        }
         // WiFi only has one state.
         mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
                 "Wi-Fi Icons",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/LockScreenWakeUpControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/LockScreenWakeUpControllerTest.java
deleted file mode 100644
index 8963b59..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/LockScreenWakeUpControllerTest.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2018 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.doze;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.Handler;
-import android.os.PowerManager;
-import android.support.test.filters.SmallTest;
-
-import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.util.AsyncSensorManager;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class LockScreenWakeUpControllerTest extends SysuiTestCase {
-
-    @Mock
-    private AsyncSensorManager mAsyncSensorManager;
-    @Mock
-    private SensorManagerPlugin.Sensor mSensor;
-    @Mock
-    private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
-    @Mock
-    private PowerManager mPowerManager;
-    @Mock
-    private DozeHost mDozeHost;
-    @Mock
-    private StatusBarStateController mStatusBarStateController;
-    @Mock
-    private Handler mHandler;
-
-    private LockScreenWakeUpController mLockScreenWakeUpController;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        doAnswer(invocation -> {
-            ((Runnable) invocation.getArgument(0)).run();
-            return null;
-        }).when(mHandler).post(any());
-
-        mLockScreenWakeUpController = new LockScreenWakeUpController(mAsyncSensorManager, mSensor,
-                mAmbientDisplayConfiguration, mPowerManager, mDozeHost, mStatusBarStateController,
-                mHandler);
-    }
-
-    @Test
-    public void testOnStateChanged_registersUnregistersListener() {
-        when(mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(anyInt())).thenReturn(true);
-        mLockScreenWakeUpController.onStateChanged(StatusBarState.KEYGUARD);
-        mLockScreenWakeUpController.onStateChanged(StatusBarState.SHADE);
-
-        verify(mAsyncSensorManager, times(1)).registerPluginListener(eq(mSensor),
-                eq(mLockScreenWakeUpController));
-
-        mLockScreenWakeUpController.onStateChanged(StatusBarState.SHADE);
-        verify(mAsyncSensorManager).unregisterPluginListener(eq(mSensor),
-                eq(mLockScreenWakeUpController));
-    }
-
-    @Test
-    public void testOnStateChanged_disabledSensor() {
-        when(mAmbientDisplayConfiguration.wakeLockScreenGestureEnabled(anyInt()))
-                .thenReturn(false);
-        mLockScreenWakeUpController.onStateChanged(StatusBarState.KEYGUARD);
-        mLockScreenWakeUpController.onStateChanged(StatusBarState.SHADE);
-
-        verify(mAsyncSensorManager, never()).registerPluginListener(eq(mSensor),
-                eq(mLockScreenWakeUpController));
-    }
-
-    @Test
-    public void testOnSensorChanged_postsToMainThread() {
-        SensorManagerPlugin.SensorEvent event = new SensorManagerPlugin.SensorEvent(mSensor, 0);
-        mLockScreenWakeUpController.onSensorChanged(event);
-
-        verify(mHandler).post(any());
-    }
-
-    @Test
-    public void testOnSensorChanged_wakeUpWhenDozing() {
-        SensorManagerPlugin.SensorEvent event =
-                new SensorManagerPlugin.SensorEvent(mSensor, 0, new float[] {1});
-        mLockScreenWakeUpController.onSensorChanged(event);
-        verify(mPowerManager, never()).wakeUp(anyLong(), any());
-
-        mLockScreenWakeUpController.onDozingChanged(true);
-        mLockScreenWakeUpController.onSensorChanged(event);
-        verify(mPowerManager).wakeUp(anyLong(), any());
-    }
-
-    @Test
-    public void testOnSensorChanged_sleepsWhenAwake() {
-        boolean[] goToSleep = new boolean[] {false};
-        doAnswer(invocation -> goToSleep[0] = true)
-                .when(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
-        SensorManagerPlugin.SensorEvent event =
-                new SensorManagerPlugin.SensorEvent(mSensor, 0, new float[] {0});
-        mLockScreenWakeUpController.onDozingChanged(true);
-        mLockScreenWakeUpController.onSensorChanged(event);
-        Assert.assertFalse("goToSleep should have never been called.", goToSleep[0]);
-
-        mLockScreenWakeUpController.onDozingChanged(false);
-        mLockScreenWakeUpController.onSensorChanged(event);
-        Assert.assertTrue("goToSleep should have been called.", goToSleep[0]);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 0bc304e..146c5d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -220,9 +220,9 @@
         mScrimController.transitionTo(ScrimState.PULSING);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
-        // Back scrim should be visible with tint
+        // Back scrim should be semi-transparent so the user can see the wallpaper
         // Pulse callback should have been invoked
-        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE);
+        assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_SEMI_TRANSPARENT);
         assertScrimTint(mScrimBehind, true /* tinted */);
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 292aae8..ef7555b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4619,10 +4619,30 @@
             mService = service;
         }
 
+        @RequiresPermission(allOf = {
+                Manifest.permission.WRITE_SECURE_SETTINGS,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL})
         @BinderThread
         @ShellCommandResult
         @Override
         public int onCommand(@Nullable String cmd) {
+            // For shell command, require all the permissions here in favor of code simplicity.
+            mService.mContext.enforceCallingPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+            mService.mContext.enforceCallingPermission(
+                    Manifest.permission.WRITE_SECURE_SETTINGS, null);
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return onCommandWithSystemIdentity(cmd);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @BinderThread
+        @ShellCommandResult
+        private int onCommandWithSystemIdentity(@Nullable String cmd) {
             if ("refresh_debug_properties".equals(cmd)) {
                 return refreshDebugProperties();
             }
@@ -4767,25 +4787,13 @@
      */
     @BinderThread
     @ShellCommandResult
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
     private int handleShellCommandEnableDisableInputMethod(
             @NonNull ShellCommand shellCommand, boolean enabled) {
         final String id = shellCommand.getNextArgRequired();
 
         final boolean previouslyEnabled;
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
-                shellCommand.getErrPrintWriter().print("Must be called from the foreground user or"
-                        + " with INTERACT_ACROSS_USERS_FULL");
-                return ShellCommandResult.FAILURE;
-            }
-            mContext.enforceCallingPermission(Manifest.permission.WRITE_SECURE_SETTINGS, null);
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                previouslyEnabled = setInputMethodEnabledLocked(id, enabled);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            previouslyEnabled = setInputMethodEnabledLocked(id, enabled);
         }
         final PrintWriter pr = shellCommand.getOutPrintWriter();
         pr.print("Input method ");
@@ -4805,7 +4813,9 @@
     @ShellCommandResult
     private int handleShellCommandSetInputMethod(@NonNull ShellCommand shellCommand) {
         final String id = shellCommand.getNextArgRequired();
-        setInputMethod(null, id);
+        synchronized (mMethodMap) {
+            setInputMethodLocked(id, NOT_A_SUBTYPE_ID);
+        }
         final PrintWriter pr = shellCommand.getOutPrintWriter();
         pr.print("Input method ");
         pr.print(id);
@@ -4820,55 +4830,29 @@
      */
     @BinderThread
     @ShellCommandResult
-    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
     private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
         synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
-                shellCommand.getErrPrintWriter().print("Must be called from the foreground user or"
-                        + " with INTERACT_ACROSS_USERS_FULL");
-                return ShellCommandResult.FAILURE;
-            }
-            mContext.enforceCallingPermission(Manifest.permission.WRITE_SECURE_SETTINGS, null);
             final String nextIme;
             final List<InputMethodInfo> nextEnabledImes;
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                synchronized (mMethodMap) {
-                    hideCurrentInputLocked(0, null);
-                    unbindCurrentMethodLocked();
-                    // Reset the current IME
-                    resetSelectedInputMethodAndSubtypeLocked(null);
-                    // Also reset the settings of the current IME
-                    mSettings.putSelectedInputMethod(null);
-                    // Disable all enabled IMEs.
-                    {
-                        final ArrayList<InputMethodInfo> enabledImes =
-                                mSettings.getEnabledInputMethodListLocked();
-                        final int N = enabledImes.size();
-                        for (int i = 0; i < N; ++i) {
-                            setInputMethodEnabledLocked(enabledImes.get(i).getId(), false);
-                        }
-                    }
-                    // Re-enable with default enabled IMEs.
-                    {
-                        final ArrayList<InputMethodInfo> defaultEnabledIme =
-                                InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList);
-                        final int N = defaultEnabledIme.size();
-                        for (int i = 0; i < N; ++i) {
-                            setInputMethodEnabledLocked(defaultEnabledIme.get(i).getId(), true);
-                        }
-                    }
-                    updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
-                    InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
-                            mSettings.getEnabledInputMethodListLocked(),
-                            mSettings.getCurrentUserId(),
-                            mContext.getBasePackageName());
-                    nextIme = mSettings.getSelectedInputMethod();
-                    nextEnabledImes = getEnabledInputMethodList();
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            hideCurrentInputLocked(0, null);
+            unbindCurrentMethodLocked();
+            // Reset the current IME
+            resetSelectedInputMethodAndSubtypeLocked(null);
+            // Also reset the settings of the current IME
+            mSettings.putSelectedInputMethod(null);
+            // Disable all enabled IMEs.
+            mSettings.getEnabledInputMethodListLocked().forEach(
+                    imi -> setInputMethodEnabledLocked(imi.getId(), false));
+            // Re-enable with default enabled IMEs.
+            InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach(
+                    imi -> setInputMethodEnabledLocked(imi.getId(), true));
+            updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
+            InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
+                    mSettings.getEnabledInputMethodListLocked(),
+                    mSettings.getCurrentUserId(),
+                    mContext.getBasePackageName());
+            nextIme = mSettings.getSelectedInputMethod();
+            nextEnabledImes = getEnabledInputMethodList();
             final PrintWriter pr = shellCommand.getOutPrintWriter();
             pr.println("Reset current and enabled IMEs");
             pr.println("Newly selected IME:");
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 598f567..fa19867 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -16,7 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.cdma.CdmaCellLocation;
 
@@ -71,30 +70,13 @@
      *        to 2592000
      * @param lat Latitude is a decimal number ranges from -1296000
      *        to 1296000
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat) {
-        this(nid, sid, bid, lon, lat, null, null);
-    }
-
-    /**
-     * public constructor
-     * @param nid Network Id 0..65535
-     * @param sid CDMA System Id 0..32767
-     * @param bid Base Station Id 0..65535
-     * @param lon Longitude is a decimal number ranges from -2592000
-     *        to 2592000
-     * @param lat Latitude is a decimal number ranges from -1296000
-     *        to 1296000
      * @param alphal long alpha Operator Name String or Enhanced Operator Name String
      * @param alphas short alpha Operator Name String or Enhanced Operator Name String
      *
      * @hide
      */
-    public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
-                             String alphas) {
+    public CellIdentityCdma(
+            int nid, int sid, int bid, int lon, int lat, String alphal, String alphas) {
         super(TAG, CellInfo.TYPE_CDMA, null, null, alphal, alphas);
         mNetworkId = nid;
         mSystemId = sid;
@@ -107,6 +89,17 @@
         }
     }
 
+    /** @hide */
+    public CellIdentityCdma(android.hardware.radio.V1_0.CellIdentityCdma cid) {
+        this(cid.networkId, cid.systemId, cid.baseStationId, cid.longitude, cid.latitude, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityCdma(android.hardware.radio.V1_2.CellIdentityCdma cid) {
+        this(cid.base.networkId, cid.base.systemId, cid.base.baseStationId, cid.base.longitude,
+                cid.base.latitude, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
+    }
+
     private CellIdentityCdma(CellIdentityCdma cid) {
         this(cid.mNetworkId, cid.mSystemId, cid.mBasestationId, cid.mLongitude, cid.mLatitude,
                 cid.mAlphaLong, cid.mAlphaShort);
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index c8a899b..9a24e47 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -36,10 +36,8 @@
     // 16-bit GSM Cell Identity described in TS 27.007, 0..65535
     private final int mCid;
     // 16-bit GSM Absolute RF Channel Number
-    @UnsupportedAppUsage
     private final int mArfcn;
     // 6-bit Base Station Identity Code
-    @UnsupportedAppUsage
     private final int mBsic;
 
     /**
@@ -53,34 +51,6 @@
         mArfcn = CellInfo.UNAVAILABLE;
         mBsic = CellInfo.UNAVAILABLE;
     }
-    /**
-     * public constructor
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535
-     * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
-     *
-     * @hide
-     */
-    public CellIdentityGsm(int mcc, int mnc, int lac, int cid) {
-        this(lac, cid, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
-                String.valueOf(mcc), String.valueOf(mnc), null, null);
-    }
-
-    /**
-     * public constructor
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535
-     * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
-     * @param arfcn 16-bit GSM Absolute RF Channel Number
-     * @param bsic 6-bit Base Station Identity Code
-     *
-     * @hide
-     */
-    public CellIdentityGsm(int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
-        this(lac, cid, arfcn, bsic, String.valueOf(mcc), String.valueOf(mnc), null, null);
-    }
 
     /**
      * public constructor
@@ -101,9 +71,21 @@
         mLac = lac;
         mCid = cid;
         mArfcn = arfcn;
-        // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
-        // for inbound parcels
-        mBsic = (bsic == 0xFF) ? CellInfo.UNAVAILABLE : bsic;
+        mBsic = bsic;
+    }
+
+    /** @hide */
+    public CellIdentityGsm(android.hardware.radio.V1_0.CellIdentityGsm cid) {
+        this(cid.lac, cid.cid, cid.arfcn,
+                cid.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.bsic,
+                cid.mcc, cid.mnc, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityGsm(android.hardware.radio.V1_2.CellIdentityGsm cid) {
+        this(cid.base.lac, cid.base.cid, cid.base.arfcn,
+                cid.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.base.bsic, cid.base.mcc,
+                cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 8e1877d..d957d07 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -38,7 +38,6 @@
     // 16-bit tracking area code
     private final int mTac;
     // 18-bit Absolute RF Channel Number
-    @UnsupportedAppUsage
     private final int mEarfcn;
     // cell bandwidth, in kHz
     private final int mBandwidth;
@@ -74,22 +73,6 @@
 
     /**
      *
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param ci 28-bit Cell Identity
-     * @param pci Physical Cell Id 0..503
-     * @param tac 16-bit Tracking Area Code
-     * @param earfcn 18-bit LTE Absolute RF Channel Number
-     *
-     * @hide
-     */
-    public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
-        this(ci, pci, tac, earfcn, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
-                null, null);
-    }
-
-    /**
-     *
      * @param ci 28-bit Cell Identity
      * @param pci Physical Cell Id 0..503
      * @param tac 16-bit Tracking Area Code
@@ -112,6 +95,18 @@
         mBandwidth = bandwidth;
     }
 
+    /** @hide */
+    public CellIdentityLte(android.hardware.radio.V1_0.CellIdentityLte cid) {
+        this(cid.ci, cid.pci, cid.tac, cid.earfcn, CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityLte(android.hardware.radio.V1_2.CellIdentityLte cid) {
+        this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
+                cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
+                cid.operatorNames.alphaShort);
+    }
+
     private CellIdentityLte(CellIdentityLte cid) {
         this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index f77c468..3814333 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -51,22 +51,6 @@
     }
 
     /**
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
-     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, CellInfo.
-     *        UNAVAILABLE if unknown
-     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, CellInfo.UNAVAILABLE
-     *        if unknown
-     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
-     *
-     * @hide
-     */
-    public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid, int uarfcn) {
-        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, uarfcn, null, null);
-    }
-
-    /**
      * @param mcc 3-digit Mobile Country Code in string format
      * @param mnc 2 or 3-digit Mobile Network Code in string format
      * @param lac 16-bit Location Area Code, 0..65535, CellInfo.UNAVAILABLE if unknown
@@ -94,6 +78,17 @@
                 cid.mCpid, cid.mUarfcn, cid.mAlphaLong, cid.mAlphaShort);
     }
 
+    /** @hide */
+    public CellIdentityTdscdma(android.hardware.radio.V1_0.CellIdentityTdscdma cid) {
+        this(cid.mcc, cid.mnc, cid.lac, cid.cid, cid.cpid, CellInfo.UNAVAILABLE, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityTdscdma(android.hardware.radio.V1_2.CellIdentityTdscdma cid) {
+        this(cid.base.mcc, cid.base.mnc, cid.base.lac, cid.base.cid, cid.base.cpid,
+                cid.uarfcn, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort);
+    }
+
     CellIdentityTdscdma copy() {
         return new CellIdentityTdscdma(this);
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 31f9e6d..6e09784 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -51,35 +51,6 @@
         mPsc = CellInfo.UNAVAILABLE;
         mUarfcn = CellInfo.UNAVAILABLE;
     }
-    /**
-     * public constructor
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535
-     * @param cid 28-bit UMTS Cell Identity
-     * @param psc 9-bit UMTS Primary Scrambling Code
-     *
-     * @hide
-     */
-    public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
-        this(lac, cid, psc, CellInfo.UNAVAILABLE, String.valueOf(mcc), String.valueOf(mnc),
-                null, null);
-    }
-
-    /**
-     * public constructor
-     * @param mcc 3-digit Mobile Country Code, 0..999
-     * @param mnc 2 or 3-digit Mobile Network Code, 0..999
-     * @param lac 16-bit Location Area Code, 0..65535
-     * @param cid 28-bit UMTS Cell Identity
-     * @param psc 9-bit UMTS Primary Scrambling Code
-     * @param uarfcn 16-bit UMTS Absolute RF Channel Number described in TS 25.101 sec. 5.4.3
-     *
-     * @hide
-     */
-    public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc, int uarfcn) {
-        this(lac, cid, psc, uarfcn, String.valueOf(mcc), String.valueOf(mnc), null, null);
-    }
 
     /**
      * public constructor
@@ -103,6 +74,18 @@
         mUarfcn = uarfcn;
     }
 
+    /** @hide */
+    public CellIdentityWcdma(android.hardware.radio.V1_0.CellIdentityWcdma cid) {
+        this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "");
+    }
+
+    /** @hide */
+    public CellIdentityWcdma(android.hardware.radio.V1_2.CellIdentityWcdma cid) {
+        this(cid.base.lac, cid.base.cid, cid.base.psc, cid.base.uarfcn,
+                cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
+                cid.operatorNames.alphaShort);
+    }
+
     private CellIdentityWcdma(CellIdentityWcdma cid) {
         this(cid.mLac, cid.mCid, cid.mPsc, cid.mUarfcn, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort);
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index d0b26876..b761bd7 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -132,7 +132,8 @@
     /** Connection status is unknown. */
     public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE;
 
-    private int mCellConnectionStatus = CONNECTION_NONE;
+    /** A cell connection status */
+    private int mCellConnectionStatus;
 
     // True if device is mRegistered to the mobile network
     private boolean mRegistered;
@@ -144,6 +145,7 @@
     protected CellInfo() {
         this.mRegistered = false;
         this.mTimeStamp = Long.MAX_VALUE;
+        mCellConnectionStatus = CONNECTION_NONE;
     }
 
     /** @hide */
@@ -300,4 +302,44 @@
             return new CellInfo[size];
         }
     };
+
+    /** @hide */
+    protected CellInfo(android.hardware.radio.V1_0.CellInfo ci) {
+        this.mRegistered = ci.registered;
+        this.mTimeStamp = ci.timeStamp;
+        this.mCellConnectionStatus = CONNECTION_UNKNOWN;
+    }
+
+    /** @hide */
+    protected CellInfo(android.hardware.radio.V1_2.CellInfo ci) {
+        this.mRegistered = ci.registered;
+        this.mTimeStamp = ci.timeStamp;
+        this.mCellConnectionStatus = ci.connectionStatus;
+    }
+
+    /** @hide */
+    public static CellInfo create(android.hardware.radio.V1_0.CellInfo ci) {
+        if (ci == null) return null;
+        switch(ci.cellInfoType) {
+            case android.hardware.radio.V1_0.CellInfoType.GSM: return new CellInfoGsm(ci);
+            case android.hardware.radio.V1_0.CellInfoType.CDMA: return new CellInfoCdma(ci);
+            case android.hardware.radio.V1_0.CellInfoType.LTE: return new CellInfoLte(ci);
+            case android.hardware.radio.V1_0.CellInfoType.WCDMA: return new CellInfoWcdma(ci);
+            case android.hardware.radio.V1_0.CellInfoType.TD_SCDMA: return new CellInfoTdscdma(ci);
+            default: return null;
+        }
+    }
+
+    /** @hide */
+    public static CellInfo create(android.hardware.radio.V1_2.CellInfo ci) {
+        if (ci == null) return null;
+        switch(ci.cellInfoType) {
+            case android.hardware.radio.V1_0.CellInfoType.GSM: return new CellInfoGsm(ci);
+            case android.hardware.radio.V1_0.CellInfoType.CDMA: return new CellInfoCdma(ci);
+            case android.hardware.radio.V1_0.CellInfoType.LTE: return new CellInfoLte(ci);
+            case android.hardware.radio.V1_0.CellInfoType.WCDMA: return new CellInfoWcdma(ci);
+            case android.hardware.radio.V1_0.CellInfoType.TD_SCDMA: return new CellInfoTdscdma(ci);
+            default: return null;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index f67733d..8c76eae 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -48,6 +48,24 @@
         this.mCellSignalStrengthCdma = ci.mCellSignalStrengthCdma.copy();
     }
 
+    /** @hide */
+    public CellInfoCdma(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoCdma cic = ci.cdma.get(0);
+        mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma);
+        mCellSignalStrengthCdma =
+            new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
+    }
+
+    /** @hide */
+    public CellInfoCdma(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoCdma cic = ci.cdma.get(0);
+        mCellIdentityCdma = new CellIdentityCdma(cic.cellIdentityCdma);
+        mCellSignalStrengthCdma =
+            new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo);
+    }
+
     @Override
     public CellIdentityCdma getCellIdentity() {
         return mCellIdentityCdma;
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 7211de1..ad16dfa 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -43,8 +43,24 @@
     /** @hide */
     public CellInfoGsm(CellInfoGsm ci) {
         super(ci);
-        this.mCellIdentityGsm = ci.mCellIdentityGsm.copy();
-        this.mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
+        mCellIdentityGsm = ci.mCellIdentityGsm.copy();
+        mCellSignalStrengthGsm = ci.mCellSignalStrengthGsm.copy();
+    }
+
+    /** @hide */
+    public CellInfoGsm(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoGsm cig = ci.gsm.get(0);
+        mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm);
+        mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
+    }
+
+    /** @hide */
+    public CellInfoGsm(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoGsm cig = ci.gsm.get(0);
+        mCellIdentityGsm = new CellIdentityGsm(cig.cellIdentityGsm);
+        mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index 7d5388b..8ca6a1a 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -51,6 +51,24 @@
         this.mCellConfig = new CellConfigLte(ci.mCellConfig);
     }
 
+    /** @hide */
+    public CellInfoLte(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoLte cil = ci.lte.get(0);
+        mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte);
+        mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte);
+        mCellConfig = new CellConfigLte();
+    }
+
+    /** @hide */
+    public CellInfoLte(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoLte cil = ci.lte.get(0);
+        mCellIdentityLte = new CellIdentityLte(cil.cellIdentityLte);
+        mCellSignalStrengthLte = new CellSignalStrengthLte(cil.signalStrengthLte);
+        mCellConfig = new CellConfigLte();
+    }
+
     @Override
     public CellIdentityLte getCellIdentity() {
         if (DBG) log("getCellIdentity: " + mCellIdentityLte);
diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java
index 40cadde..a8c49b7 100644
--- a/telephony/java/android/telephony/CellInfoTdscdma.java
+++ b/telephony/java/android/telephony/CellInfoTdscdma.java
@@ -48,8 +48,23 @@
         this.mCellSignalStrengthTdscdma = ci.mCellSignalStrengthTdscdma.copy();
     }
 
-    @Override
-    public CellIdentityTdscdma getCellIdentity() {
+    /** @hide */
+    public CellInfoTdscdma(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoTdscdma cit = ci.tdscdma.get(0);
+        mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma);
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
+    }
+
+    /** @hide */
+    public CellInfoTdscdma(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoTdscdma cit = ci.tdscdma.get(0);
+        mCellIdentityTdscdma = new CellIdentityTdscdma(cit.cellIdentityTdscdma);
+        mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma);
+    }
+
+    @Override public CellIdentityTdscdma getCellIdentity() {
         return mCellIdentityTdscdma;
     }
     /** @hide */
diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java
index 4f9dcb1..a427e80 100644
--- a/telephony/java/android/telephony/CellInfoWcdma.java
+++ b/telephony/java/android/telephony/CellInfoWcdma.java
@@ -47,6 +47,22 @@
         this.mCellSignalStrengthWcdma = ci.mCellSignalStrengthWcdma.copy();
     }
 
+    /** @hide */
+    public CellInfoWcdma(android.hardware.radio.V1_0.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_0.CellInfoWcdma ciw = ci.wcdma.get(0);
+        mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma);
+        mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
+    }
+
+    /** @hide */
+    public CellInfoWcdma(android.hardware.radio.V1_2.CellInfo ci) {
+        super(ci);
+        final android.hardware.radio.V1_2.CellInfoWcdma ciw = ci.wcdma.get(0);
+        mCellIdentityWcdma = new CellIdentityWcdma(ciw.cellIdentityWcdma);
+        mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma);
+    }
+
     @Override
     public CellIdentityWcdma getCellIdentity() {
         return mCellIdentityWcdma;
diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
index 564effe..27a5121 100644
--- a/telephony/java/android/telephony/ModemInfo.java
+++ b/telephony/java/android/telephony/ModemInfo.java
@@ -32,6 +32,11 @@
     public final boolean isVoiceSupported;
     public final boolean isDataSupported;
 
+    // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
+    public ModemInfo(int modemId) {
+        this(modemId, 0, true, true);
+    }
+
     public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
         this.modemId = modemId;
         this.rat = rat;
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 0fdca5d..96f7a1b 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -24,7 +24,7 @@
     /**
      * IDLE: ready to start data connection setup, default state
      * CONNECTING: state of issued startPppd() but not finish yet
-     * SCANNING: data connection fails with one apn but other apns are available
+     * RETRYING: data connection fails with one apn but other apns are available
      *           ready to start data connection on other apns (before INITING)
      * CONNECTED: IP connection is setup
      * DISCONNECTING: Connection.disconnect() has been called, but PDP
@@ -34,19 +34,16 @@
      *
      * getDataConnectionState() maps State to DataState
      *      FAILED or IDLE : DISCONNECTED
-     *      RETRYING or CONNECTING or SCANNING: CONNECTING
+     *      RETRYING or CONNECTING: CONNECTING
      *      CONNECTED : CONNECTED or DISCONNECTING
      */
     public enum State {
         IDLE,
         CONNECTING,
-        SCANNING,
+        RETRYING,
         CONNECTED,
         DISCONNECTING,
         FAILED,
-        RETRYING        // After moving retry manager to ApnContext, we'll never enter this state!
-                        // Todo: Remove this state and other places that use this state and then
-                        // rename SCANNING to RETRYING.
     }
 
     public enum Activity {
@@ -81,7 +78,6 @@
     public static final int EVENT_RESTART_RADIO = BASE + 26;
     public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 29;
     public static final int EVENT_ICC_CHANGED = BASE + 33;
-    public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
     public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
     public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
     public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index f2cb653..1c103a9 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -498,6 +498,7 @@
     int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 203;
     int RIL_REQUEST_SET_PREFERRED_DATA_MODEM = 204;
     int RIL_REQUEST_EMERGENCY_DIAL = 205;
+    int RIL_REQUEST_GET_PHONE_CAPABILITY = 206;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;