Merge "Fix unit test that failed due to incorrect refactor."
diff --git a/api/current.txt b/api/current.txt
index 27fd669..4270b4f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2722,10 +2722,10 @@
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
     method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
-    method public void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public final android.os.IBinder onBind(android.content.Intent);
     method protected boolean onGesture(int);
-    method public void onInterrupt();
+    method public abstract void onInterrupt();
     method protected boolean onKeyEvent(android.view.KeyEvent);
     method protected void onServiceConnected();
     method public final boolean performGlobalAction(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index a06c913..75095b3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2842,10 +2842,10 @@
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
     method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
-    method public void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public final android.os.IBinder onBind(android.content.Intent);
     method protected boolean onGesture(int);
-    method public void onInterrupt();
+    method public abstract void onInterrupt();
     method protected boolean onKeyEvent(android.view.KeyEvent);
     method protected void onServiceConnected();
     method public final boolean performGlobalAction(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index a590e45..af8bb7f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2722,10 +2722,10 @@
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
     method public final android.accessibilityservice.AccessibilityService.SoftKeyboardController getSoftKeyboardController();
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
-    method public void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public final android.os.IBinder onBind(android.content.Intent);
     method protected boolean onGesture(int);
-    method public void onInterrupt();
+    method public abstract void onInterrupt();
     method protected boolean onKeyEvent(android.view.KeyEvent);
     method protected void onServiceConnected();
     method public final boolean performGlobalAction(int);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 3884748..b4e119e 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -421,12 +421,12 @@
      * this method returns. Services wishing to use the event after this method returns should
      * make a copy.
      */
-    public void onAccessibilityEvent(AccessibilityEvent event) {}
+    public abstract void onAccessibilityEvent(AccessibilityEvent event);
 
     /**
      * Callback for interrupting the accessibility feedback.
      */
-    public void onInterrupt() {}
+    public abstract void onInterrupt();
 
     /**
      * Dispatches service connection to internal components first, then the
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8666930..66c394f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -953,7 +953,7 @@
     public @interface AutoFillMode {}
 
     /**
-     * This view inherits the auto-fill state from it's parent. If there is no parent it is
+     * This view inherits the autofill state from it's parent. If there is no parent it is
      * {@link #AUTO_FILL_MODE_AUTO}.
      * Use with {@link #setAutoFillMode(int)} and <a href="#attr_android:autoFillMode">
      * {@code android:autoFillMode}.
@@ -968,11 +968,7 @@
     public static final int AUTO_FILL_MODE_AUTO = 1;
 
     /**
-     * Do not trigger an auto-fill request if this view is focused. The user can still force
-     * an auto-fill request.
-     * <p>This does not prevent this field from being auto-filled if an auto-fill operation is
-     * triggered from a different view.</p>
-     *
+     * Require the user to manually force an auto-fill request.
      * Use with {@link #setAutoFillMode(int)} and <a href="#attr_android:autoFillMode">{@code
      * android:autoFillMode}.
      */
@@ -6527,12 +6523,7 @@
         if (isAutoFillable()) {
             AutoFillManager afm = getAutoFillManager();
             if (afm != null) {
-                boolean adjGainFocus = gainFocus;
-                if (adjGainFocus && getResolvedAutoFillMode() == AUTO_FILL_MODE_MANUAL) {
-                    adjGainFocus = false;
-                }
-
-                afm.focusChanged(this, adjGainFocus);
+                afm.focusChanged(this, gainFocus);
             }
         }
 
@@ -9316,30 +9307,6 @@
     }
 
     /**
-     * Returns the resolved auto-fill mode for this view.
-     *
-     * This is the same as {@link #getAutoFillMode()} but if the mode is
-     * {@link #AUTO_FILL_MODE_INHERIT} the parents auto-fill mode will be returned.
-     *
-     * @return One of {@link #AUTO_FILL_MODE_AUTO}, or {@link #AUTO_FILL_MODE_MANUAL}.
-     *
-     * @hide
-     */
-    public @AutoFillMode int getResolvedAutoFillMode() {
-        @AutoFillMode int autoFillMode = getAutoFillMode();
-
-        if (autoFillMode == AUTO_FILL_MODE_INHERIT) {
-            if (mParent == null) {
-                throw new IllegalStateException("View is detached, cannot resolve autoFillMode");
-            } else {
-                return mParent.getResolvedAutoFillMode();
-            }
-        } else {
-            return autoFillMode;
-        }
-    }
-
-    /**
      * Find the nearest view in the specified direction that can take focus.
      * This does not actually give focus to that view.
      *
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index cdfd61b..cc11cb8 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -659,17 +659,4 @@
      * @return true if the action was consumed by this ViewParent
      */
     public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments);
-
-    /**
-     * Return the resolved auto-fill mode.
-     *
-     * @return The resolved auto-fill mode
-     *
-     * @see View#getResolvedAutoFillMode()
-     *
-     * @hide
-     */
-    default @View.AutoFillMode int getResolvedAutoFillMode() {
-        return View.AUTO_FILL_MODE_AUTO;
-    }
 }
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index baba389..e82021e 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -23,6 +23,7 @@
 import android.content.IntentSender;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.util.Log;
@@ -131,7 +132,7 @@
         if (!mHasSession) {
             if (gainFocus) {
                 // Starts new session.
-                startSession(id, bounds, value);
+                startSession(id, view.getWindowToken(), bounds, value);
             }
         } else {
             // Update focus on existing session.
@@ -159,7 +160,7 @@
         if (!mHasSession) {
             if (gainFocus) {
                 // Starts new session.
-                startSession(id, bounds, null);
+                startSession(id, parent.getWindowToken(), bounds, null);
             }
         } else {
             // Update focus on existing session.
@@ -251,13 +252,14 @@
         return new AutoFillId(parent.getAccessibilityViewId(), childId);
     }
 
-    private void startSession(AutoFillId id, Rect bounds, AutoFillValue value) {
+    private void startSession(AutoFillId id, IBinder windowToken,
+            Rect bounds, AutoFillValue value) {
         if (DEBUG) {
             Log.v(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
         }
         try {
-            mService.startSession(mContext.getActivityToken(), mServiceClient.asBinder(),
-                    id, bounds, value, mContext.getUserId());
+            mService.startSession(mContext.getActivityToken(), windowToken,
+                    mServiceClient.asBinder(), id, bounds, value, mContext.getUserId());
             AutoFillClient client = getClient();
             if (client != null) {
                 client.resetableStateAvailable();
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 0433a8f..d054e97 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -30,7 +30,7 @@
  */
 interface IAutoFillManager {
     boolean addClient(in IAutoFillManagerClient client, int userId);
-    oneway void startSession(in IBinder activityToken, in IBinder appCallback,
+    oneway void startSession(in IBinder activityToken, IBinder windowToken, in IBinder appCallback,
             in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId);
     oneway void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds,
             in AutoFillValue value, int flags, int userId);
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 555263d..28291ae 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -496,6 +496,7 @@
                 } finally {
                     args.recycle();
                 }
+                return;
             }
             case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
                 SomeArgs args = (SomeArgs)msg.obj;
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
index 45d5061..f1bc76e 100644
--- a/core/jni/android_os_seccomp.cpp
+++ b/core/jni/android_os_seccomp.cpp
@@ -65,11 +65,6 @@
 
 #pragma clang diagnostic pop
 
-inline static void AllowSyscall(filter& f, __u32 num) {
-    f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, num, 0, 1));
-    Allow(f);
-}
-
 inline static void ExamineSyscall(filter& f) {
     f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
 }
@@ -125,34 +120,6 @@
     // arm64-only filter - autogenerated from bionic syscall usage
     for (size_t i = 0; i < arm64_filter_size; ++i)
         f.push_back(arm64_filter[i]);
-
-    // Syscalls needed to boot Android
-    AllowSyscall(f, 41);  // __NR_pivot_root
-    AllowSyscall(f, 31);  // __NR_ioprio_get
-    AllowSyscall(f, 30);  // __NR_ioprio_set
-    AllowSyscall(f, 178); // __NR_gettid
-    AllowSyscall(f, 98);  // __NR_futex
-    AllowSyscall(f, 220); // __NR_clone
-    AllowSyscall(f, 139); // __NR_rt_sigreturn
-    AllowSyscall(f, 240); // __NR_rt_tgsigqueueinfo
-    AllowSyscall(f, 128); // __NR_restart_syscall
-    AllowSyscall(f, 278); // __NR_getrandom
-
-    // Needed for performance tools
-    AllowSyscall(f, 241); // __NR_perf_event_open
-
-    // Needed for strace
-    AllowSyscall(f, 130); // __NR_tkill
-
-    // Needed for kernel to restart syscalls
-    AllowSyscall(f, 128); // __NR_restart_syscall
-
-    // b/35034743
-    AllowSyscall(f, 267); // __NR_syncfs
-
-    // b/34763393
-    AllowSyscall(f, 277); // __NR_seccomp
-
     Trap(f);
 
     if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0)
@@ -164,64 +131,6 @@
     // arm32 filter - autogenerated from bionic syscall usage
     for (size_t i = 0; i < arm_filter_size; ++i)
         f.push_back(arm_filter[i]);
-
-    // Syscalls needed to boot android
-    AllowSyscall(f, 120); // __NR_clone
-    AllowSyscall(f, 240); // __NR_futex
-    AllowSyscall(f, 119); // __NR_sigreturn
-    AllowSyscall(f, 173); // __NR_rt_sigreturn
-    AllowSyscall(f, 363); // __NR_rt_tgsigqueueinfo
-    AllowSyscall(f, 224); // __NR_gettid
-
-    // Syscalls needed to run Chrome
-    AllowSyscall(f, 383); // __NR_seccomp - needed to start Chrome
-    AllowSyscall(f, 384); // __NR_getrandom - needed to start Chrome
-
-    // Syscalls needed to run GFXBenchmark
-    AllowSyscall(f, 190); // __NR_vfork
-
-    // Needed for strace
-    AllowSyscall(f, 238); // __NR_tkill
-
-    // Needed for kernel to restart syscalls
-    AllowSyscall(f, 0);   // __NR_restart_syscall
-
-    // Needed for debugging 32-bit Chrome
-    AllowSyscall(f, 42);  // __NR_pipe
-
-    // b/34732712
-    AllowSyscall(f, 364); // __NR_perf_event_open
-
-    // b/34651972
-    AllowSyscall(f, 33);  // __NR_access
-    AllowSyscall(f, 195); // __NR_stat64
-
-    // b/34813887
-    AllowSyscall(f, 5);   // __NR_open
-    AllowSyscall(f, 141); // __NR_getdents
-    AllowSyscall(f, 217); // __NR_getdents64
-
-    // b/34719286
-    AllowSyscall(f, 351); // __NR_eventfd
-
-    // b/34817266
-    AllowSyscall(f, 252); // __NR_epoll_wait
-
-    // Needed by sanitizers (b/34606909)
-    // 5 (__NR_open) and 195 (__NR_stat64) are also required, but they are
-    // already allowed.
-    AllowSyscall(f, 85);  // __NR_readlink
-
-    // b/34908783
-    AllowSyscall(f, 250); // __NR_epoll_create
-
-    // b/34979910
-    AllowSyscall(f, 8);   // __NR_creat
-    AllowSyscall(f, 10);  // __NR_unlink
-
-    // b/35059702
-    AllowSyscall(f, 196); // __NR_lstat64
-
     Trap(f);
 
     return install_filter(f);
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index d55a012..8453cd3 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -14,44 +14,79 @@
      limitations under the License.
 -->
 
-<!-- TODO(b/33197203) remove hardcoded color once color is final -->
-<RelativeLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:paddingStart="16dip"
     android:paddingEnd="16dip"
     android:paddingTop="16dip"
     android:paddingBottom="16dip"
-    android:background="#FDF8C8">
+    android:elevation="16dp"
+    android:background="?android:attr/colorBackground"
+    android:orientation="vertical">
 
-  <!-- TODO(b/33197203) use.R.string once final wording is done -->
-  <TextView
-      android:id="@+id/autofill_save_title"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:text="Save for autofill?"
-      android:singleLine="true"/>
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
 
-  <TextView
-      android:id="@+id/autofill_save_no"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_below="@+id/autofill_save_title"
-      android:layout_toLeftOf="@+id/autofill_save_yes"
-      android:layout_marginRight="16dip"
-      android:text="No thanks"
-      android:textAllCaps="true"
-      android:singleLine="true"/>
+        <TextView
+            android:id="@+id/autofill_save_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/autofill_save_title"
+            android:singleLine="true">
+        </TextView>
 
-    <TextView
-      android:id="@+id/autofill_save_yes"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_below="@+id/autofill_save_title"
-      android:layout_alignParentRight="true"
-      android:text="Save"
-      android:textAllCaps="true"
-      android:singleLine="true"/>
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:visibility="invisible" >
+        </Space>
 
-</RelativeLayout>
+        <ImageView
+            android:id="@+id/autofill_save_close"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@android:drawable/ic_close"
+            android:background="?android:attr/selectableItemBackgroundBorderless">
+        </ImageView>
+
+    </LinearLayout>
+
+    <com.android.internal.widget.ButtonBarLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end"
+        android:layout_marginTop="16dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:visibility="invisible" >
+        </Space>
+
+        <Button
+            android:id="@+id/autofill_save_no"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="?android:attr/buttonBarButtonStyle"
+            android:text="@string/autofill_save_no" >
+        </Button>
+
+        <Button
+            android:id="@+id/autofill_save_yes"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            style="?android:attr/buttonBarButtonStyle"
+            android:text="@string/autofill_save_yes" >
+        </Button>
+
+   </com.android.internal.widget.ButtonBarLayout>
+
+</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d2d6620..8031f19 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2289,14 +2289,12 @@
 
         <!-- Controls the auto-fill behavior for this view -->
         <attr name="autoFillMode">
-            <!-- Inherit the behavior from the parent. If there is no parent it is auto. This is the
-                 default value for this attribute.-->
+            <!-- Inherit the behavior from the parent. If there is no parent it is auto. -->
             <enum name="inherit" value="0" />
             <!-- Allows this view to automatically trigger an auto-fill request when it get focus.
                  -->
             <enum name="auto" value="1" />
-            <!-- Do not trigger an auto-fill request when this view is focused. The user can still
-                 manually force an auto-fill request for this view. -->
+            <!-- The user has to manually force an auto-fill request for this view. -->
             <enum name="manual" value="2" />
         </attr>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f6da660..6abc009 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4510,4 +4510,11 @@
     <!-- Accessibility string used for describing the button in time picker that changes the dialog to circular clock mode. [CHAR LIMIT=NONE] -->
     <string name="time_picker_radial_mode_description">Switch to clock mode for the time input.</string>
 
+    <!-- Title for the auto-fill save dialog shown when the user entered savable text [CHAR LIMIT=NONE] -->
+    <string name="autofill_save_title">Save to <xliff:g id="label" example="MyPass">%1$s</xliff:g>?</string>
+    <!-- Label for the auto-fill save button [CHAR LIMIT=NONE] -->
+    <string name="autofill_save_yes">Save</string>
+    <!-- Label for the auto-fill cancel button [CHAR LIMIT=NONE] -->
+    <string name="autofill_save_no">No thanks</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eb248e5..e7d3ec9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2842,6 +2842,10 @@
   <java-symbol type="id" name="autofill_save_title" />
   <java-symbol type="id" name="autofill_save_no" />
   <java-symbol type="id" name="autofill_save_yes" />
+  <java-symbol type="id" name="autofill_save_close" />
+  <java-symbol type="string" name="autofill_save_title" />
+  <java-symbol type="string" name="autofill_save_yes" />
+  <java-symbol type="string" name="autofill_save_no" />
 
   <!-- Accessibility fingerprint gestures -->
   <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index b397b45..1f5986f 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -58,7 +58,7 @@
     protected float mAuxEffectSendLevel = 0.0f;
 
     // for AppOps
-    private IAppOpsService mAppOps;
+    private IAppOpsService mAppOps; // may be null
     private IAppOpsCallback mAppOpsCallback;
     private boolean mHasAppOpsPlayAudio = true; // sync'd on mLock
     private final Object mLock = new Object();
@@ -251,7 +251,9 @@
             Log.e(TAG, "Error talking to audio service, the player will still be tracked", e);
         }
         try {
-            mAppOps.stopWatchingMode(mAppOpsCallback);
+            if (mAppOps != null) {
+                mAppOps.stopWatchingMode(mAppOpsCallback);
+            }
         } catch (RemoteException e) {
             // nothing to do here, the object is supposed to be released anyway
         }
@@ -264,9 +266,12 @@
     void updateAppOpsPlayAudio_sync() {
         boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
         try {
-            final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+            int mode = AppOpsManager.MODE_IGNORED;
+            if (mAppOps != null) {
+                mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
                     mAttributes.getUsage(),
                     Process.myUid(), ActivityThread.currentPackageName());
+            }
             mHasAppOpsPlayAudio = (mode == AppOpsManager.MODE_ALLOWED);
         } catch (RemoteException e) {
             mHasAppOpsPlayAudio = false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 6e10aab..f31c09b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -33,7 +33,6 @@
 import android.os.UserManager;
 import android.provider.Settings.Global;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 
@@ -353,10 +352,7 @@
             CharSequence title = null;
             String summary = null;
             String keyHint = null;
-            String uriString = null;
             Uri uri = null;
-            // Several resources can be using the same provider. Only acquire a single provider.
-            Map<String, IContentProvider> providerMap = new ArrayMap<>();
 
             // Get the activity's meta-data
             try {
@@ -365,11 +361,7 @@
                 Bundle metaData = activityInfo.metaData;
 
                 if (res != null && metaData != null) {
-                    if (metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
-                        iconFromUri = getIconFromUri(context, activityInfo.packageName,
-                                metaData.getString(META_DATA_PREFERENCE_ICON_URI), providerMap);
-                    }
-                    if (iconFromUri == null && metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
+                    if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
                         icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
                     }
                     if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
@@ -379,13 +371,7 @@
                             title = metaData.getString(META_DATA_PREFERENCE_TITLE);
                         }
                     }
-                    if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
-                        summary = getTextFromUri(context,
-                                metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI), providerMap,
-                                META_DATA_PREFERENCE_SUMMARY);
-                    }
-                    if (TextUtils.isEmpty(summary)
-                            && metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
+                    if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
                         if (metaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
                             summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
                         } else {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 1683901..2d3c4a7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -282,26 +282,11 @@
         when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
                 .thenReturn(info);
 
-        Bundle bundle = new Bundle();
-        bundle.putInt("com.android.settings.icon", 161803);
-        bundle.putString("com.android.settings.icon_package", "abc");
-        bundle.putString("com.android.settings.summary", "dynamic-summary");
-        when(mIContentProvider.call(anyString(),
-                eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_ICON))), eq(URI_GET_ICON), any()))
-                .thenReturn(bundle);
-        when(mIContentProvider.call(anyString(),
-                eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
-                any())).thenReturn(bundle);
-        when(mContentResolver.acquireProvider(anyString())).thenReturn(mIContentProvider);
-        when(mContentResolver.acquireProvider(any(Uri.class))).thenReturn(mIContentProvider);
-
         TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
                 null /* defaultCategory */, outTiles, false /* usePriority */,
                 false /* checkCategory */);
 
         assertThat(outTiles.size()).isEqualTo(1);
-        assertThat(outTiles.get(0).icon.getResId()).isEqualTo(161803);
-        assertThat(outTiles.get(0).summary).isEqualTo("dynamic-summary");
     }
 
     public static ResolveInfo newInfo(boolean systemApp, String category) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2dc9d63..5b4d2fd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2740,7 +2740,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 141;
+            private static final int SETTINGS_VERSION = 142;
 
             private final int mUserId;
 
@@ -3265,6 +3265,26 @@
                     currentVersion = 141;
                 }
 
+                if (currentVersion == 141) {
+                    // Version 141: We added the notion of a default and whether the system set
+                    // the setting. This is used for resetting the internal state and we need
+                    // to make sure this value is updated for the existing settings, otherwise
+                    // we would delete system set settings while they should stay unmodified.
+                    SettingsState globalSettings = getGlobalSettingsLocked();
+                    ensureLegacyDefaultValueAndSystemSetUpdatedLocked(globalSettings);
+                    globalSettings.persistSyncLocked();
+
+                    SettingsState secureSettings = getSecureSettingsLocked(mUserId);
+                    ensureLegacyDefaultValueAndSystemSetUpdatedLocked(secureSettings);
+                    secureSettings.persistSyncLocked();
+
+                    SettingsState systemSettings = getSystemSettingsLocked(mUserId);
+                    ensureLegacyDefaultValueAndSystemSetUpdatedLocked(systemSettings);
+                    systemSettings.persistSyncLocked();
+
+                    currentVersion = 142;
+                }
+
                 if (currentVersion != newVersion) {
                     Slog.wtf("SettingsProvider", "warning: upgrading settings database to version "
                             + newVersion + " left it at "
@@ -3280,5 +3300,22 @@
                 return currentVersion;
             }
         }
+
+        private void ensureLegacyDefaultValueAndSystemSetUpdatedLocked(SettingsState settings) {
+            List<String> names = settings.getSettingNamesLocked();
+            final int nameCount = names.size();
+            for (int i = 0; i < nameCount; i++) {
+                String name = names.get(i);
+                Setting setting = settings.getSettingLocked(name);
+                if (setting.getDefaultValue() == null) {
+                    boolean systemSet = SettingsState.isSystemPackage(getContext(),
+                            setting.getPackageName());
+                    if (systemSet) {
+                        settings.insertSettingLocked(name, setting.getValue(),
+                                setting.getTag(), true, setting.getPackageName());
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/res/drawable/clipboard_empty.xml b/packages/SystemUI/res/drawable/clipboard_empty.xml
index 14a5ad2..33cd58c 100644
--- a/packages/SystemUI/res/drawable/clipboard_empty.xml
+++ b/packages/SystemUI/res/drawable/clipboard_empty.xml
@@ -1,25 +1,24 @@
 <!--
-    Copyright (C) 2016 The Android Open Source Project
+    Copyright (C) 2017 The Android Open Source Project
 
-    Licensed under the Apache License, Version 2.0 (the License);
+    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,
+    distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
+        android:width="18.0dp"
+        android:height="18.0dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M19.0,2.0l-4.18,0.0C14.0,0.84 13.3,0.0 12.0,0.0c-1.3,0.0 -2.0,0.84 -2.82,2.0L5.0,2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,16.0c0.0,1.0 0.9,2.0 2.0,2.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,4.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0zm-7.0,0.0c0.55,0.0 1.0,0.45 1.0,1.0s-0.45,1.0 -1.0,1.0 -1.0,-0.45 -1.0,-1.0 0.45,-1.0 1.0,-1.0zm7.0,18.0L5.0,20.0L5.0,4.0l2.0,0.0l0.0,3.0l10.0,0.0L17.0,4.0l2.0,0.0l0.0,16.0z" />
+        android:pathData="M16.0,1.0L4.0,1.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,14.0l2.0,0.0L4.0,3.0l12.0,0.0L16.0,1.0zm3.0,4.0L8.0,5.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,14.0c0.0,1.0 0.9,2.0 2.0,2.0l11.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,7.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0zm0.0,16.0L8.0,21.0L8.0,7.0l11.0,0.0l0.0,14.0z"/>
 </vector>
-
diff --git a/packages/SystemUI/res/drawable/clipboard_full.xml b/packages/SystemUI/res/drawable/clipboard_full.xml
index 2d46870..cb056da 100644
--- a/packages/SystemUI/res/drawable/clipboard_full.xml
+++ b/packages/SystemUI/res/drawable/clipboard_full.xml
@@ -1,25 +1,24 @@
 <!--
-    Copyright (C) 2016 The Android Open Source Project
+    Copyright (C) 2017 The Android Open Source Project
 
-    Licensed under the Apache License, Version 2.0 (the License);
+    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,
+    distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24.0dp"
-    android:height="24.0dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+        android:width="18.0dp"
+        android:height="18.0dp"
+        android:viewportWidth="18.0"
+        android:viewportHeight="18.0">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M19.0,2.0l-4.18,0.0C14.0,0.84 13.3,0.0 12.0,0.0c-1.3,0.0 -2.0,0.84 -2.82,2.0L5.0,2.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,16.0c0.0,1.0 0.9,2.0 2.0,2.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,4.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0zm-7.0,0.0c0.55,0.0 1.0,0.45 1.0,1.0s-0.45,1.0 -1.0,1.0 -1.0,-0.45 -1.0,-1.0 0.45,-1.0 1.0,-1.0zm7.0,18.0L5.0,20.0L5.0,4.0l2.0,0.0l0.0,3.0l10.0,0.0L17.0,4.0l2.0,0.0l0.0,16.0z M 6,8 l 12,0 l 0,11 l -12,0 l 0,-11z" />
+        android:pathData="M12.0,0.75L3.0,0.75c-0.82,0.0 -1.5,0.67 -1.5,1.5l0.0,10.5L3.0,12.75L3.0,2.25l9.0,0.0L12.0,0.75zM14.25,3.75L6.0,3.75c-0.83,0.0 -1.5,0.67 -1.5,1.5l0.0,10.5c0.0,0.83 0.67,1.5 1.5,1.5l8.25,0.0c0.83,0.0 1.5,-0.67 1.5,-1.5L15.75,5.25C15.75,4.42 15.08,3.75 14.25,3.75zM7.25,6.5l5.74,0.0l0.0,1.75L7.25,8.25L7.25,6.5zM11.76,14.5l-4.5,0.0l0.0,-1.75l4.5,0.0L11.76,14.5zM13.0,11.38L7.26,11.38L7.26,9.62L13.0,9.62L13.0,11.38z"
+        android:fillColor="#FFFFFF"/>
 </vector>
-
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
index 54c52bf..782fbe4 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_4g_plus.xml
@@ -16,8 +16,8 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="32.0dp"
         android:height="32.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportWidth="19.0"
+        android:viewportHeight="19.0">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M4.6,7.8l0.7,0.0l0.0,1.3L4.6,9.1L4.6,11.0L3.0,11.0L3.0,9.2L0.1,9.2L0.0,8.2l3.0,-5.7l1.7,0.0L4.6,7.8L4.6,7.8zM1.7,7.8L3.0,7.8l0.0,-3.0L2.9,5.0L1.7,7.8z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
index dd0f271..4232126 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_e.xml
@@ -16,9 +16,12 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="32dp"
         android:height="32dp"
-        android:viewportWidth="5.0"
-        android:viewportHeight="5.0">
+        android:viewportWidth="13.0"
+        android:viewportHeight="13.0">
+  <group
+    android:translateX="3.5" >
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M4.400000,7.300000L1.700000,7.300000l0.000000,2.400000l3.300000,0.000000L5.000000,11.000000L0.000000,11.000000L0.000000,2.500000l4.900000,0.000000l0.000000,1.300000L1.700000,3.800000l0.000000,2.100000l2.800000,0.000000L4.500000,7.300000z"/>
+      android:fillColor="#FFFFFFFF"
+      android:pathData="M4.400000,7.300000L1.700000,7.300000l0.000000,2.400000l3.300000,0.000000L5.000000,11.000000L0.000000,11.000000L0.000000,2.500000l4.900000,0.000000l0.000000,1.300000L1.700000,3.800000l0.000000,2.100000l2.800000,0.000000L4.500000,7.300000z"/>
+  </group>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
index 3b47c0d..0c512d7 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_g.xml
@@ -16,9 +16,11 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="32dp"
         android:height="32dp"
-        android:viewportWidth="7.0"
-        android:viewportHeight="7.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M6.500000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S0.700000,9.000000 0.700000,7.900000L0.700000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000s1.200000,-0.800000 2.100000,-0.800000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000L4.700000,5.200000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S4.000000,3.700000 3.600000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S2.300000,5.000000 2.300000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L4.700000,7.800000L3.500000,7.800000L3.500000,6.600000l2.900000,0.000000L6.400000,9.900000z"/>
+        android:viewportWidth="13.0"
+        android:viewportHeight="13.0">
+    <group android:translateX="3.5" >
+      <path
+          android:fillColor="#FFFFFFFF"
+          android:pathData="M6.500000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S0.700000,9.000000 0.700000,7.900000L0.700000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000s1.200000,-0.800000 2.100000,-0.800000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000L4.700000,5.200000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S4.000000,3.700000 3.600000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S2.300000,5.000000 2.300000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L4.700000,7.800000L3.500000,7.800000L3.500000,6.600000l2.900000,0.000000L6.400000,9.900000z"/>
+    </group>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
index d694e61..b9572b2 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_h.xml
@@ -14,11 +14,14 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="32.0dp"
+        android:width="32dp"
         android:height="32dp"
-        android:viewportWidth="6.0"
-        android:viewportHeight="6.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M6.000000,11.000000L4.400000,11.000000L4.400000,7.500000L1.700000,7.500000L1.700000,11.000000L0.000000,11.000000L0.000000,2.500000l1.700000,0.000000l0.000000,3.700000l2.700000,0.000000L4.400000,2.500000L6.000000,2.500000L6.000000,11.000000z"/>
+        android:viewportWidth="13.0"
+        android:viewportHeight="13.0">
+      <group
+        android:translateX="3.5" >
+        <path
+            android:fillColor="#FFFFFFFF"
+            android:pathData="M6.000000,11.000000L4.400000,11.000000L4.400000,7.500000L1.700000,7.500000L1.700000,11.000000L0.000000,11.000000L0.000000,2.500000l1.700000,0.000000l0.000000,3.700000l2.700000,0.000000L4.400000,2.500000L6.000000,2.500000L6.000000,11.000000z"/>
+      </group>
 </vector>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
index aa4eaa7..21f9a79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
@@ -26,7 +26,9 @@
 
     interface DeviceProvisionedListener {
         default void onDeviceProvisionedChanged() { }
-        default void onUserSwitched() { }
+        default void onUserSwitched() {
+            onUserSetupChanged();
+        }
         default void onUserSetupChanged() { }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
index 528fefe..cfaca0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
@@ -66,6 +66,8 @@
         if (mListeners.size() == 1) {
             startListening(getCurrentUser());
         }
+        listener.onUserSetupChanged();
+        listener.onDeviceProvisionedChanged();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 03c46e8..91acf04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -17,8 +17,11 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.database.ContentObserver;
 import android.net.NetworkCapabilities;
+import android.os.Handler;
 import android.os.Looper;
+import android.provider.Settings.Global;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -49,6 +52,7 @@
     private final SubscriptionDefaults mDefaults;
     private final String mNetworkNameDefault;
     private final String mNetworkNameSeparator;
+    private final ContentObserver mObserver;
     @VisibleForTesting
     final PhoneStateListener mPhoneStateListener;
     // Save entire info for logging, we only use the id.
@@ -97,6 +101,12 @@
         mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
         // Get initial data sim state.
         updateDataSim();
+        mObserver = new ContentObserver(new Handler(receiverLooper)) {
+            @Override
+            public void onChange(boolean selfChange) {
+                updateTelephony();
+            }
+        };
     }
 
     public void setConfiguration(Config config) {
@@ -144,6 +154,11 @@
                         | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
                         | PhoneStateListener.LISTEN_DATA_ACTIVITY
                         | PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
+        mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),
+                true, mObserver);
+        mContext.getContentResolver().registerContentObserver(Global.getUriFor(
+                Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
+                true, mObserver);
     }
 
     /**
@@ -151,6 +166,7 @@
      */
     public void unregisterListener() {
         mPhone.listen(mPhoneStateListener, 0);
+        mContext.getContentResolver().unregisterContentObserver(mObserver);
     }
 
     /**
@@ -242,11 +258,11 @@
             description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
         }
         boolean activityIn = mCurrentState.dataConnected
-                        && !mCurrentState.carrierNetworkChangeMode
-                        && mCurrentState.activityIn;
+                && !mCurrentState.carrierNetworkChangeMode
+                && mCurrentState.activityIn;
         boolean activityOut = mCurrentState.dataConnected
-                        && !mCurrentState.carrierNetworkChangeMode
-                        && mCurrentState.activityOut;
+                && !mCurrentState.carrierNetworkChangeMode
+                && mCurrentState.activityOut;
         showDataIcon &= mCurrentState.isDefault || dataDisabled;
         int typeIcon = showDataIcon ? icons.mDataType : 0;
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
@@ -294,7 +310,7 @@
             final int iconMode = mServiceState.getCdmaEriIconMode();
             return mServiceState.getCdmaEriIconIndex() != EriInfo.ROAMING_INDICATOR_OFF
                     && (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
-                        || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
+                    || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH);
         } else {
             return mServiceState != null && mServiceState.getRoaming();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index eb47a3c..5657560 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -43,7 +43,6 @@
 
     void addEmergencyListener(EmergencyListener listener);
     void removeEmergencyListener(EmergencyListener listener);
-    void setUserSetupComplete(boolean userSetup);
     boolean hasEmergencyCryptKeeperText();
     boolean isRadioOn();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index d7c919d..a85426c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -551,17 +551,11 @@
         updateAirplaneMode(true /* force */);
     }
 
-    public void setUserSetupComplete(final boolean userSetup) {
-        mReceiverHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                handleSetUserSetupComplete(userSetup);
-            }
-        });
+    private void setUserSetupComplete(final boolean userSetup) {
+        mReceiverHandler.post(() -> handleSetUserSetupComplete(userSetup));
     }
 
-    @VisibleForTesting
-    void handleSetUserSetupComplete(boolean userSetup) {
+    private void handleSetUserSetupComplete(boolean userSetup) {
         mUserSetup = userSetup;
         for (MobileSignalController controller : mMobileSignalControllers.values()) {
             controller.setUserSetupComplete(mUserSetup);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 0e5f513..19b4b17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -31,6 +31,7 @@
 import android.util.Log;
 import com.android.internal.telephony.cdma.EriInfo;
 import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
@@ -43,6 +44,8 @@
 import org.junit.runner.Description;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -56,6 +59,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -82,6 +86,8 @@
     protected CallbackHandler mCallbackHandler;
     protected SubscriptionDefaults mMockSubDefaults;
     protected NetworkScoreManager mMockNetworkScoreManager;
+    protected DeviceProvisionedController mMockProvisionController;
+    protected DeviceProvisionedListener mUserCallback;
 
     protected int mSubId;
 
@@ -120,11 +126,21 @@
         mConfig = new Config();
         mConfig.hspaDataDistinguishable = true;
         mCallbackHandler = mock(CallbackHandler.class);
+
+        mMockProvisionController = mock(DeviceProvisionedController.class);
+        when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(true);
+        doAnswer(invocation -> {
+            mUserCallback = (DeviceProvisionedListener) invocation.getArguments()[0];
+            mUserCallback.onUserSetupChanged();
+            mUserCallback.onDeviceProvisionedChanged();
+            return null;
+        }).when(mMockProvisionController).addCallback(any());
+
         mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
                 mMockTm, mMockWm, mMockSm,
                 mConfig, Looper.getMainLooper(), mCallbackHandler,
                 mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
-                mMockSubDefaults, mock(DeviceProvisionedController.class));
+                mMockSubDefaults, mMockProvisionController);
         setupNetworkController();
 
         // Trigger blank callbacks to always get the current state (some tests don't trigger
@@ -139,7 +155,6 @@
         when(mMockTm.getDataEnabled(mSubId)).thenReturn(true);
         setDefaultSubId(mSubId);
         setSubscriptions(mSubId);
-        mNetworkController.handleSetUserSetupComplete(true);
         mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
         mPhoneStateListener = mMobileSignalController.mPhoneStateListener;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index d7f961c..ba20999 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -1,16 +1,19 @@
 package com.android.systemui.statusbar.policy;
 
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.net.NetworkCapabilities;
 import android.os.Looper;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
+
 import com.android.settingslib.net.DataUsageController;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -106,7 +109,7 @@
     @Test
     public void testDataDisabledIcon() {
         setupNetworkController();
-        Mockito.when(mMockTm.getDataEnabled(mSubId)).thenReturn(false);
+        when(mMockTm.getDataEnabled(mSubId)).thenReturn(false);
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
         setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
@@ -118,11 +121,12 @@
     @Test
     public void testDataDisabledIcon_UserNotSetup() {
         setupNetworkController();
-        Mockito.when(mMockTm.getDataEnabled(mSubId)).thenReturn(false);
+        when(mMockTm.getDataEnabled(mSubId)).thenReturn(false);
         setupDefaultSignal();
         updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
         setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
-        mNetworkController.handleSetUserSetupComplete(false);
+        when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(false);
+        mUserCallback.onUserSetupChanged();
 
         // Don't show the X until the device is setup.
         verifyDataIndicators(0, 0);
@@ -154,7 +158,7 @@
         verifyDataIndicators(TelephonyIcons.DATA_LTE[1][0 /* No direction */],
                 TelephonyIcons.QS_DATA_LTE);
 
-        Mockito.when(mServiceState.getDataNetworkType())
+        when(mServiceState.getDataNetworkType())
                 .thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
         updateServiceState();
         verifyDataIndicators(TelephonyIcons.DATA_H[1][0 /* No direction */],
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index 5497686..47ed5ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -47,11 +47,6 @@
     }
 
     @Override
-    public void setUserSetupComplete(boolean userSetup) {
-
-    }
-
-    @Override
     public boolean hasEmergencyCryptKeeperText() {
         return false;
     }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c81bd1b..c7f518c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3458,6 +3458,14 @@
     // OPEN: Settings > Apps > Default Apps > Assist >  Default voice input
     DEFAULT_VOICE_INPUT_PICKER = 844;
 
+    // OPEN: Settings > Storage > [Profile]
+    SETTINGS_STORAGE_PROFILE = 845;
+
+    // OPEN: Settings > Security & screen lock -> Encryption & crendentials
+    // CATEGORY: SETTINGS
+    // OS: O
+    ENCRYPTION_AND_CREDENTIAL = 846;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
deleted file mode 100644
index 64c6abd..0000000
--- a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.autofill;
-
-import static com.android.server.autofill.Helper.DEBUG;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
-
-/**
- * A window above the application that is smartly anchored to a rectangular region.
- */
-final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchListener {
-    private static final String TAG = "AutoFill";
-
-    private static final int NULL_HEIGHT = -1;
-
-    private final WindowManager mWm;
-    private final IBinder mAppToken;
-    private final View mContentView;
-
-    private final View mWindowSizeListenerView;
-    private final int mMinMargin;
-
-    private int mLastHeight = NULL_HEIGHT;
-    @Nullable
-    private Rect mLastBounds;
-    @Nullable
-    private Rect mLastDisplayBounds;
-
-    /**
-     * Constructor.
-     *
-     * @param wm window manager that draws the content on a window
-     * @param appToken token to pass to window manager
-     * @param contentView content of the window
-     */
-    AnchoredWindow(WindowManager wm, IBinder appToken, View contentView) {
-        mWm = wm;
-        mAppToken = appToken;
-        mContentView = contentView;
-
-        mContentView.addOnLayoutChangeListener(this);
-
-        Context context = contentView.getContext();
-
-        mWindowSizeListenerView = new FrameLayout(context);
-        mWindowSizeListenerView.addOnLayoutChangeListener(this);
-
-        mMinMargin = context.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.autofill_fill_min_margin);
-    }
-
-    /**
-     * Shows the window.
-     *
-     * @param bounds the region the window should be anchored to
-     */
-    void show(Rect bounds) {
-        if (DEBUG) Slog.d(TAG, "show bounds=" + bounds);
-
-        if (!mWindowSizeListenerView.isAttachedToWindow()) {
-            if (DEBUG) Slog.d(TAG, "adding mWindowSizeListenerView");
-            LayoutParams params = createWindowLayoutParams(
-                    mAppToken,
-                    LayoutParams.FLAG_NOT_TOUCHABLE); // not touchable
-            params.gravity = Gravity.LEFT | Gravity.TOP;
-            params.x = 0;
-            params.y = 0;
-            params.width = LayoutParams.MATCH_PARENT;
-            params.height = LayoutParams.MATCH_PARENT;
-            mWm.addView(mWindowSizeListenerView, params);
-        }
-
-        updateBounds(bounds);
-    }
-
-    /**
-     * Hides the window.
-     */
-    void hide() {
-        if (DEBUG) Slog.d(TAG, "hide");
-
-        mLastHeight = NULL_HEIGHT;
-        mLastBounds = null;
-        mLastDisplayBounds = null;
-
-        if (mWindowSizeListenerView.isAttachedToWindow()) {
-            if (DEBUG) Slog.d(TAG, "removing mWindowSizeListenerView");
-            mWm.removeView(mWindowSizeListenerView);
-        }
-
-        if (mContentView.isAttachedToWindow()) {
-            if (DEBUG) Slog.d(TAG, "removing mContentView");
-            mContentView.setOnTouchListener(null);
-            mWm.removeView(mContentView);
-        }
-    }
-
-    @Override
-    public void onLayoutChange(View view, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        if (view == mWindowSizeListenerView) {
-            if (DEBUG) Slog.d(TAG, "onLayoutChange() for mWindowSizeListenerView");
-            // mWindowSizeListenerView layout changed, get the size of the display bounds and updateLocked
-            // the window.
-            final Rect displayBounds = new Rect();
-            view.getBoundsOnScreen(displayBounds);
-            updateDisplayBounds(displayBounds);
-        } else if (view == mContentView) {
-            // mContentView layout changed, updateLocked the window in case its height changed.
-            if (DEBUG) Slog.d(TAG, "onLayoutChange() for mContentView");
-            updateHeight();
-        }
-    }
-
-    // When the window is touched outside, hide the window.
-    @Override
-    public boolean onTouch(View view, MotionEvent event) {
-        if (view == mContentView && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-            hide();
-            return true;
-        }
-        return false;
-    }
-
-    private boolean updateHeight() {
-        final Rect displayBounds = mLastDisplayBounds;
-        if (displayBounds == null) {
-            return false;
-        }
-
-        mContentView.measure(
-                MeasureSpec.makeMeasureSpec(displayBounds.width(), MeasureSpec.AT_MOST),
-                MeasureSpec.makeMeasureSpec(displayBounds.height(), MeasureSpec.AT_MOST));
-        int height = mContentView.getMeasuredHeight();
-        if (height != mLastHeight) {
-            if (DEBUG) Slog.d(TAG, "updateLocked height=" + height);
-            mLastHeight = height;
-            update(height, mLastBounds, displayBounds);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private void updateBounds(Rect bounds) {
-        if (!bounds.equals(mLastBounds)) {
-            if (DEBUG) Slog.d(TAG, "updateLocked bounds=" + bounds);
-            mLastBounds = bounds;
-
-            update(mLastHeight, bounds, mLastDisplayBounds);
-        }
-    }
-
-    private void updateDisplayBounds(Rect displayBounds) {
-        if (!displayBounds.equals(mLastDisplayBounds)) {
-            if (DEBUG) Slog.d(TAG, "updateLocked displayBounds=" + displayBounds);
-            mLastDisplayBounds = displayBounds;
-
-            if (!updateHeight()) {
-                update(mLastHeight, mLastBounds, displayBounds);
-            }
-        }
-    }
-
-    // Updates the window if height, bounds, and displayBounds are not null.
-    // Caller should ensure that something changed before calling.
-    private void update(int height, @Nullable Rect bounds, @Nullable Rect displayBounds) {
-        if (height == NULL_HEIGHT || bounds == null || displayBounds == null) {
-            return;
-        }
-
-        if (DEBUG) Slog.d(TAG, "updateLocked height=" + height + ", bounds=" + bounds
-                + ", displayBounds=" + displayBounds);
-
-        final LayoutParams params = createWindowLayoutParams(mAppToken,
-                LayoutParams.FLAG_NOT_TOUCH_MODAL // outside touches go to windows behind us
-                | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); // outside touches trigger MotionEvent
-        params.setTitle("AutoFill Fill"); // used for debugging
-        updatePosition(params, height, mMinMargin, bounds, displayBounds);
-        if (!mContentView.isAttachedToWindow()) {
-            if (DEBUG) Slog.d(TAG, "adding mContentView");
-            mWm.addView(mContentView, params);
-            mContentView.setOnTouchListener(this);
-        } else {
-            if (DEBUG) Slog.d(TAG, "updating mContentView");
-            mWm.updateViewLayout(mContentView, params);
-        }
-    }
-
-    /**
-     * Updates the position of the window by altering the {@link LayoutParams}.
-     *
-     * <p>The window can be anchored either above or below the bounds. Anchoring the window below
-     * the bounds is preferred, if it fits. Otherwise, anchor the window on the side with more
-     * space.
-     *
-     * @param params the params to updateLocked
-     * @param height the requested height of the window
-     * @param minMargin the minimum margin between the window and the display bounds
-     * @param bounds the region the window should be anchored to
-     * @param displayBounds the region in which the window may be displayed
-     */
-    private static void updatePosition(
-            LayoutParams params,
-            int height,
-            int minMargin,
-            Rect bounds,
-            Rect displayBounds) {
-        boolean below;
-        int verticalSpace;
-        final int verticalSpaceBelow = displayBounds.bottom - bounds.bottom - minMargin;
-        if (height <= verticalSpaceBelow) {
-            // Fits below bounds.
-            below = true;
-            verticalSpace = height;
-        } else {
-            final int verticalSpaceAbove = bounds.top - displayBounds.top - minMargin;
-            if (height <= verticalSpaceAbove) {
-                // Fits above bounds.
-                below = false;
-                verticalSpace = height;
-            } else {
-                // Pick above/below based on which has the most space.
-                if (verticalSpaceBelow >= verticalSpaceAbove) {
-                    below = true;
-                    verticalSpace = verticalSpaceBelow;
-                } else {
-                    below = false;
-                    verticalSpace = verticalSpaceAbove;
-                }
-            }
-        }
-
-        int gravity;
-        int y;
-        if (below) {
-            if (DEBUG) Slog.d(TAG, "anchorBelow");
-            gravity = Gravity.TOP | Gravity.LEFT;
-            y = bounds.bottom - displayBounds.top;
-        } else {
-            if (DEBUG) Slog.d(TAG, "anchorAbove");
-            gravity = Gravity.BOTTOM | Gravity.LEFT;
-            y = displayBounds.bottom - bounds.top;
-        }
-
-        final int x = bounds.left - displayBounds.left;
-
-        params.gravity = gravity;
-        params.x = x;
-        params.y = y;
-        params.width = bounds.width();
-        params.height = verticalSpace;
-    }
-
-    private static LayoutParams createWindowLayoutParams(IBinder appToken, int flags) {
-        final LayoutParams params = new LayoutParams();
-        params.token = appToken;
-        params.type = LayoutParams.TYPE_PHONE;
-        params.flags =
-                flags
-                | LayoutParams.FLAG_NOT_FOCUSABLE // don't receive input events
-                | LayoutParams.FLAG_ALT_FOCUSABLE_IM; // resize for soft input
-        params.format = PixelFormat.TRANSLUCENT;
-        return params;
-    }
-}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 3257812..520b43a 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -57,6 +57,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.autofill.ui.AutoFillUI;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -271,8 +272,8 @@
         }
 
         @Override
-        public void startSession(IBinder activityToken, IBinder appCallback, AutoFillId autoFillId,
-                Rect bounds, AutoFillValue value, int userId) {
+        public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
+                AutoFillId autoFillId, Rect bounds, AutoFillValue value, int userId) {
             // TODO(b/33197203): make sure it's called by resumed / focused activity
 
             if (VERBOSE) {
@@ -282,7 +283,8 @@
 
             synchronized (mLock) {
                 final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
-                service.startSessionLocked(activityToken, appCallback, autoFillId, bounds, value);
+                service.startSessionLocked(activityToken, windowToken, appCallback,
+                        autoFillId, bounds, value);
             }
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index ec3d356..f399f59 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -70,6 +70,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.IResultReceiver;
+import com.android.server.autofill.ui.AutoFillUI;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -272,8 +273,8 @@
         }
     }
 
-    void startSessionLocked(IBinder activityToken, IBinder appCallbackToken, AutoFillId autoFillId,
-            Rect bounds, AutoFillValue value) {
+    void startSessionLocked(IBinder activityToken, IBinder windowToken, IBinder appCallbackToken,
+            AutoFillId autoFillId,  Rect bounds, AutoFillValue value) {
         if (!hasService()) {
             return;
         }
@@ -290,7 +291,8 @@
             return;
         }
 
-        final Session newSession = createSessionByTokenLocked(activityToken, appCallbackToken);
+        final Session newSession = createSessionByTokenLocked(activityToken,
+                windowToken, appCallbackToken);
         newSession.updateLocked(autoFillId, bounds, value, FLAG_START_SESSION);
     }
 
@@ -308,8 +310,10 @@
         session.showSaveLocked();
     }
 
-    private Session createSessionByTokenLocked(IBinder activityToken, IBinder appCallbackToken) {
-        final Session newSession = new Session(mContext, activityToken, appCallbackToken);
+    private Session createSessionByTokenLocked(IBinder activityToken, IBinder windowToken,
+            IBinder appCallbackToken) {
+        final Session newSession = new Session(mContext, activityToken,
+                windowToken, appCallbackToken);
         mSessions.put(activityToken, newSession);
 
         /*
@@ -556,6 +560,7 @@
     final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
             AutoFillUI.AutoFillUiCallback {
         private final IBinder mActivityToken;
+        private final IBinder mWindowToken;
 
         @GuardedBy("mLock")
         private final Map<AutoFillId, ViewState> mViewStates = new ArrayMap<>();
@@ -587,10 +592,12 @@
         @GuardedBy("mLock")
         private AssistStructure mStructure;
 
-        private Session(Context context, IBinder activityToken, IBinder client) {
+        private Session(Context context, IBinder activityToken, IBinder windowToken,
+                IBinder client) {
             mRemoteFillService = new RemoteFillService(context,
                     mInfo.getServiceInfo().getComponentName(), mUserId, this);
             mActivityToken = activityToken;
+            mWindowToken = windowToken;
 
             mClient = IAutoFillManagerClient.Stub.asInterface(client);
             try {
@@ -662,7 +669,9 @@
         // AutoFillUiCallback
         @Override
         public void fill(Dataset dataset) {
-            autoFill(dataset);
+            mHandlerCaller.getHandler().post(() -> {
+                autoFill(dataset);
+            });
         }
 
         // AutoFillUiCallback
@@ -726,7 +735,8 @@
                         Slog.d(TAG, "finishSessionLocked(): found a change on " + id + ": "
                                 + state.mAutoFillValue);
                     }
-                    getUiForShowing().showSaveUi();
+                    getUiForShowing().showSaveUi(mInfo.getServiceInfo()
+                            .loadLabel(mContext.getPackageManager()));
                     return;
                 }
             }
@@ -810,8 +820,9 @@
                     viewState.mAutoFillValue = value;
 
                     // Update the chooser UI
-                    mUi.updateFillUi(value.coerceToString());
+                    getUiForShowing().filterFillUi(value.coerceToString());
                 }
+
                 return;
             }
 
@@ -955,7 +966,7 @@
 
         private AutoFillUI getUiForShowing() {
             synchronized (mLock) {
-                mUi.setCallbackLocked(this, mActivityToken);
+                mUi.setCallback(this, mWindowToken);
                 return mUi;
             }
         }
@@ -995,7 +1006,7 @@
 
         private void destroyLocked() {
             mRemoteFillService.destroy();
-            mUi.setCallbackLocked(null, null);
+            mUi.setCallback(null, null);
         }
 
         private void removeSelf() {
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
deleted file mode 100644
index ee485df..0000000
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.autofill;
-
-import static com.android.server.autofill.Helper.DEBUG;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.IntentSender;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.IBinder;
-import android.service.autofill.Dataset;
-import android.service.autofill.FillResponse;
-import android.text.format.DateUtils;
-import android.util.Slog;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.view.autofill.AutoFillId;
-import android.widget.Toast;
-
-import com.android.server.UiThread;
-
-import java.io.PrintWriter;
-
-/**
- * Handles all auto-fill related UI tasks.
- */
-// TODO(b/33197203): document exactly what once the auto-fill bar is implemented
-final class AutoFillUI {
-    private static final String TAG = "AutoFillUI";
-
-    private static final long SNACK_BAR_LIFETIME_MS = 5 * DateUtils.SECOND_IN_MILLIS;
-
-    private static final int MSG_HIDE_SNACK_BAR = 1;
-
-    private final Handler mHandler = UiThread.getHandler();
-
-    private final Context mContext;
-
-    private final WindowManager mWm;
-
-    private AnchoredWindow mFillWindow;
-
-    private DatasetPicker mDatasetPicker;
-
-    private AutoFillUiCallback mCallback;
-
-    private IBinder mActivityToken;
-
-    /**
-     * Custom snackbar UI used for saving autofill or other informational messages.
-     */
-    private View mSnackbar;
-
-    AutoFillUI(Context context) {
-        mContext = context;
-        mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-    }
-
-    void setCallbackLocked(AutoFillUiCallback callback, IBinder activityToken) {
-        mHandler.post(() -> {
-            if (callback != mCallback && activityToken != mActivityToken) {
-                hideAllUiThread();
-                mCallback = callback;
-                mActivityToken = activityToken;
-            }
-        });
-    }
-
-    /**
-     * Displays an error message to the user.
-     */
-    void showError(CharSequence message) {
-        // TODO(b/33197203): proper implementation
-        UiThread.getHandler().post(() -> {
-            if (!hasCallback()) {
-                return;
-            }
-            hideAllUiThread();
-            Toast.makeText(mContext, "AutoFill error: " + message, Toast.LENGTH_LONG).show();
-        });
-    }
-
-    /**
-     * Hides the fill UI.
-     */
-    void hideFillUi() {
-        mHandler.post(() -> hideFillUiUiThread());
-    }
-
-    @android.annotation.UiThread
-    private void hideFillUiUiThread() {
-        if (mFillWindow != null) {
-            if (DEBUG) Slog.d(TAG, "hideFillUiUiThread(): hide" + mFillWindow);
-            mFillWindow.hide();
-        }
-        mFillWindow = null;
-        mDatasetPicker = null;
-    }
-
-    void updateFillUi(@Nullable String filterText) {
-        mHandler.post(() -> {
-            if (!hasCallback()) {
-                return;
-            }
-            hideSnackbarUiThread();
-            if (mDatasetPicker != null) {
-                mDatasetPicker.update(filterText);
-            }
-        });
-    }
-
-    /**
-     * Shows the fill UI, removing the previous fill UI if the has changed.
-     *
-     * @param focusedId the currently focused field
-     * @param response the current fill response
-     * @param bounds bounds of the view to be filled, used if changed
-     * @param filterText text of the view to be filled, used if changed
-     */
-    void showFillUi(AutoFillId focusedId, @Nullable FillResponse response, Rect bounds,
-            String filterText) {
-        mHandler.post(() -> {
-            if (!hasCallback()) {
-                return;
-            }
-            hideSnackbarUiThread();
-            final View content;
-            if (response.getPresentation() != null) {
-                content = response.getPresentation().apply(mContext, null);
-                content.setOnClickListener((view) -> {
-                    if (mCallback != null) {
-                        mCallback.authenticate(response.getAuthentication());
-                    }
-                    hideFillUiUiThread();
-                });
-            } else {
-                mDatasetPicker = new DatasetPicker(mContext, response.getDatasets(),
-                        focusedId, new DatasetPicker.Listener() {
-                    @Override
-                    public void onDatasetPicked(Dataset dataset) {
-                        if (mCallback != null) {
-                            mCallback.fill(dataset);
-                        }
-                        hideFillUiUiThread();
-                    }
-
-                    @Override
-                    public void onCanceled() {
-                        hideFillUiUiThread();
-                    }
-                });
-                mDatasetPicker.update(filterText);
-                content = mDatasetPicker;
-            }
-
-            mFillWindow = new AnchoredWindow(mWm, mActivityToken, content);
-            mFillWindow.show(bounds);
-        });
-    }
-
-    /**
-     * Shows the UI asking the user to save for auto-fill.
-     */
-    void showSaveUi() {
-        mHandler.post(() -> {
-            if (!hasCallback()) {
-                return;
-            }
-            hideAllUiThread();
-            showSnackbarUiThread(new SavePrompt(mContext,
-                    new SavePrompt.OnSaveListener() {
-                @Override
-                public void onSaveClick() {
-                    hideSnackbarUiThread();
-                    // TODO(b/33197203): add MetricsLogger call
-                    mCallback.save();
-                }
-
-                @Override
-                public void onCancelClick() {
-                    // TODO(b/33197203): add MetricsLogger call
-                    hideSnackbarUiThread();
-                }
-            }));
-        });
-    }
-
-    /**
-     * Hides all UI affordances.
-     */
-    void hideAll() {
-        mHandler.post(() -> hideAllUiThread());
-    }
-
-    @android.annotation.UiThread
-    private void hideAllUiThread() {
-        hideSnackbarUiThread();
-        hideFillUiUiThread();
-    }
-
-    void dump(PrintWriter pw) {
-        pw.println("AufoFill UI");
-        final String prefix = "  ";
-        pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
-        pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
-    }
-
-    //similar to a snackbar, but can be a bit custom since it is more than just text. This will
-    //allow two buttons for saving or not saving the autofill for instance as well.
-    @android.annotation.UiThread
-    private void showSnackbarUiThread(View snackBar) {
-        final LayoutParams params = new LayoutParams();
-        params.type = LayoutParams.TYPE_PHONE; // TODO(b/33197203) use app window token
-        params.flags =
-                LayoutParams.FLAG_NOT_FOCUSABLE // don't receive input events,
-                | LayoutParams.FLAG_ALT_FOCUSABLE_IM // resize for soft input
-                | LayoutParams.FLAG_NOT_TOUCH_MODAL; // outside touches go to windows behind us
-        params.softInputMode =
-                LayoutParams.SOFT_INPUT_ADJUST_PAN; // pan with soft input
-        params.gravity = Gravity.BOTTOM | Gravity.START;
-        params.width = LayoutParams.MATCH_PARENT;
-        params.height = LayoutParams.WRAP_CONTENT;
-
-        mHandler.post(() -> {
-            mSnackbar = snackBar;
-            mWm.addView(mSnackbar, params);
-        });
-
-        if (DEBUG) {
-            Slog.d(TAG, "showSnackbar(): auto dismissing it in " + SNACK_BAR_LIFETIME_MS + " ms");
-        }
-        mHandler.sendMessageDelayed(mHandler
-                        .obtainMessage(MSG_HIDE_SNACK_BAR), SNACK_BAR_LIFETIME_MS);
-    }
-
-    @android.annotation.UiThread
-    private void hideSnackbarUiThread() {
-        mHandler.removeMessages(MSG_HIDE_SNACK_BAR);
-        if (mSnackbar != null) {
-            mWm.removeView(mSnackbar);
-            mSnackbar = null;
-        }
-    }
-
-    private boolean hasCallback() {
-        return mCallback != null;
-    }
-
-    interface AutoFillUiCallback {
-        void authenticate(IntentSender intent);
-        void fill(Dataset dataset);
-        void save();
-    }
-}
diff --git a/services/autofill/java/com/android/server/autofill/DatasetPicker.java b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
deleted file mode 100644
index e25f2ce..0000000
--- a/services/autofill/java/com/android/server/autofill/DatasetPicker.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.autofill;
-
-import android.content.Context;
-import android.service.autofill.Dataset;
-import android.util.ArraySet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.AutoFillValue;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.FrameLayout;
-import android.widget.ListView;
-import android.widget.RemoteViews;
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class manages the dataset selection UI.
- */
-final class DatasetPicker extends FrameLayout implements OnItemClickListener {
-    interface Listener {
-        void onDatasetPicked(Dataset dataset);
-        void onCanceled();
-    }
-
-    private final Listener mListener;
-
-    private final ArrayAdapter<ViewItem> mAdapter;
-
-    DatasetPicker(Context context, ArrayList<Dataset> datasets, AutoFillId filteredViewId,
-            Listener listener) {
-        super(context);
-        mListener = listener;
-
-        final List<ViewItem> items = new ArrayList<>(datasets.size());
-        for (Dataset dataset : datasets) {
-            final int index = dataset.getFieldIds().indexOf(filteredViewId);
-            if (index >= 0) {
-                AutoFillValue value = dataset.getFieldValues().get(index);
-                items.add(new ViewItem(dataset, value.coerceToString()));
-            }
-        }
-
-        mAdapter = new ArrayAdapter<ViewItem>(context, 0, items) {
-            @Override
-            public View getView(int position, View convertView, ViewGroup parent) {
-                RemoteViews presentation = getItem(position).getDataset().getPresentation();
-                return presentation.apply(context, parent);
-            }
-        };
-
-        LayoutInflater inflater = LayoutInflater.from(context);
-        ListView content = (ListView) inflater.inflate(
-                com.android.internal.R.layout.autofill_dataset_picker, this, true)
-                .findViewById(com.android.internal.R.id.list);
-        content.setAdapter(mAdapter);
-        content.setOnItemClickListener(this);
-    }
-
-    public void update(String prefix) {
-        mAdapter.getFilter().filter(prefix, (count) -> {
-            if (count <= 0 && mListener != null) {
-                mListener.onCanceled();
-            }
-        });
-    }
-
-    @Override
-    public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
-        if (mListener != null) {
-            final ViewItem vi = (ViewItem) adapterView.getItemAtPosition(pos);
-            mListener.onDatasetPicked(vi.getDataset());
-        }
-    }
-
-    private static class ViewItem {
-        private final String mValue;
-        private final Dataset mDataset;
-
-        ViewItem(Dataset dataset, String value) {
-            mDataset = dataset;
-            mValue = value;
-        }
-
-        public Dataset getDataset() {
-            return mDataset;
-        }
-
-        @Override
-        public String toString() {
-            // used by ArrayAdapter
-            return mValue;
-        }
-    }
-}
diff --git a/services/autofill/java/com/android/server/autofill/SavePrompt.java b/services/autofill/java/com/android/server/autofill/SavePrompt.java
deleted file mode 100644
index 85c6d7d..0000000
--- a/services/autofill/java/com/android/server/autofill/SavePrompt.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.autofill;
-
-import android.content.Context;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.internal.R;
-
-/**
- * Autofill Save Prompt
- */
-final class SavePrompt extends RelativeLayout {
-    public interface OnSaveListener {
-        void onSaveClick();
-        void onCancelClick();
-    }
-
-    private final TextView mNoButton;
-    private final TextView mYesButton;
-    private final OnSaveListener mListener;
-
-    SavePrompt(Context context, OnSaveListener listener) {
-        super(context);
-        mListener = listener;
-        LayoutInflater inflater = LayoutInflater.from(context);
-        View view = inflater.inflate(R.layout.autofill_save, this);
-
-        mNoButton = (TextView) view.findViewById(R.id.autofill_save_no);
-        mNoButton.setOnClickListener((v) -> {
-            mListener.onCancelClick();
-        });
-
-        mYesButton = (TextView) view.findViewById(R.id.autofill_save_yes);
-        mYesButton.setOnClickListener((v) -> {
-            mListener.onSaveClick();
-        });
-
-    }
-}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
new file mode 100644
index 0000000..cc0baa3
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.IntentSender;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IBinder;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.text.TextUtils;
+import android.view.autofill.AutoFillId;
+import android.widget.Toast;
+
+import com.android.server.UiThread;
+
+import java.io.PrintWriter;
+
+/**
+ * Handles all auto-fill related UI tasks. The UI has two components:
+ * fill UI that shows a popup style window anchored at the focused
+ * input field for choosing a dataset to fill or trigger the response
+ * authentication flow; save UI that shows a toast style window for
+ * managing saving of user edits.
+ */
+public final class AutoFillUI {
+    private final Handler mHandler = UiThread.getHandler();
+    private final @NonNull Context mContext;
+
+    private @Nullable FillUi mFillUi;
+    private @Nullable SaveUi mSaveUi;
+
+    private @Nullable AutoFillUiCallback mCallback;
+    private @Nullable IBinder mWindowToken;
+
+    public interface AutoFillUiCallback {
+        void authenticate(@NonNull IntentSender intent);
+        void fill(@NonNull Dataset dataset);
+        void save();
+    }
+
+    public AutoFillUI(@NonNull Context context) {
+        mContext = context;
+    }
+
+    public void setCallback(@Nullable AutoFillUiCallback callback,
+            @Nullable IBinder windowToken) {
+        mHandler.post(() -> {
+            if (mCallback != callback || mWindowToken != windowToken) {
+                hideAllUiThread();
+                mCallback = callback;
+                mWindowToken = windowToken;
+            }
+        });
+    }
+
+    /**
+     * Displays an error message to the user.
+     */
+    public void showError(@Nullable CharSequence message) {
+        mHandler.post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
+            hideAllUiThread();
+            if (!TextUtils.isEmpty(message)) {
+                Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+            }
+        });
+    }
+
+    /**
+     * Hides the fill UI.
+     */
+    public void hideFillUi() {
+        mHandler.post(this::hideFillUiUiThread);
+    }
+
+    /**
+     * Filters the options in the fill UI.
+     *
+     * @param filterText The filter prefix.
+     */
+    public void filterFillUi(@Nullable String filterText) {
+        mHandler.post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
+            hideSaveUiUiThread();
+            if (mFillUi != null) {
+                mFillUi.filter(filterText);
+            }
+        });
+    }
+
+    /**
+     * Updates the position of the fill UI.
+     *
+     * @param anchoredBounds The bounds of the anchor view.
+     */
+    public void updateFillUi(@NonNull Rect anchoredBounds) {
+        mHandler.post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
+            hideSaveUiUiThread();
+            if (mFillUi != null) {
+                mFillUi.update(anchoredBounds);
+            }
+        });
+    }
+
+    /**
+     * Shows the fill UI, removing the previous fill UI if the has changed.
+     *
+     * @param focusedId the currently focused field
+     * @param response the current fill response
+     * @param anchorBounds bounds of the focused view
+     * @param filterText text of the view to be filled
+     */
+    public void showFillUi(@NonNull AutoFillId focusedId, @NonNull FillResponse response,
+            @NonNull Rect anchorBounds, @Nullable String filterText) {
+        mHandler.post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
+            hideAllUiThread();
+            mFillUi = new FillUi(mContext, response, focusedId,
+                    mWindowToken, anchorBounds, filterText, new FillUi.Callback() {
+                @Override
+                public void onResponsePicked(FillResponse response) {
+                    hideFillUiUiThread();
+                    if (mCallback != null) {
+                        mCallback.authenticate(response.getAuthentication());
+                    }
+                }
+
+                @Override
+                public void onDatasetPicked(Dataset dataset) {
+                    hideFillUiUiThread();
+                    if (mCallback != null) {
+                        mCallback.fill(dataset);
+                    }
+                    // TODO(b/33197203): add MetricsLogger call
+                }
+
+                @Override
+                public void onCanceled() {
+                    hideFillUiUiThread();
+                    // TODO(b/33197203): add MetricsLogger call
+                }
+            });
+        });
+    }
+
+    /**
+     * Shows the UI asking the user to save for auto-fill.
+     */
+    public void showSaveUi(@NonNull CharSequence providerLabel) {
+        mHandler.post(() -> {
+            if (!hasCallback()) {
+                return;
+            }
+            hideAllUiThread();
+            mSaveUi = new SaveUi(mContext, providerLabel,
+                    new SaveUi.OnSaveListener() {
+                @Override
+                public void onSave() {
+                    hideSaveUiUiThread();
+                    if (mCallback != null) {
+                        mCallback.save();
+                    }
+                    // TODO(b/33197203): add MetricsLogger call
+                }
+
+                @Override
+                public void onCancel() {
+                    // TODO(b/33197203): add MetricsLogger call
+                    hideSaveUiUiThread();
+                }
+            });
+        });
+    }
+
+    /**
+     * Hides all UI affordances.
+     */
+    public void hideAll() {
+        mHandler.post(this::hideAllUiThread);
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println("AufoFill UI");
+        final String prefix = "  ";
+        pw.print(prefix); pw.print("showsFillUi: "); pw.println(mFillUi != null);
+        pw.print(prefix); pw.print("showsSaveUi: "); pw.println(mSaveUi != null);
+    }
+
+    @android.annotation.UiThread
+    private void hideFillUiUiThread() {
+        if (mFillUi != null) {
+            mFillUi.destroy();
+            mFillUi = null;
+        }
+    }
+
+    @android.annotation.UiThread
+    private void hideSaveUiUiThread() {
+        if (mSaveUi != null) {
+            mSaveUi.destroy();
+            mSaveUi = null;
+        }
+    }
+
+    @android.annotation.UiThread
+    private void hideAllUiThread() {
+        hideFillUiUiThread();
+        hideSaveUiUiThread();
+    }
+
+    private boolean hasCallback() {
+        return mCallback != null;
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
new file mode 100644
index 0000000..0d5fbbe
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import com.android.internal.R;
+import com.android.internal.R;
+import libcore.util.Objects;
+
+import java.util.ArrayList;
+
+final class FillUi {
+    private static final String TAG = "FillUi";
+
+    interface Callback {
+        void onResponsePicked(@NonNull FillResponse response);
+        void onDatasetPicked(@NonNull Dataset dataset);
+        void onCanceled();
+    }
+
+    private final Rect mAnchorBounds = new Rect();
+
+    private final @NonNull AnchoredWindow mWindow;
+
+    private final @NonNull Callback mCallback;
+
+    private final @Nullable ArrayAdapter<ViewItem> mAdapter;
+
+    private @Nullable String mFilterText;
+
+    private int mContentWidth;
+    private int mContentHeight;
+
+    private boolean mDestroyed;
+
+    FillUi(@NonNull Context context, @NonNull FillResponse response,
+            @NonNull AutoFillId focusedViewId, @NonNull IBinder windowToken,
+            @NonNull Rect anchorBounds, @Nullable String filterText,
+            @NonNull Callback callback) {
+        mAnchorBounds.set(anchorBounds);
+        mCallback = callback;
+
+        if (response.getAuthentication() != null) {
+            final View content;
+            try {
+                content = response.getPresentation().apply(context, null);
+            } catch (RuntimeException e) {
+                callback.onCanceled();
+                Slog.e(TAG, "Error inflating remote views", e);
+                mWindow = null;
+                mAdapter = null;
+                return;
+            }
+            final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+            final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+            content.measure(widthMeasureSpec, heightMeasureSpec);
+            content.setOnClickListener(v -> mCallback.onResponsePicked(response));
+            mContentWidth = content.getMeasuredWidth();
+            mContentHeight = content.getMeasuredHeight();
+            mAdapter = null;
+
+            mWindow = new AnchoredWindow(windowToken, content);
+            mWindow.update(mContentWidth, mContentHeight, mAnchorBounds);
+        } else {
+            final int datasetCount = response.getDatasets().size();
+            final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
+            for (int i = 0; i < datasetCount; i++) {
+                final Dataset dataset = response.getDatasets().get(i);
+                final int index = dataset.getFieldIds().indexOf(focusedViewId);
+                if (index >= 0) {
+                    AutoFillValue value = dataset.getFieldValues().get(index);
+                    final View view;
+                    try {
+                        view = dataset.getPresentation().apply(context, null);
+                    } catch (RuntimeException e) {
+                        Slog.e(TAG, "Error inflating remote views", e);
+                        continue;
+                    }
+                    items.add(new ViewItem(dataset, value.coerceToString()
+                            .toLowerCase(), view));
+                }
+            }
+
+            mAdapter = new ArrayAdapter<ViewItem>(context, 0, items) {
+                @Override
+                public View getView(int position, View convertView, ViewGroup parent) {
+                    return getItem(position).getView();
+                }
+            };
+
+            final LayoutInflater inflater = LayoutInflater.from(context);
+            final ListView listView = (ListView) inflater.inflate(
+                    com.android.internal.R.layout.autofill_dataset_picker, null);
+            listView.setAdapter(mAdapter);
+            listView.setOnItemClickListener((adapter, view, position, id) -> {
+                final ViewItem vi = mAdapter.getItem(position);
+                mCallback.onDatasetPicked(vi.getDataset());
+            });
+
+            filter(filterText);
+            mWindow = new AnchoredWindow(windowToken, listView);
+        }
+    }
+
+    public void update(@NonNull Rect anchorBounds) {
+        throwIfDestroyed();
+        if (!mAnchorBounds.equals(anchorBounds)) {
+            mAnchorBounds.set(anchorBounds);
+            mWindow.update(mContentWidth, mContentHeight, anchorBounds);
+        }
+    }
+
+    public void filter(@Nullable String filterText) {
+        throwIfDestroyed();
+        if (mAdapter == null) {
+            return;
+        }
+        if (Objects.equal(mFilterText, filterText)) {
+            return;
+        }
+        mFilterText = filterText;
+        mAdapter.getFilter().filter(filterText, (count) -> {
+            if (mDestroyed) {
+                return;
+            }
+            if (count <= 0) {
+                mCallback.onCanceled();
+            } else {
+                if (updateContentSize()) {
+                    mWindow.update(mContentWidth, mContentHeight, mAnchorBounds);
+                }
+            }
+        });
+    }
+
+    public void destroy() {
+        throwIfDestroyed();
+        mWindow.destroy();
+        mDestroyed = true;
+    }
+
+    private boolean updateContentSize() {
+        if (mAdapter == null) {
+            return false;
+        }
+        boolean changed = false;
+        if (mAdapter.getCount() <= 0) {
+            if (mContentWidth != 0) {
+                mContentWidth = 0;
+                changed = true;
+            }
+            if (mContentHeight != 0) {
+                mContentHeight = 0;
+                changed = true;
+            }
+            return changed;
+        }
+
+        mContentWidth = 0;
+        mContentHeight = 0;
+
+        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+        final int itemCount = mAdapter.getCount();
+        for (int i = 0; i < itemCount; i++) {
+            View view = mAdapter.getItem(i).getView();
+            view.measure(widthMeasureSpec, heightMeasureSpec);
+            final int newContentWidth = Math.max(mContentWidth, view.getMeasuredWidth());
+            if (newContentWidth != mContentWidth) {
+                mContentWidth = newContentWidth;
+                changed = true;
+            }
+            final int newContentHeight = mContentHeight + view.getMeasuredHeight();
+            if (newContentHeight != mContentHeight) {
+                mContentHeight = newContentHeight;
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    private void throwIfDestroyed() {
+        if (mDestroyed) {
+            throw new IllegalStateException("cannot interact with a destroyed instance");
+        }
+    }
+
+    private static class ViewItem {
+        private final String mValue;
+        private final Dataset mDataset;
+        private final View mView;
+
+        ViewItem(Dataset dataset, String value, View view) {
+            mDataset = dataset;
+            mValue = value.toLowerCase();
+            mView = view;
+        }
+
+        public View getView() {
+            return mView;
+        }
+
+        public Dataset getDataset() {
+            return mDataset;
+        }
+
+        @Override
+        public String toString() {
+            // Used for filtering in the adapter
+            return mValue;
+        }
+    }
+
+    final class AnchoredWindow implements View.OnTouchListener {
+        private final Point mTempPoint = new Point();
+
+        private final WindowManager mWm;
+
+        private final IBinder mActivityToken;
+        private final View mContentView;
+
+        /**
+         * Constructor.
+         *
+         * @param activityToken token to pass to window manager
+         * @param contentView content of the window
+         */
+        AnchoredWindow(IBinder activityToken, View contentView) {
+            mWm = contentView.getContext().getSystemService(WindowManager.class);
+            mActivityToken = activityToken;
+            mContentView = contentView;
+        }
+
+        /**
+         * Hides the window.
+         */
+        void destroy() {
+            if (mContentView.isAttachedToWindow()) {
+                mContentView.setOnTouchListener(null);
+                mWm.removeView(mContentView);
+            }
+        }
+
+        @Override
+        public boolean onTouch(View view, MotionEvent event) {
+            // When the window is touched outside, hide the window.
+            if (view == mContentView && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                mCallback.onCanceled();
+                return true;
+            }
+            return false;
+        }
+
+        public void update(int desiredWidth, int desiredHeight, Rect anchorBounds) {
+            final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+            params.setTitle("FillUi");
+            params.token = mActivityToken;
+            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+            params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+            params.format = PixelFormat.TRANSLUCENT;
+
+            mWm.getDefaultDisplay().getRealSize(mTempPoint);
+            final int screenWidth = mTempPoint.x;
+            final int screenHeight = mTempPoint.y;
+
+            // Try to place the window at the start of the anchor view if
+            // there is space to fit the content, otherwise fit as much of
+            // the window as possible moving it to the left using all available
+            // screen width.
+            params.x = Math.min(anchorBounds.left, Math.max(screenWidth - desiredWidth, 0));
+            params.width = Math.min(screenWidth, desiredWidth);
+
+            // Try to fit below using all available space with top-start gravity
+            // and if that fails try to fit above using all available space with
+            // bottom-start gravity.
+            final int verticalSpaceBelow = screenHeight - anchorBounds.bottom;
+            if (desiredHeight <= verticalSpaceBelow) {
+                // Fits below bounds.
+                params.height = desiredHeight;
+                params.gravity = Gravity.TOP | Gravity.START;
+                params.y = anchorBounds.bottom;
+            } else {
+                final int verticalSpaceAbove = anchorBounds.top;
+                if (desiredHeight <= verticalSpaceAbove) {
+                    // Fits above bounds.
+                    params.height = desiredHeight;
+                    params.gravity = Gravity.BOTTOM | Gravity.START;
+                    params.y = anchorBounds.top + desiredHeight;
+                } else {
+                    // Pick above/below based on which has the most space.
+                    if (verticalSpaceBelow >= verticalSpaceAbove) {
+                        params.height = verticalSpaceBelow;
+                        params.gravity = Gravity.TOP | Gravity.START;
+                        params.y = anchorBounds.bottom;
+                    } else {
+                        params.height = verticalSpaceAbove;
+                        params.gravity = Gravity.BOTTOM | Gravity.START;
+                        params.y = anchorBounds.top + desiredHeight;
+                    }
+                }
+            }
+
+            if (!mContentView.isAttachedToWindow()) {
+                mWm.addView(mContentView, params);
+                mContentView.setOnTouchListener(this);
+            } else {
+                mWm.updateViewLayout(mContentView, params);
+            }
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
new file mode 100644
index 0000000..b7215d6
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Handler;
+import android.text.format.DateUtils;
+import android.view.Gravity;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.R;
+import com.android.server.UiThread;
+
+/**
+ * Autofill Save Prompt
+ */
+final class SaveUi {
+    public interface OnSaveListener {
+        void onSave();
+        void onCancel();
+    }
+
+    private static final long LIFETIME_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+
+    private final Handler mHandler = UiThread.getHandler();
+
+    private final @NonNull Dialog mDialog;
+
+    private final @NonNull OnSaveListener mListener;
+
+    private boolean mDestroyed;
+
+    SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel,
+            @NonNull OnSaveListener listener) {
+        mListener = listener;
+
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        final View view = inflater.inflate(R.layout.autofill_save, null);
+
+        final TextView title = (TextView) view.findViewById(R.id.autofill_save_title);
+        title.setText(context.getString(R.string.autofill_save_title, providerLabel));
+
+        final View noButton = view.findViewById(R.id.autofill_save_no);
+        noButton.setOnClickListener((v) -> mListener.onCancel());
+
+        final View yesButton = view.findViewById(R.id.autofill_save_yes);
+        yesButton.setOnClickListener((v) -> mListener.onSave());
+
+        final View closeButton = view.findViewById(R.id.autofill_save_close);
+        closeButton.setOnClickListener((v) -> mListener.onCancel());
+
+        mDialog = new Dialog(context, R.style.Theme_Material_Panel);
+        mDialog.setContentView(view);
+
+        final Window window = mDialog.getWindow();
+        window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+        window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
+        window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
+        window.setCloseOnTouchOutside(true);
+        window.getAttributes().width = WindowManager.LayoutParams.MATCH_PARENT;
+
+        mDialog.show();
+
+        mHandler.postDelayed(() -> mListener.onCancel(), LIFETIME_MILLIS);
+    }
+
+    void destroy() {
+        throwIfDestroyed();
+        mHandler.removeCallbacksAndMessages(mListener);
+        mDialog.dismiss();
+        mDestroyed = true;
+    }
+
+    private void throwIfDestroyed() {
+        if (mDestroyed) {
+            throw new IllegalStateException("cannot interact with a destroyed instance");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 33351ff..480b08a 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -19,13 +19,9 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.UserInfo;
-import android.os.BatteryManager;
-import android.os.BatteryProperties;
 import android.os.Build;
-import android.os.IBatteryPropertiesListener;
-import android.os.IBatteryPropertiesRegistrar;
+import android.os.FileUtils;
 import android.os.RecoverySystem;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -34,14 +30,12 @@
 import android.text.format.DateUtils;
 import android.util.ExceptionUtils;
 import android.util.MathUtils;
-import android.util.MutableBoolean;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.util.ArrayUtils;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.io.File;
 
 /**
  * Utilities to help rescue the system from crash loops. Callers are expected to
@@ -325,30 +319,13 @@
 
     /**
      * Hacky test to check if the device has an active USB connection, which is
-     * a good proxy for someone doing local development work. It uses a low
-     * level call since we may not have started {@link BatteryManager} yet.
+     * a good proxy for someone doing local development work.
      */
     private static boolean isUsbActive() {
-        final MutableBoolean res = new MutableBoolean(false);
-        final CountDownLatch latch = new CountDownLatch(1);
-        final IBatteryPropertiesListener listener = new IBatteryPropertiesListener.Stub() {
-            @Override
-            public void batteryPropertiesChanged(BatteryProperties props) {
-                res.value = props.chargerUsbOnline;
-                latch.countDown();
-            }
-        };
-
         try {
-            final IBatteryPropertiesRegistrar bpr = IBatteryPropertiesRegistrar.Stub
-                    .asInterface(ServiceManager.getService("batteryproperties"));
-            bpr.registerListener(listener);
-            try {
-                latch.await(5, TimeUnit.SECONDS);
-            } finally {
-                bpr.unregisterListener(listener);
-            }
-            return res.value;
+            final String state = FileUtils
+                    .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, "");
+            return "CONFIGURED".equals(state.trim());
         } catch (Throwable t) {
             Slog.w(TAG, "Failed to determine if device was on USB", t);
             return false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7a3326b..30b267f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7694,7 +7694,7 @@
                             aspectRatio);
                     mStackSupervisor.moveActivityToPinnedStackLocked(r, "enterPictureInPictureMode",
                             bounds, true /* moveHomeStackToFront */);
-                    final ActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
+                    final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
                     stack.setPictureInPictureAspectRatio(aspectRatio);
                     stack.setPictureInPictureActions(actions);
 
@@ -7748,9 +7748,9 @@
 
                 // Only update the saved args from the args that are set
                 r.pictureInPictureArgs.copyOnlySet(args);
-                final ActivityStack stack = r.getStack();
-                if (stack.getStackId() == PINNED_STACK_ID) {
+                if (r.getStack().getStackId() == PINNED_STACK_ID) {
                     // If the activity is already in picture-in-picture, update the pinned stack now
+                    final PinnedActivityStack stack = r.getStack();
                     stack.setPictureInPictureAspectRatio(r.pictureInPictureArgs.getAspectRatio());
                     stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
                 }
@@ -10312,7 +10312,7 @@
             synchronized (this) {
                 if (animate) {
                     if (stackId == PINNED_STACK_ID) {
-                        final ActivityStack pinnedStack =
+                        final PinnedActivityStack pinnedStack =
                                 mStackSupervisor.getStack(PINNED_STACK_ID);
                         pinnedStack.animateResizePinnedStack(bounds, animationDuration);
                     } else {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 6f89cf2..aef429e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -880,8 +880,8 @@
     /**
      * @return Stack value from current task, null if there is no task.
      */
-    ActivityStack getStack() {
-        return task != null ? task.getStack() : null;
+    <T extends ActivityStack> T getStack() {
+        return task != null ? (T) task.getStack() : null;
     }
 
     boolean changeWindowTranslucency(boolean toOpaque) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b948c15..7f7caff 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -84,7 +84,6 @@
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityController;
-import android.app.RemoteAction;
 import android.app.ResultInfo;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -135,7 +134,8 @@
 /**
  * State and management of a single stack of activities.
  */
-final class ActivityStack extends ConfigurationContainer implements StackWindowListener {
+class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
+        implements StackWindowListener {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
@@ -247,7 +247,7 @@
 
     final ActivityManagerService mService;
     private final WindowManagerService mWindowManager;
-    private StackWindowController mWindowContainerController;
+    T mWindowContainerController;
     private final RecentTasks mRecentTasks;
 
     /**
@@ -462,14 +462,18 @@
                 ? new LaunchingTaskPositioner() : null;
         final ActivityStackSupervisor.ActivityDisplay display = mActivityContainer.mActivityDisplay;
         mTmpRect2.setEmpty();
-        mWindowContainerController = new StackWindowController(mStackId, this,
-                display.mDisplayId, onTop, mTmpRect2);
+        mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
+                mTmpRect2);
         activityContainer.mStack = this;
         mStackSupervisor.mActivityContainers.put(mStackId, activityContainer);
         postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
     }
 
-    StackWindowController getWindowContainerController() {
+    T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
+        return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
+    }
+
+    T getWindowContainerController() {
         return mWindowContainerController;
     }
 
@@ -540,18 +544,6 @@
         mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
     }
 
-    void animateResizePinnedStack(Rect bounds, int animationDuration) {
-        mWindowContainerController.animateResizePinnedStack(bounds, animationDuration);
-    }
-
-    void setPictureInPictureAspectRatio(float aspectRatio) {
-        mWindowContainerController.setPictureInPictureAspectRatio(aspectRatio);
-    }
-
-    void setPictureInPictureActions(List<RemoteAction> actions) {
-        mWindowContainerController.setPictureInPictureActions(actions);
-    }
-
     void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds, Rect outTempInsetBounds,
             boolean ignoreVisibility) {
         mWindowContainerController.getStackDockedModeBounds(outBounds, outTempBounds,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7ceeff8..1e33bb9 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -120,7 +120,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
@@ -153,7 +152,6 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.IntArray;
 import android.util.Slog;
@@ -2037,19 +2035,20 @@
                 || mService.mSupportsFreeformWindowManagement;
     }
 
-    ActivityStack getStack(int stackId) {
+    protected <T extends ActivityStack> T getStack(int stackId) {
         return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
     }
 
-    ActivityStack getStack(int stackId, boolean createStaticStackIfNeeded, boolean createOnTop) {
+    protected <T extends ActivityStack> T getStack(int stackId, boolean createStaticStackIfNeeded,
+            boolean createOnTop) {
         final ActivityContainer activityContainer = mActivityContainers.get(stackId);
         if (activityContainer != null) {
-            return activityContainer.mStack;
+            return (T) activityContainer.mStack;
         }
         if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
             return null;
         }
-        return createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
+        return (T) createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
     }
 
     /**
@@ -2856,7 +2855,7 @@
 
         mWindowManager.deferSurfaceLayout();
         // Need to make sure the pinned stack exist so we can resize it below...
-        final ActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+        final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
 
         try {
             final TaskRecord task = r.task;
@@ -2906,7 +2905,7 @@
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
 
-        stack.animateResizePinnedStack(bounds, -1);
+        stack.animateResizePinnedStack(bounds, -1 /* animationDuration */);
         mService.mTaskChangeNotificationController.notifyActivityPinned();
     }
 
@@ -4367,7 +4366,14 @@
             synchronized (mService) {
                 mStackId = stackId;
                 mActivityDisplay = activityDisplay;
-                new ActivityStack(this, mRecentTasks, onTop);
+                switch (mStackId) {
+                    case PINNED_STACK_ID:
+                        new PinnedActivityStack(this, mRecentTasks, onTop);
+                        break;
+                    default:
+                        new ActivityStack(this, mRecentTasks, onTop);
+                        break;
+                }
                 mIdString = "ActivtyContainer{" + mStackId + "}";
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this);
             }
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
new file mode 100644
index 0000000..aa7ab15
--- /dev/null
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.app.RemoteAction;
+import android.graphics.Rect;
+
+import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
+import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.StackWindowController;
+
+import java.util.List;
+
+/**
+ * State and management of the pinned stack of activities.
+ */
+class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> {
+
+    PinnedActivityStack(ActivityContainer activityContainer,
+            RecentTasks recentTasks, boolean onTop) {
+        super(activityContainer, recentTasks, onTop);
+    }
+
+    @Override
+    PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
+            Rect outBounds) {
+        return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
+    }
+
+    void animateResizePinnedStack(Rect bounds, int animationDuration) {
+        getWindowContainerController().animateResizePinnedStack(bounds, animationDuration);
+    }
+
+    void setPictureInPictureAspectRatio(float aspectRatio) {
+        getWindowContainerController().setPictureInPictureAspectRatio(aspectRatio);
+    }
+
+    void setPictureInPictureActions(List<RemoteAction> actions) {
+        getWindowContainerController().setPictureInPictureActions(actions);
+    }
+}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 74e025d..5149933 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -149,13 +149,7 @@
     }
 
     private void handleBinderDiedLocked(IBinder appToken) {
-        VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
-        if (device != null) {
-            Slog.i(TAG, "Virtual display device released because application token died: "
-                    + device.mOwnerPackageName);
-            device.destroyLocked(false);
-            sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
-        }
+        mVirtualDisplayDevices.remove(appToken);
     }
 
     private void handleMediaProjectionStoppedLocked(IBinder appToken) {
@@ -216,6 +210,10 @@
         public void binderDied() {
             synchronized (getSyncRoot()) {
                 handleBinderDiedLocked(mAppToken);
+                Slog.i(TAG, "Virtual display device released because application token died: "
+                    + mOwnerPackageName);
+                destroyLocked(false);
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 01bd86d..837b69e 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -272,18 +272,10 @@
         void onAnimationEnd();
 
         void moveToFullscreen();
-
-        void getFullScreenBounds(Rect bounds);
     }
 
-    void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration) {
-        boolean moveToFullscreen = false;
-        if (to == null) {
-            to = new Rect();
-            target.getFullScreenBounds(to);
-            moveToFullscreen = true;
-        }
-
+    void animateBounds(final AnimateBoundsUser target, Rect from, Rect to, int animationDuration,
+            boolean moveToFullscreen) {
         final BoundsAnimator existing = mRunningAnimations.get(target);
         final boolean replacing = existing != null;
 
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index cfeb198..5f41187 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -230,28 +230,6 @@
     }
 
     /**
-     * @return the movement bounds for the given {@param stackBounds} and the current state of the
-     *         controller.
-     */
-    private Rect getMovementBounds(Rect stackBounds) {
-        return getMovementBounds(stackBounds, true /* adjustForIme */);
-    }
-
-    /**
-     * @return the movement bounds for the given {@param stackBounds} and the current state of the
-     *         controller.
-     */
-    private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
-        final Rect movementBounds = new Rect();
-        getInsetBounds(movementBounds);
-
-        // Apply the movement bounds adjustments based on the current state
-        mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
-                (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
-        return movementBounds;
-    }
-
-    /**
      * @param preChangeTargetBounds The final bounds of the stack if it is currently animating
      * @return the repositioned PIP bounds given it's pre-change bounds, and the new display
      *         content.
@@ -387,6 +365,28 @@
     }
 
     /**
+     * @return the movement bounds for the given {@param stackBounds} and the current state of the
+     *         controller.
+     */
+    private Rect getMovementBounds(Rect stackBounds) {
+        return getMovementBounds(stackBounds, true /* adjustForIme */);
+    }
+
+    /**
+     * @return the movement bounds for the given {@param stackBounds} and the current state of the
+     *         controller.
+     */
+    private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
+        final Rect movementBounds = new Rect();
+        getInsetBounds(movementBounds);
+
+        // Apply the movement bounds adjustments based on the current state
+        mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+                (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
+        return movementBounds;
+    }
+
+    /**
      * Applies the minimized offsets to the given stack bounds.
      */
     private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
new file mode 100644
index 0000000..71f88de
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+
+import android.app.RemoteAction;
+import android.graphics.Rect;
+
+import com.android.server.UiThread;
+
+import java.util.List;
+
+/**
+ * Controller for the pinned stack container. See {@link StackWindowController}.
+ */
+public class PinnedStackWindowController extends StackWindowController {
+
+    private Rect mTmpBoundsRect = new Rect();
+
+    public PinnedStackWindowController(int stackId, StackWindowListener listener, int displayId,
+            boolean onTop, Rect outBounds) {
+        super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+    }
+
+    /**
+     * Animates the pinned stack.
+     */
+    public void animateResizePinnedStack(Rect bounds, int animationDuration) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("Pinned stack container not found :(");
+            }
+
+            // Get non-null fullscreen bounds if the bounds are null
+            final boolean moveToFullscreen = bounds == null;
+            bounds = getPinnedStackAnimationBounds(bounds);
+
+            // If the bounds are truly null, then there was no fullscreen stack at this time, so
+            // animate this to the full display bounds
+            final Rect toBounds;
+            if (bounds == null) {
+                toBounds = new Rect();
+                mContainer.getDisplayContent().getLogicalDisplayRect(toBounds);
+            } else {
+                toBounds = bounds;
+            }
+
+            final Rect originalBounds = new Rect();
+            mContainer.getBounds(originalBounds);
+            mContainer.setAnimatingBounds(toBounds);
+            UiThread.getHandler().post(() -> {
+                mService.mBoundsAnimationController.animateBounds(mContainer, originalBounds,
+                        toBounds, animationDuration, moveToFullscreen);
+            });
+        }
+    }
+
+    /**
+     * Sets the current picture-in-picture aspect ratio.
+     */
+    public void setPictureInPictureAspectRatio(float aspectRatio) {
+        synchronized (mWindowMap) {
+            if (!mService.mSupportsPictureInPicture || mContainer == null) {
+                return;
+            }
+
+            final int displayId = mContainer.getDisplayContent().getDisplayId();
+            final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
+            final Rect targetBounds = new Rect();
+            mContainer.getAnimatingBounds(targetBounds);
+            if (!toBounds.equals(targetBounds)) {
+                animateResizePinnedStack(toBounds, -1 /* duration */);
+            }
+
+            final PinnedStackController pinnedStackController =
+                    mContainer.getDisplayContent().getPinnedStackController();
+            pinnedStackController.setAspectRatio(
+                    pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
+                            ? aspectRatio : -1f);
+        }
+    }
+
+    /**
+     * Sets the current picture-in-picture actions.
+     */
+    public void setPictureInPictureActions(List<RemoteAction> actions) {
+        synchronized (mWindowMap) {
+            if (!mService.mSupportsPictureInPicture || mContainer == null) {
+                return;
+            }
+
+            mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
+        }
+    }
+
+    /**
+     * Checks the {@param bounds} and retirms non-null fullscreen bounds for the pinned stack
+     * animation if necessary.
+     */
+    private Rect getPinnedStackAnimationBounds(Rect bounds) {
+        mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpBoundsRect);
+        if (bounds == null && !mTmpBoundsRect.isEmpty()) {
+            bounds = new Rect(mTmpBoundsRect);
+        }
+        return bounds;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 142f69a..b0e115b 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -199,59 +199,6 @@
         }
     }
 
-    // TODO: This and similar methods should be separated into PinnedStackWindowContainerController
-    public void animateResizePinnedStack(final Rect bounds, final int animationDuration) {
-        synchronized (mWindowMap) {
-            if (mContainer == null) {
-                throw new IllegalArgumentException("Pinned stack container not found :(");
-            }
-            final Rect originalBounds = new Rect();
-            mContainer.getBounds(originalBounds);
-            mContainer.setAnimatingBounds(bounds);
-            UiThread.getHandler().post(new Runnable() {
-                @Override
-                public void run() {
-                    mService.mBoundsAnimationController.animateBounds(
-                            mContainer, originalBounds, bounds, animationDuration);
-                }
-            });
-        }
-    }
-
-    /** Sets the current picture-in-picture aspect ratio. */
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        synchronized (mWindowMap) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return;
-            }
-
-            final int displayId = mContainer.getDisplayContent().getDisplayId();
-            final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
-            final Rect targetBounds = new Rect();
-            mContainer.getAnimatingBounds(targetBounds);
-            if (!toBounds.equals(targetBounds)) {
-                animateResizePinnedStack(toBounds, -1 /* duration */);
-            }
-
-            final PinnedStackController pinnedStackController =
-                    mContainer.getDisplayContent().getPinnedStackController();
-            pinnedStackController.setAspectRatio(
-                    pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
-                            ? aspectRatio : -1f);
-        }
-    }
-
-    /** Sets the current picture-in-picture actions. */
-    public void setPictureInPictureActions(List<RemoteAction> actions) {
-        synchronized (mWindowMap) {
-            if (!mService.mSupportsPictureInPicture || mContainer == null) {
-                return;
-            }
-
-            mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
-        }
-    }
-
     public void getStackDockedModeBounds(Rect outBounds, Rect outTempBounds,
             Rect outTempInsetBounds, boolean ignoreVisibility) {
         synchronized (mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 5f8b694..c7532ac 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -1476,14 +1477,6 @@
         }
     }
 
-    @Override
-    public void getFullScreenBounds(Rect bounds) {
-        // This is currently only used for the pinned stack animation when leaving PiP
-        // (see {@link BoundsAnimationController}), and in that case we need to animate this back
-        // to the full bounds to match the fullscreen stack
-        getDisplayContent().getLogicalDisplayRect(bounds);
-    }
-
     public boolean hasMovementAnimations() {
         return StackId.hasMovementAnimations(mStackId);
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9397c9e..474610b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7267,10 +7267,6 @@
         return mRoot.getDisplayContentOrCreate(DEFAULT_DISPLAY);
     }
 
-    private DisplayInfo getDefaultDisplayInfoLocked() {
-        return getDefaultDisplayContentLocked().getDisplayInfo();
-    }
-
     public void onDisplayAdded(int displayId) {
         mH.sendMessage(mH.obtainMessage(H.DO_DISPLAY_ADDED, displayId, 0));
     }
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..69b8acc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.internal.ImsCallSession;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+/**
+ * Base implementation of IImsCallSession, which implements stub versions of the methods in the
+ * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+    /**
+     * Closes the object. This object is not usable after being closed.
+     */
+    @Override
+    public void close() throws RemoteException {
+
+    }
+
+    /**
+     * Gets the call ID of the session.
+     *
+     * @return the call ID
+     */
+    @Override
+    public String getCallId() throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Gets the call profile that this session is associated with
+     *
+     * @return the {@link ImsCallProfile} that this session is associated with
+     */
+    @Override
+    public ImsCallProfile getCallProfile() throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Gets the local call profile that this session is associated with
+     *
+     * @return the local {@link ImsCallProfile} that this session is associated with
+     */
+    @Override
+    public ImsCallProfile getLocalCallProfile() throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Gets the remote call profile that this session is associated with
+     *
+     * @return the remote {@link ImsCallProfile} that this session is associated with
+     */
+    @Override
+    public ImsCallProfile getRemoteCallProfile() throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Gets the value associated with the specified property of this session.
+     *
+     * @return the string value associated with the specified property
+     */
+    @Override
+    public String getProperty(String name) throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Gets the session state.
+     * The value returned must be one of the states in {@link ImsCallSession.State}.
+     *
+     * @return the session state
+     */
+    @Override
+    public int getState() throws RemoteException {
+        return ImsCallSession.State.INVALID;
+    }
+
+    /**
+     * Checks if the session is in call.
+     *
+     * @return true if the session is in call, false otherwise
+     */
+    @Override
+    public boolean isInCall() throws RemoteException {
+        return false;
+    }
+
+    /**
+     * Sets the listener to listen to the session events. An {@link ImsCallSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     */
+    @Override
+    public void setListener(IImsCallSessionListener listener) throws RemoteException {
+    }
+
+    /**
+     * Mutes or unmutes the mic for the active call.
+     *
+     * @param muted true if the call is muted, false otherwise
+     */
+    @Override
+    public void setMute(boolean muted) throws RemoteException {
+    }
+
+    /**
+     * Initiates an IMS call with the specified target and call profile.
+     * The session listener set in {@link #setListener} is called back upon defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param callee dialed string to make the call to
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void start(String callee, ImsCallProfile profile) throws RemoteException {
+    }
+
+    /**
+     * Initiates an IMS call with the specified participants and call profile.
+     * The session listener set in {@link #setListener} is called back upon defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param participants participant list to initiate an IMS conference call
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see {@link ImsCallSession.Listener#callSessionStarted},
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void startConference(String[] participants, ImsCallProfile profile)
+            throws RemoteException {
+    }
+
+    /**
+     * Accepts an incoming call or session update.
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be answered
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+     * @see {@link ImsCallSession.Listener#callSessionStarted}
+     */
+    @Override
+    public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+    }
+
+    /**
+     * Rejects an incoming call or session update.
+     *
+     * @param reason reason code to reject an incoming call, defined in
+     *         com.android.ims.ImsReasonInfo
+     * {@link ImsCallSession.Listener#callSessionStartFailed}
+     */
+    @Override
+    public void reject(int reason) throws RemoteException {
+    }
+
+    /**
+     * Terminates a call.
+     *
+     * @param reason reason code to terminate a call, defined in
+     *         com.android.ims.ImsReasonInfo
+     *
+     * @see {@link ImsCallSession.Listener#callSessionTerminated}
+     */
+    @Override
+    public void terminate(int reason) throws RemoteException {
+    }
+
+    /**
+     * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+     * called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+     * @see {@link ImsCallSession.Listener#callSessionHeld},
+     * {@link ImsCallSession.Listener#callSessionHoldFailed}
+     */
+    @Override
+    public void hold(ImsStreamMediaProfile profile) throws RemoteException {
+    }
+
+    /**
+     * Continues a call that's on hold. When it succeeds,
+     * {@link ImsCallSession.Listener#callSessionResumed} is called.
+     *
+     * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+     * @see {@link ImsCallSession.Listener#callSessionResumed},
+     * {@link ImsCallSession.Listener#callSessionResumeFailed}
+     */
+    @Override
+    public void resume(ImsStreamMediaProfile profile) throws RemoteException {
+    }
+
+    /**
+     * Merges the active & hold call. When the merge starts,
+     * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+     * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+     * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+     * fails.
+     *
+     * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+     * {@link ImsCallSession.Listener#callSessionMergeComplete},
+     *      {@link ImsCallSession.Listener#callSessionMergeFailed}
+     */
+    @Override
+    public void merge() throws RemoteException {
+    }
+
+    /**
+     * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be updated
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+     * @see {@link ImsCallSession.Listener#callSessionUpdated},
+     * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+     */
+    @Override
+    public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+    }
+
+    /**
+     * Extends this call to the conference call with the specified recipients.
+     *
+     * @param participants participant list to be invited to the conference call after extending the
+     * call
+     * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+     * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+     */
+    @Override
+    public void extendToConference(String[] participants) throws RemoteException {
+    }
+
+    /**
+     * Requests the conference server to invite an additional participants to the conference.
+     *
+     * @param participants participant list to be invited to the conference call
+     * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+     */
+    @Override
+    public void inviteParticipants(String[] participants) throws RemoteException {
+    }
+
+    /**
+     * Requests the conference server to remove the specified participants from the conference.
+     *
+     * @param participants participant list to be removed from the conference call
+     * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+     *      {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+     */
+    @Override
+    public void removeParticipants(String[] participants) throws RemoteException {
+    }
+
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    @Override
+    public void sendDtmf(char c, Message result) throws RemoteException {
+    }
+
+    /**
+     * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    @Override
+    public void startDtmf(char c) throws RemoteException {
+    }
+
+    /**
+     * Stop a DTMF code.
+     */
+    @Override
+    public void stopDtmf() throws RemoteException {
+    }
+
+    /**
+     * Sends an USSD message.
+     *
+     * @param ussdMessage USSD message to send
+     */
+    @Override
+    public void sendUssd(String ussdMessage) throws RemoteException {
+    }
+
+    /**
+     * Returns a binder for the video call provider implementation contained within the IMS service
+     * process. This binder is used by the VideoCallProvider subclass in Telephony which
+     * intermediates between the propriety implementation and Telecomm/InCall.
+     */
+    @Override
+    public IImsVideoCallProvider getVideoCallProvider() throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Determines if the current session is multiparty.
+     * @return {@code True} if the session is multiparty.
+     */
+    @Override
+    public boolean isMultiparty() throws RemoteException {
+        return false;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
new file mode 100644
index 0000000..46f8f80
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+
+/**
+ * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods
+ * in the IImsCallSessionListener AIDL. Override the methods that your implementation of
+ * ImsCallSessionListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub {
+    /**
+     * Notifies the result of the basic session operation (setup / terminate).
+     */
+    @Override
+    public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    /**
+     * Notifies the result of the call hold/resume operation.
+     */
+    @Override
+    public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) {
+        // no-op
+    }
+
+    /**
+     * Notifies the result of call merge operation.
+     */
+    @Override
+    public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession,
+            ImsCallProfile profile) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionMergeComplete(IImsCallSession session) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    /**
+     * Notifies the result of call upgrade / downgrade or any other call
+     * updates.
+     */
+    @Override
+    public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) {
+        // no-op
+    }
+
+    /**
+     * Notifies the result of conference extension.
+     */
+    @Override
+    public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession,
+            ImsCallProfile profile) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionConferenceExtendFailed(IImsCallSession session,
+            ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionConferenceExtendReceived(IImsCallSession session,
+            IImsCallSession newSession,
+            ImsCallProfile profile) {
+        // no-op
+    }
+
+    /**
+     * Notifies the result of the participant invitation / removal to/from the
+     * conference session.
+     */
+    @Override
+    public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
+            ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
+            ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    /**
+     * Notifies the changes of the conference info. the conference session.
+     */
+    @Override
+    public void callSessionConferenceStateUpdated(IImsCallSession session,
+            ImsConferenceState state) {
+        // no-op
+    }
+
+    /**
+     * Notifies the incoming USSD message.
+     */
+    @Override
+    public void callSessionUssdMessageReceived(IImsCallSession session, int mode,
+            String ussdMessage) {
+        // no-op
+    }
+
+    /**
+     * Notifies of handover information for this call
+     */
+    @Override
+    public void callSessionHandover(IImsCallSession session, int srcAccessTech,
+            int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    @Override
+    public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech,
+            int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+        // no-op
+    }
+
+    /**
+     * Notifies the TTY mode change by remote party.
+     *
+     * @param mode one of the following: -
+     *            {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+     *            {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+     *            {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+     *            {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+     */
+    @Override
+    public void callSessionTtyModeReceived(IImsCallSession session, int mode) {
+        // no-op
+    }
+
+    /**
+     * Notifies of a change to the multiparty state for this
+     * {@code ImsCallSession}.
+     *
+     * @param session The call session.
+     * @param isMultiParty {@code true} if the session became multiparty,
+     *            {@code false} otherwise.
+     */
+    @Override
+    public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) {
+        // no-op
+    }
+
+    /**
+     * Notifies the supplementary service information for the current session.
+     */
+    @Override
+    public void callSessionSuppServiceReceived(IImsCallSession session,
+            ImsSuppServiceNotification suppSrvNotification) {
+        // no-op
+    }
+}
+
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..5a4db99
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+
+/**
+ * Base implementation of ImsConfig, which implements stub versions of the methods
+ * in the IImsConfig AIDL. Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+
+public class ImsConfigImplBase extends IImsConfig.Stub {
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in Integer format.
+     */
+    @Override
+    public int getProvisionedValue(int item) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in String format.
+     */
+    @Override
+    public String getProvisionedStringValue(int item) throws RemoteException {
+        return null;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the master value is derived. Synchronous blocking call.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    @Override
+    public int setProvisionedValue(int item, int value) throws RemoteException {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the master value is derived.  Synchronous blocking call.
+     *
+     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    @Override
+    public int setProvisionedStringValue(int item, String value) throws RemoteException {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Gets the value of the specified IMS feature item for specified network type.
+     * This operation gets the feature config value from the master storage (i.e. final
+     * value). Asynchronous non-blocking call.
+     *
+     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param listener feature value returned asynchronously through listener.
+     */
+    @Override
+    public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+            throws RemoteException {
+    }
+
+    /**
+     * Sets the value for IMS feature item for specified network type.
+     * This operation stores the user setting in setting db from which master db
+     * is derived.
+     *
+     * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+     * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+     * @param listener, provided if caller needs to be notified for set result.
+     */
+    @Override
+    public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+            throws RemoteException {
+    }
+
+    /**
+     * Gets the value for IMS VoLTE provisioned.
+     * This should be the same as the operator provisioned value if applies.
+     */
+    @Override
+    public boolean getVolteProvisioned() throws RemoteException {
+        return false;
+    }
+
+    /**
+     * Gets the value for IMS feature item video quality.
+     *
+     * @param listener Video quality value returned asynchronously through listener.
+     */
+    @Override
+    public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+    }
+
+    /**
+     * Sets the value for IMS feature item video quality.
+     *
+     * @param quality, defines the value of video quality.
+     * @param listener, provided if caller needs to be notified for set result.
+     */
+    @Override
+    public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
new file mode 100644
index 0000000..89f95ff
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsEcbmListener;
+
+/**
+ * Base implementation of ImsEcbm, which implements stub versions of the methods
+ * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsEcbm maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsEcbmImplBase extends IImsEcbm.Stub {
+
+    /**
+     * Sets the listener.
+     */
+    @Override
+    public void setListener(IImsEcbmListener listener) throws RemoteException {
+
+    }
+
+    /**
+     * Requests Modem to come out of ECBM mode
+     */
+    @Override
+    public void exitEmergencyCallbackMode() throws RemoteException {
+
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
new file mode 100644
index 0000000..05da9da
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsExternalCallStateListener;
+import com.android.ims.internal.IImsMultiEndpoint;
+
+/**
+ * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
+ * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
+ * ImsMultiEndpoint supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsMultiEndpoint maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub {
+
+    /**
+     * Sets the listener.
+     */
+    @Override
+    public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+
+    }
+
+    /**
+     * Query API to get the latest Dialog Event Package information
+     * Should be invoked only after setListener is done
+     */
+    @Override
+    public void requestImsExternalCallStateInfo() throws RemoteException {
+
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
new file mode 100644
index 0000000..92f1a01
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsStreamMediaSession;
+
+/**
+ * Base implementation of ImsStreamMediaSession, which implements stub versions of the methods
+ * in the IImsStreamMediaSession AIDL. Override the methods that your implementation of
+ * ImsStreamMediaSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsStreamMediaSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsStreamMediaSessionImplBase extends IImsStreamMediaSession.Stub {
+
+    @Override
+    public void close() throws RemoteException {
+
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
new file mode 100644
index 0000000..dc74094
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUt, which implements stub versions of the methods
+ * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUt maintained by other ImsServices.
+ *
+ * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ *
+ * @hide
+ */
+
+public class ImsUtImplBase extends IImsUt.Stub {
+
+    /**
+     * Closes the object. This object is not usable after being closed.
+     */
+    @Override
+    public void close() throws RemoteException {
+
+    }
+
+    /**
+     * Retrieves the configuration of the call barring.
+     */
+    @Override
+    public int queryCallBarring(int cbType) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Retrieves the configuration of the call forward.
+     */
+    @Override
+    public int queryCallForward(int condition, String number) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Retrieves the configuration of the call waiting.
+     */
+    @Override
+    public int queryCallWaiting() throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Retrieves the default CLIR setting.
+     */
+    @Override
+    public int queryCLIR() throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Retrieves the CLIP call setting.
+     */
+    @Override
+    public int queryCLIP() throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Retrieves the COLR call setting.
+     */
+    @Override
+    public int queryCOLR() throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Retrieves the COLP call setting.
+     */
+    @Override
+    public int queryCOLP() throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Updates or retrieves the supplementary service configuration.
+     */
+    @Override
+    public int transact(Bundle ssInfo) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the call barring.
+     */
+    @Override
+    public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the call forward.
+     */
+    @Override
+    public int updateCallForward(int action, int condition, String number, int serviceClass,
+            int timeSeconds) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Updates the configuration of the call waiting.
+     */
+    @Override
+    public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the CLIR supplementary service.
+     */
+    @Override
+    public int updateCLIR(int clirMode) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the CLIP supplementary service.
+     */
+    @Override
+    public int updateCLIP(boolean enable) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the COLR supplementary service.
+     */
+    @Override
+    public int updateCOLR(int presentation) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Updates the configuration of the COLP supplementary service.
+     */
+    @Override
+    public int updateCOLP(boolean enable) throws RemoteException {
+        return -1;
+    }
+
+    /**
+     * Sets the listener.
+     */
+    @Override
+    public void setListener(IImsUtListener listener) throws RemoteException {
+    }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
new file mode 100644
index 0000000..b371efb
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallForwardInfo;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsSsInfo;
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUtListener, which implements stub versions of the methods
+ * in the IImsUtListener AIDL. Override the methods that your implementation of
+ * ImsUtListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUtListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsUtListenerImplBase extends IImsUtListener.Stub {
+
+    /**
+     * Notifies the result of the supplementary service configuration udpate.
+     */
+    @Override
+    public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException {
+    }
+
+    @Override
+    public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the result of the supplementary service configuration query.
+     */
+    @Override
+    public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException {
+    }
+
+    @Override
+    public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the status of the call barring supplementary service.
+     */
+    @Override
+    public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the status of the call forwarding supplementary service.
+     */
+    @Override
+    public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo)
+            throws RemoteException {
+    }
+
+    /**
+     * Notifies the status of the call waiting supplementary service.
+     */
+    @Override
+    public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
+            throws RemoteException {
+    }
+}
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
new file mode 100644
index 0000000..cd076b1
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.ims;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+import com.android.ims.internal.IImsConfig;
+
+/**
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+public class ImsConfig {
+    private static final String TAG = "ImsConfig";
+    private boolean DBG = true;
+    private final IImsConfig miConfig;
+    private Context mContext;
+
+    /**
+     * Broadcast action: the feature enable status was changed
+     *
+     * @hide
+     */
+    public static final String ACTION_IMS_FEATURE_CHANGED =
+            "com.android.intent.action.IMS_FEATURE_CHANGED";
+
+    /**
+     * Broadcast action: the configuration was changed
+     *
+     * @hide
+     */
+    public static final String ACTION_IMS_CONFIG_CHANGED =
+            "com.android.intent.action.IMS_CONFIG_CHANGED";
+
+    /**
+     * Extra parameter "item" of intent ACTION_IMS_FEATURE_CHANGED and ACTION_IMS_CONFIG_CHANGED.
+     * It is the value of FeatureConstants or ConfigConstants.
+     *
+     * @hide
+     */
+    public static final String EXTRA_CHANGED_ITEM = "item";
+
+    /**
+     * Extra parameter "value" of intent ACTION_IMS_FEATURE_CHANGED and ACTION_IMS_CONFIG_CHANGED.
+     * It is the new value of "item".
+     *
+     * @hide
+     */
+    public static final String EXTRA_NEW_VALUE = "value";
+
+    /**
+    * Defines IMS service/capability feature constants.
+    */
+    public static class FeatureConstants {
+        public static final int FEATURE_TYPE_UNKNOWN = -1;
+
+        /**
+         * FEATURE_TYPE_VOLTE supports features defined in 3GPP and
+         * GSMA IR.92 over LTE.
+         */
+        public static final int FEATURE_TYPE_VOICE_OVER_LTE = 0;
+
+        /**
+         * FEATURE_TYPE_LVC supports features defined in 3GPP and
+         * GSMA IR.94 over LTE.
+         */
+        public static final int FEATURE_TYPE_VIDEO_OVER_LTE = 1;
+
+        /**
+         * FEATURE_TYPE_VOICE_OVER_WIFI supports features defined in 3GPP and
+         * GSMA IR.92 over WiFi.
+         */
+        public static final int FEATURE_TYPE_VOICE_OVER_WIFI = 2;
+
+        /**
+         * FEATURE_TYPE_VIDEO_OVER_WIFI supports features defined in 3GPP and
+         * GSMA IR.94 over WiFi.
+         */
+        public static final int FEATURE_TYPE_VIDEO_OVER_WIFI = 3;
+
+        /**
+         * FEATURE_TYPE_UT supports features defined in 3GPP and
+         * GSMA IR.92 over LTE.
+         */
+        public static final int FEATURE_TYPE_UT_OVER_LTE = 4;
+
+       /**
+         * FEATURE_TYPE_UT_OVER_WIFI supports features defined in 3GPP and
+         * GSMA IR.92 over WiFi.
+         */
+        public static final int FEATURE_TYPE_UT_OVER_WIFI = 5;
+    }
+
+    /**
+    * Defines IMS service/capability parameters.
+    */
+    public static class ConfigConstants {
+
+        // Define IMS config items
+        public static final int CONFIG_START = 0;
+
+        // Define operator provisioned config items
+        public static final int PROVISIONED_CONFIG_START = CONFIG_START;
+
+        /**
+         * AMR CODEC Mode Value set, 0-7 in comma separated sequence.
+         * Value is in String format.
+         */
+        public static final int VOCODER_AMRMODESET = CONFIG_START;
+
+        /**
+         * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
+         * Value is in String format.
+         */
+        public static final int VOCODER_AMRWBMODESET = 1;
+
+        /**
+         * SIP Session Timer value (seconds).
+         * Value is in Integer format.
+         */
+        public static final int SIP_SESSION_TIMER = 2;
+
+        /**
+         * Minimum SIP Session Expiration Timer in (seconds).
+         * Value is in Integer format.
+         */
+        public static final int MIN_SE = 3;
+
+        /**
+         * SIP_INVITE cancellation time out value (in milliseconds). Integer format.
+         * Value is in Integer format.
+         */
+        public static final int CANCELLATION_TIMER = 4;
+
+        /**
+         * Delay time when an iRAT transition from eHRPD/HRPD/1xRTT to LTE.
+         * Value is in Integer format.
+         */
+        public static final int TDELAY = 5;
+
+        /**
+         * Silent redial status of Enabled (True), or Disabled (False).
+         * Value is in Integer format.
+         */
+        public static final int SILENT_REDIAL_ENABLE = 6;
+
+        /**
+         * SIP T1 timer value in milliseconds. See RFC 3261 for define.
+         * Value is in Integer format.
+         */
+        public static final int SIP_T1_TIMER = 7;
+
+        /**
+         * SIP T2 timer value in milliseconds.  See RFC 3261 for define.
+         * Value is in Integer format.
+         */
+        public static final int SIP_T2_TIMER  = 8;
+
+         /**
+         * SIP TF timer value in milliseconds.  See RFC 3261 for define.
+         * Value is in Integer format.
+         */
+        public static final int SIP_TF_TIMER = 9;
+
+        /**
+         * VoLTE status for VLT/s status of Enabled (1), or Disabled (0).
+         * Value is in Integer format.
+         */
+        public static final int VLT_SETTING_ENABLED = 10;
+
+        /**
+         * VoLTE status for LVC/s status of Enabled (1), or Disabled (0).
+         * Value is in Integer format.
+         */
+        public static final int LVC_SETTING_ENABLED = 11;
+        /**
+         * Domain Name for the device to populate the request URI for REGISTRATION.
+         * Value is in String format.
+         */
+        public static final int DOMAIN_NAME = 12;
+         /**
+         * Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
+         * Value is in Integer format. 3GPP2(0), 3GPP(1)
+         */
+        public static final int SMS_FORMAT = 13;
+         /**
+         * Turns IMS ON/OFF on the device.
+         * Value is in Integer format. ON (1), OFF(0).
+         */
+        public static final int SMS_OVER_IP = 14;
+        /**
+         * Requested expiration for Published Online availability.
+         * Value is in Integer format.
+         */
+        public static final int PUBLISH_TIMER = 15;
+        /**
+         * Requested expiration for Published Offline availability.
+         * Value is in Integer format.
+         */
+        public static final int PUBLISH_TIMER_EXTENDED = 16;
+        /**
+         *
+         * Value is in Integer format.
+         */
+        public static final int CAPABILITY_DISCOVERY_ENABLED = 17;
+        /**
+         * Period of time the capability information of the  contact is cached on handset.
+         * Value is in Integer format.
+         */
+        public static final int CAPABILITIES_CACHE_EXPIRATION = 18;
+        /**
+         * Peiod of time the availability information of a contact is cached on device.
+         * Value is in Integer format.
+         */
+        public static final int AVAILABILITY_CACHE_EXPIRATION = 19;
+        /**
+         * Interval between successive capabilities polling.
+         * Value is in Integer format.
+         */
+        public static final int CAPABILITIES_POLL_INTERVAL = 20;
+        /**
+         * Minimum time between two published messages from the device.
+         * Value is in Integer format.
+         */
+        public static final int SOURCE_THROTTLE_PUBLISH = 21;
+        /**
+         * The Maximum number of MDNs contained in one Request Contained List.
+         * Value is in Integer format.
+         */
+        public static final int MAX_NUMENTRIES_IN_RCL = 22;
+        /**
+         * Expiration timer for subscription of a Request Contained List, used in capability
+         * polling.
+         * Value is in Integer format.
+         */
+        public static final int CAPAB_POLL_LIST_SUB_EXP = 23;
+        /**
+         * Applies compression to LIST Subscription.
+         * Value is in Integer format. Enable (1), Disable(0).
+         */
+        public static final int GZIP_FLAG = 24;
+        /**
+         * VOLTE Status for EAB/s status of Enabled (1), or Disabled (0).
+         * Value is in Integer format.
+         */
+        public static final int EAB_SETTING_ENABLED = 25;
+        /**
+         * Wi-Fi calling roaming status.
+         * Value is in Integer format. ON (1), OFF(0).
+         */
+        public static final int VOICE_OVER_WIFI_ROAMING = 26;
+        /**
+         * Wi-Fi calling modem - WfcModeFeatureValueConstants.
+         * Value is in Integer format.
+         */
+        public static final int VOICE_OVER_WIFI_MODE = 27;
+        /**
+         * VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0).
+         * Value is in Integer format.
+         */
+        public static final int VOICE_OVER_WIFI_SETTING_ENABLED = 28;
+        /**
+         * Mobile data enabled.
+         * Value is in Integer format. On (1), OFF(0).
+         */
+        public static final int MOBILE_DATA_ENABLED = 29;
+        /**
+         * VoLTE user opted in status.
+         * Value is in Integer format. Opted-in (1) Opted-out (0).
+         */
+        public static final int VOLTE_USER_OPT_IN_STATUS = 30;
+        /**
+         * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
+         * Value is in String format.
+         */
+        public static final int LBO_PCSCF_ADDRESS = 31;
+        /**
+         * Keep Alive Enabled for SIP.
+         * Value is in Integer format. On(1), OFF(0).
+         */
+        public static final int KEEP_ALIVE_ENABLED = 32;
+        /**
+         * Registration retry Base Time value in seconds.
+         * Value is in Integer format.
+         */
+        public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+        /**
+         * Registration retry Max Time value in seconds.
+         * Value is in Integer format.
+         */
+        public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+        /**
+         * Smallest RTP port for speech codec.
+         * Value is in integer format.
+         */
+        public static final int SPEECH_START_PORT = 35;
+        /**
+         * Largest RTP port for speech code.
+         * Value is in Integer format.
+         */
+        public static final int SPEECH_END_PORT = 36;
+        /**
+         * SIP Timer A's value in milliseconds. Timer A is the INVITE request
+         * retransmit interval, for UDP only.
+         * Value is in Integer format.
+         */
+        public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 37;
+        /**
+         * SIP Timer B's value in milliseconds. Timer B is the wait time for
+         * INVITE message to be acknowledged.
+         * Value is in Integer format.
+         */
+        public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 38;
+        /**
+         * SIP Timer D's value in milliseconds. Timer D is the wait time for
+         * response retransmits of the invite client transactions.
+         * Value is in Integer format.
+         */
+        public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 39;
+        /**
+         * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE
+         * request retransmit interval, for UDP only.
+         * Value is in Integer format.
+         */
+        public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 40;
+        /**
+         * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction
+         * timeout timer.
+         * Value is in Integer format.
+         */
+        public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 41;
+        /**
+         * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
+         * retransmit interval.
+         * Value is in Integer format.
+         */
+        public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 42;
+        /**
+         * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
+         * ACK receipt.
+         * Value is in Integer format.
+         */
+        public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 43;
+        /**
+         * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
+         * ACK retransmits.
+         * Value is in Integer format.
+         */
+        public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 44;
+        /**
+         * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
+         * non-invite request retransmission.
+         * Value is in Integer format.
+         */
+        public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 45;
+        /**
+         * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
+         * non-invite response retransmits.
+         * Value is in Integer format.
+         */
+        public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 46;
+        /**
+         * AMR WB octet aligned dynamic payload type.
+         * Value is in Integer format.
+         */
+        public static final int AMR_WB_OCTET_ALIGNED_PT = 47;
+        /**
+         * AMR WB bandwidth efficient payload type.
+         * Value is in Integer format.
+         */
+        public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 48;
+        /**
+         * AMR octet aligned dynamic payload type.
+         * Value is in Integer format.
+         */
+        public static final int AMR_OCTET_ALIGNED_PT = 49;
+        /**
+         * AMR bandwidth efficient payload type.
+         * Value is in Integer format.
+         */
+        public static final int AMR_BANDWIDTH_EFFICIENT_PT = 50;
+        /**
+         * DTMF WB payload type.
+         * Value is in Integer format.
+         */
+        public static final int DTMF_WB_PT = 51;
+        /**
+         * DTMF NB payload type.
+         * Value is in Integer format.
+         */
+        public static final int DTMF_NB_PT = 52;
+        /**
+         * AMR Default encoding mode.
+         * Value is in Integer format.
+         */
+        public static final int AMR_DEFAULT_MODE = 53;
+        /**
+         * SMS Public Service Identity.
+         * Value is in String format.
+         */
+        public static final int SMS_PSI = 54;
+        /**
+         * Video Quality - VideoQualityFeatureValuesConstants.
+         * Value is in Integer format.
+         */
+        public static final int VIDEO_QUALITY = 55;
+        /**
+         * LTE threshold.
+         * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+         */
+        public static final int TH_LTE1 = 56;
+        /**
+         * LTE threshold.
+         * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+         */
+        public static final int TH_LTE2 = 57;
+        /**
+         * LTE threshold.
+         * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+         */
+        public static final int TH_LTE3 = 58;
+        /**
+         * 1x threshold.
+         * Handover from 1x to WiFi if 1x < TH1x
+         */
+        public static final int TH_1x = 59;
+        /**
+         * WiFi threshold.
+         * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+         */
+        public static final int VOWT_A = 60;
+        /**
+         * WiFi threshold.
+         * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+         */
+        public static final int VOWT_B = 61;
+        /**
+         * LTE ePDG timer.
+         * Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+         */
+        public static final int T_EPDG_LTE = 62;
+        /**
+         * WiFi ePDG timer.
+         * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+         */
+        public static final int T_EPDG_WIFI = 63;
+        /**
+         * 1x ePDG timer.
+         * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+         */
+        public static final int T_EPDG_1X = 64;
+        /**
+         * MultiEndpoint status: Enabled (1), or Disabled (0).
+         * Value is in Integer format.
+         */
+        public static final int VICE_SETTING_ENABLED = 65;
+
+        // Expand the operator config items as needed here, need to change
+        // PROVISIONED_CONFIG_END after that.
+        public static final int PROVISIONED_CONFIG_END = VICE_SETTING_ENABLED;
+
+        // Expand the operator config items as needed here.
+    }
+
+    /**
+    * Defines IMS set operation status.
+    */
+    public static class OperationStatusConstants {
+        public static final int UNKNOWN = -1;
+        public static final int SUCCESS = 0;
+        public static final int FAILED =  1;
+        public static final int UNSUPPORTED_CAUSE_NONE = 2;
+        public static final int UNSUPPORTED_CAUSE_RAT = 3;
+        public static final int UNSUPPORTED_CAUSE_DISABLED = 4;
+    }
+
+    /**
+     * Defines IMS get operation values.
+     */
+    public static class OperationValuesConstants {
+        /**
+         * Values related to Video Quality
+         */
+        public static final int VIDEO_QUALITY_UNKNOWN = -1;
+        public static final int VIDEO_QUALITY_LOW = 0;
+        public static final int VIDEO_QUALITY_HIGH = 1;
+    }
+
+    /**
+     * Defines IMS video quality feature value.
+     */
+    public static class VideoQualityFeatureValuesConstants {
+        public static final int LOW = 0;
+        public static final int HIGH = 1;
+    }
+
+   /**
+    * Defines IMS feature value.
+    */
+    public static class FeatureValueConstants {
+        public static final int OFF = 0;
+        public static final int ON = 1;
+    }
+
+    /**
+     * Defines IMS feature value.
+     */
+    public static class WfcModeFeatureValueConstants {
+        public static final int WIFI_ONLY = 0;
+        public static final int CELLULAR_PREFERRED = 1;
+        public static final int WIFI_PREFERRED = 2;
+    }
+
+    public ImsConfig(IImsConfig iconfig, Context context) {
+        if (DBG) Rlog.d(TAG, "ImsConfig creates");
+        miConfig = iconfig;
+        mContext = context;
+    }
+
+    /**
+     * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
+     * This function should not be called from the mainthread as it could block the
+     * mainthread.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return the value in Integer format.
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+    public int getProvisionedValue(int item) throws ImsException {
+        int ret = 0;
+        try {
+            ret = miConfig.getProvisionedValue(item);
+        }  catch (RemoteException e) {
+            throw new ImsException("getValue()", e,
+                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+        }
+        if (DBG) Rlog.d(TAG, "getProvisionedValue(): item = " + item + ", ret =" + ret);
+
+        return ret;
+    }
+
+    /**
+     * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
+     * This function should not be called from the mainthread as it could block the
+     * mainthread.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in String format.
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+    public String getProvisionedStringValue(int item) throws ImsException {
+        String ret = "Unknown";
+        try {
+            ret = miConfig.getProvisionedStringValue(item);
+        }  catch (RemoteException e) {
+            throw new ImsException("getProvisionedStringValue()", e,
+                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+        }
+        if (DBG) Rlog.d(TAG, "getProvisionedStringValue(): item = " + item + ", ret =" + ret);
+
+        return ret;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by
+     * the operator device management entity.
+     * This function should not be called from main thread as it could block
+     * mainthread.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+    public int setProvisionedValue(int item, int value)
+            throws ImsException {
+        int ret = OperationStatusConstants.UNKNOWN;
+        if (DBG) {
+            Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+                    "value = " + value);
+        }
+        try {
+            ret = miConfig.setProvisionedValue(item, value);
+        }  catch (RemoteException e) {
+            throw new ImsException("setProvisionedValue()", e,
+                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+        }
+        if (DBG) {
+            Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+                    " value = " + value + " ret = " + ret);
+        }
+        return ret;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by
+     * the operator device management entity.
+     * This function should not be called from main thread as it could block
+     * mainthread.
+     *
+     * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+    public int setProvisionedStringValue(int item, String value)
+            throws ImsException {
+        int ret = OperationStatusConstants.UNKNOWN;
+        try {
+            ret = miConfig.setProvisionedStringValue(item, value);
+        }  catch (RemoteException e) {
+            throw new ImsException("setProvisionedStringValue()", e,
+                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+        }
+        if (DBG) {
+            Rlog.d(TAG, "setProvisionedStringValue(): item = " + item +
+                    ", value =" + value);
+        }
+        return ret;
+    }
+
+    /**
+     * Gets the value for IMS feature item for specified network type.
+     *
+     * @param feature, defined as in FeatureConstants.
+     * @param network, defined as in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param listener, provided to be notified for the feature on/off status.
+     * @return void
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+    public void getFeatureValue(int feature, int network,
+            ImsConfigListener listener) throws ImsException {
+        if (DBG) {
+            Rlog.d(TAG, "getFeatureValue: feature = " + feature + ", network =" + network +
+                    ", listener =" + listener);
+        }
+        try {
+            miConfig.getFeatureValue(feature, network, listener);
+        } catch (RemoteException e) {
+            throw new ImsException("getFeatureValue()", e,
+                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+        }
+    }
+
+    /**
+     * Sets the value for IMS feature item for specified network type.
+     *
+     * @param feature, as defined in FeatureConstants.
+     * @param network, as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+     * @param value, as defined in FeatureValueConstants.
+     * @param listener, provided if caller needs to be notified for set result.
+     * @return void
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+    public void setFeatureValue(int feature, int network, int value,
+            ImsConfigListener listener) throws ImsException {
+        if (DBG) {
+            Rlog.d(TAG, "setFeatureValue: feature = " + feature + ", network =" + network +
+                    ", value =" + value + ", listener =" + listener);
+        }
+        try {
+            miConfig.setFeatureValue(feature, network, value, listener);
+        } catch (RemoteException e) {
+            throw new ImsException("setFeatureValue()", e,
+                    ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+        }
+    }
+}
diff --git a/telephony/java/com/android/ims/ImsException.java b/telephony/java/com/android/ims/ImsException.java
new file mode 100644
index 0000000..74b20f4
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 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.ims;
+
+/**
+ * This class defines a general IMS-related exception.
+ *
+ * @hide
+ */
+public class ImsException extends Exception {
+
+    /**
+     * Refer to CODE_LOCAL_* in {@link ImsReasonInfo}
+     */
+    private int mCode;
+
+    public ImsException() {
+    }
+
+    public ImsException(String message, int code) {
+        super(message + ", code = " + code);
+        mCode = code;
+    }
+
+    public ImsException(String message, Throwable cause, int code) {
+        super(message, cause);
+        mCode = code;
+    }
+
+    /**
+     * Gets the detailed exception code when ImsException is throwed
+     *
+     * @return the exception code in {@link ImsReasonInfo}
+     */
+    public int getCode() {
+        return mCode;
+    }
+}
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
new file mode 100644
index 0000000..5984e78
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2013 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.ims;
+
+import android.os.Message;
+
+/**
+ * Provides APIs for the supplementary service settings using IMS (Ut interface).
+ * It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol)
+ * over the Ut interface for manipulating supplementary services).
+ *
+ * @hide
+ */
+public interface ImsUtInterface {
+    /**
+     * Actions
+     * @hide
+     */
+    public static final int ACTION_DEACTIVATION = 0;
+    public static final int ACTION_ACTIVATION = 1;
+    public static final int ACTION_REGISTRATION = 3;
+    public static final int ACTION_ERASURE = 4;
+    public static final int ACTION_INTERROGATION = 5;
+
+    /**
+     * OIR (Originating Identification Restriction, 3GPP TS 24.607)
+     * OIP (Originating Identification Presentation, 3GPP TS 24.607)
+     * TIR (Terminating Identification Restriction, 3GPP TS 24.608)
+     * TIP (Terminating Identification Presentation, 3GPP TS 24.608)
+     */
+    public static final int OIR_DEFAULT = 0;    // "user subscription default value"
+    public static final int OIR_PRESENTATION_RESTRICTED = 1;
+    public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+
+    /**
+     * CW (Communication Waiting, 3GPP TS 24.615)
+     */
+
+    /**
+     * CDIV (Communication Diversion, 3GPP TS 24.604)
+     *     actions: target, no reply timer
+     */
+    public static final int CDIV_CF_UNCONDITIONAL = 0;
+    public static final int CDIV_CF_BUSY = 1;
+    public static final int CDIV_CF_NO_REPLY = 2;
+    public static final int CDIV_CF_NOT_REACHABLE = 3;
+    // For CS service code: 002
+    public static final int CDIV_CF_ALL = 4;
+    // For CS service code: 004
+    public static final int CDIV_CF_ALL_CONDITIONAL = 5;
+    // It's only supported in the IMS service (CS does not define it).
+    // IR.92 recommends that an UE activates both the CFNRc and the CFNL
+    // (CDIV using condition not-registered) to the same target.
+    public static final int CDIV_CF_NOT_LOGGED_IN = 6;
+
+    /**
+     * CB (Communication Barring, 3GPP TS 24.611)
+     */
+    // Barring of All Incoming Calls
+    public static final int CB_BAIC = 1;
+    // Barring of All Outgoing Calls
+    public static final int CB_BAOC = 2;
+    // Barring of Outgoing International Calls
+    public static final int CB_BOIC = 3;
+    // Barring of Outgoing International Calls - excluding Home Country
+    public static final int CB_BOIC_EXHC = 4;
+    // Barring of Incoming Calls - when roaming
+    public static final int CB_BIC_WR = 5;
+    // Barring of Anonymous Communication Rejection (ACR) - a particular case of ICB service
+    public static final int CB_BIC_ACR = 6;
+    // Barring of All Calls
+    public static final int CB_BA_ALL = 7;
+    // Barring of Outgoing Services (Service Code 333 - 3GPP TS 22.030 Table B-1)
+    public static final int CB_BA_MO = 8;
+    // Barring of Incoming Services (Service Code 353 - 3GPP TS 22.030 Table B-1)
+    public static final int CB_BA_MT = 9;
+    // Barring of Specific Incoming calls
+    public static final int CB_BS_MT = 10;
+
+    /**
+     * Invalid result value.
+     */
+    public static final int INVALID = (-1);
+
+
+
+    /**
+     * Operations for the supplementary service configuration
+     */
+
+    /**
+     * Retrieves the configuration of the call barring.
+     * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+     */
+    public void queryCallBarring(int cbType, Message result);
+
+    /**
+     * Retrieves the configuration of the call forward.
+     * The return value of ((AsyncResult)result.obj) is an array of {@link ImsCallForwardInfo}.
+     */
+    public void queryCallForward(int condition, String number, Message result);
+
+    /**
+     * Retrieves the configuration of the call waiting.
+     * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+     */
+    public void queryCallWaiting(Message result);
+
+    /**
+     * Retrieves the default CLIR setting.
+     */
+    public void queryCLIR(Message result);
+
+    /**
+     * Retrieves the CLIP call setting.
+     */
+    public void queryCLIP(Message result);
+
+    /**
+     * Retrieves the COLR call setting.
+     */
+    public void queryCOLR(Message result);
+
+    /**
+     * Retrieves the COLP call setting.
+     */
+    public void queryCOLP(Message result);
+
+    /**
+     * Modifies the configuration of the call barring.
+     */
+    public void updateCallBarring(int cbType, int action,
+            Message result, String[] barrList);
+
+    /**
+     * Modifies the configuration of the call forward.
+     */
+    public void updateCallForward(int action, int condition, String number,
+            int serviceClass, int timeSeconds, Message result);
+
+    /**
+     * Modifies the configuration of the call waiting.
+     */
+    public void updateCallWaiting(boolean enable, int serviceClass, Message result);
+
+    /**
+     * Updates the configuration of the CLIR supplementary service.
+     */
+    public void updateCLIR(int clirMode, Message result);
+
+    /**
+     * Updates the configuration of the CLIP supplementary service.
+     */
+    public void updateCLIP(boolean enable, Message result);
+
+    /**
+     * Updates the configuration of the COLR supplementary service.
+     */
+    public void updateCOLR(int presentation, Message result);
+
+    /**
+     * Updates the configuration of the COLP supplementary service.
+     */
+    public void updateCOLP(boolean enable, Message result);
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
index 82a13dc..df10700 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
@@ -17,7 +17,8 @@
 package com.android.ims.internal;
 
 /**
-*  Interface from ImsResolver to ImsServiceProxy in ImsManager.
+ *  Interface from ImsResolver to ImsServiceProxy in ImsManager.
+ * Callback to ImsManager when a feature changes in the ImsServiceController.
  * {@hide}
  */
 oneway interface IImsServiceFeatureListener {
diff --git a/telephony/java/com/android/ims/internal/ImsCallSession.java b/telephony/java/com/android/ims/internal/ImsCallSession.java
new file mode 100644
index 0000000..8196b23
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/ImsCallSession.java
@@ -0,0 +1,1290 @@
+/*
+ * Copyright (c) 2013 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.ims.internal;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import java.util.Objects;
+
+import android.telephony.ims.stub.ImsCallSessionListenerImplBase;
+import android.util.Log;
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+
+/**
+ * Provides the call initiation/termination, and media exchange between two IMS endpoints.
+ * It directly communicates with IMS service which implements the IMS protocol behavior.
+ *
+ * @hide
+ */
+public class ImsCallSession {
+    private static final String TAG = "ImsCallSession";
+
+    /**
+     * Defines IMS call session state.
+     */
+    public static class State {
+        public static final int IDLE = 0;
+        public static final int INITIATED = 1;
+        public static final int NEGOTIATING = 2;
+        public static final int ESTABLISHING = 3;
+        public static final int ESTABLISHED = 4;
+
+        public static final int RENEGOTIATING = 5;
+        public static final int REESTABLISHING = 6;
+
+        public static final int TERMINATING = 7;
+        public static final int TERMINATED = 8;
+
+        public static final int INVALID = (-1);
+
+        /**
+         * Converts the state to string.
+         */
+        public static String toString(int state) {
+            switch (state) {
+                case IDLE:
+                    return "IDLE";
+                case INITIATED:
+                    return "INITIATED";
+                case NEGOTIATING:
+                    return "NEGOTIATING";
+                case ESTABLISHING:
+                    return "ESTABLISHING";
+                case ESTABLISHED:
+                    return "ESTABLISHED";
+                case RENEGOTIATING:
+                    return "RENEGOTIATING";
+                case REESTABLISHING:
+                    return "REESTABLISHING";
+                case TERMINATING:
+                    return "TERMINATING";
+                case TERMINATED:
+                    return "TERMINATED";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        private State() {
+        }
+    }
+
+    /**
+     * Listener for events relating to an IMS session, such as when a session is being
+     * recieved ("on ringing") or a call is outgoing ("on calling").
+     * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
+     */
+    public static class Listener {
+        /**
+         * Called when a request is sent out to initiate a new session
+         * and 1xx response is received from the network.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionProgressing(ImsCallSession session,
+                ImsStreamMediaProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session is established.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionStarted(ImsCallSession session,
+                ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session establishment is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session establishment failure
+         */
+        public void callSessionStartFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is terminated.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session termination
+         */
+        public void callSessionTerminated(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is in hold.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionHeld(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session hold is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session hold failure
+         */
+        public void callSessionHoldFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session hold is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionHoldReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session resume is done.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionResumed(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session resume is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session resume failure
+         */
+        public void callSessionResumeFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session resume is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionResumeReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session merge has been started.  At this point, the {@code newSession}
+         * represents the session which has been initiated to the IMS conference server for the
+         * new merged conference.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param newSession the session object that is merged with an active & hold session
+         */
+        public void callSessionMergeStarted(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session merge is successful and the merged session is active.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionMergeComplete(ImsCallSession session) {
+        }
+
+        /**
+         * Called when the session merge has failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the call merge failure
+         */
+        public void callSessionMergeFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session is updated (except for hold/unhold).
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionUpdated(ImsCallSession session,
+                ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the session update is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the session update failure
+         */
+        public void callSessionUpdateFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the session update is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionUpdateReceived(ImsCallSession session,
+                ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session is extended to the conference session.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param newSession the session object that is extended to the conference
+         *      from the active session
+         */
+        public void callSessionConferenceExtended(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+        }
+
+        /**
+         * Called when the conference extension is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference extension failure
+         */
+        public void callSessionConferenceExtendFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+        }
+
+        /**
+         * Called when the conference extension is received from the remote user.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionConferenceExtendReceived(ImsCallSession session,
+                ImsCallSession newSession, ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the invitation request of the participants is delivered to the conference
+         * server.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
+            // no-op
+        }
+
+        /**
+         * Called when the invitation request of the participants is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference invitation failure
+         */
+        public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when the removal request of the participants is delivered to the conference
+         * server.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
+            // no-op
+        }
+
+        /**
+         * Called when the removal request of the participants is failed.
+         *
+         * @param session the session object that carries out the IMS session
+         * @param reasonInfo detailed reason of the conference removal failure
+         */
+        public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when the conference state is updated.
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionConferenceStateUpdated(ImsCallSession session,
+                ImsConferenceState state) {
+            // no-op
+        }
+
+        /**
+         * Called when the USSD message is received from the network.
+         *
+         * @param mode mode of the USSD message (REQUEST / NOTIFY)
+         * @param ussdMessage USSD message
+         */
+        public void callSessionUssdMessageReceived(ImsCallSession session,
+                int mode, String ussdMessage) {
+            // no-op
+        }
+
+        /**
+         * Called when session access technology changes
+         *
+         * @param session IMS session object
+         * @param srcAccessTech original access technology
+         * @param targetAccessTech new access technology
+         * @param reasonInfo
+         */
+        public void callSessionHandover(ImsCallSession session,
+                                 int srcAccessTech, int targetAccessTech,
+                                 ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when session access technology change fails
+         *
+         * @param session IMS session object
+         * @param srcAccessTech original access technology
+         * @param targetAccessTech new access technology
+         * @param reasonInfo handover failure reason
+         */
+        public void callSessionHandoverFailed(ImsCallSession session,
+                                       int srcAccessTech, int targetAccessTech,
+                                       ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when TTY mode of remote party changed
+         *
+         * @param session IMS session object
+         * @param mode TTY mode of remote party
+         */
+        public void callSessionTtyModeReceived(ImsCallSession session,
+                                       int mode) {
+            // no-op
+        }
+
+        /**
+         * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+         *
+         * @param session The call session.
+         * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+         *      otherwise.
+         */
+        public void callSessionMultipartyStateChanged(ImsCallSession session,
+                boolean isMultiParty) {
+            // no-op
+        }
+
+        /**
+         * Called when the session supplementary service is received
+         *
+         * @param session the session object that carries out the IMS session
+         */
+        public void callSessionSuppServiceReceived(ImsCallSession session,
+                ImsSuppServiceNotification suppServiceInfo) {
+        }
+    }
+
+    private final IImsCallSession miSession;
+    private boolean mClosed = false;
+    private Listener mListener;
+
+    public ImsCallSession(IImsCallSession iSession) {
+        miSession = iSession;
+
+        if (iSession != null) {
+            try {
+                iSession.setListener(new IImsCallSessionListenerProxy());
+            } catch (RemoteException e) {
+            }
+        } else {
+            mClosed = true;
+        }
+    }
+
+    public ImsCallSession(IImsCallSession iSession, Listener listener) {
+        this(iSession);
+        setListener(listener);
+    }
+
+    /**
+     * Closes this object. This object is not usable after being closed.
+     */
+    public synchronized void close() {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.close();
+            mClosed = true;
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Gets the call ID of the session.
+     *
+     * @return the call ID
+     */
+    public String getCallId() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getCallId();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the call profile that this session is associated with
+     *
+     * @return the call profile that this session is associated with
+     */
+    public ImsCallProfile getCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the local call profile that this session is associated with
+     *
+     * @return the local call profile that this session is associated with
+     */
+    public ImsCallProfile getLocalCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getLocalCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the remote call profile that this session is associated with
+     *
+     * @return the remote call profile that this session is associated with
+     */
+    public ImsCallProfile getRemoteCallProfile() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getRemoteCallProfile();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the video call provider for the session.
+     *
+     * @return The video call provider.
+     */
+    public IImsVideoCallProvider getVideoCallProvider() {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getVideoCallProvider();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the value associated with the specified property of this session.
+     *
+     * @return the string value associated with the specified property
+     */
+    public String getProperty(String name) {
+        if (mClosed) {
+            return null;
+        }
+
+        try {
+            return miSession.getProperty(name);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the session state.
+     * The value returned must be one of the states in {@link State}.
+     *
+     * @return the session state
+     */
+    public int getState() {
+        if (mClosed) {
+            return State.INVALID;
+        }
+
+        try {
+            return miSession.getState();
+        } catch (RemoteException e) {
+            return State.INVALID;
+        }
+    }
+
+    /**
+     * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or
+     * closed state).
+     *
+     * @return {@code True} if the session is alive.
+     */
+    public boolean isAlive() {
+        if (mClosed) {
+            return false;
+        }
+
+        int state = getState();
+        switch (state) {
+            case State.IDLE:
+            case State.INITIATED:
+            case State.NEGOTIATING:
+            case State.ESTABLISHING:
+            case State.ESTABLISHED:
+            case State.RENEGOTIATING:
+            case State.REESTABLISHING:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Gets the native IMS call session.
+     * @hide
+     */
+    public IImsCallSession getSession() {
+        return miSession;
+    }
+
+    /**
+     * Checks if the session is in call.
+     *
+     * @return true if the session is in call
+     */
+    public boolean isInCall() {
+        if (mClosed) {
+            return false;
+        }
+
+        try {
+            return miSession.isInCall();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Sets the listener to listen to the session events. A {@link ImsCallSession}
+     * can only hold one listener at a time. Subsequent calls to this method
+     * override the previous listener.
+     *
+     * @param listener to listen to the session events of this object
+     */
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Mutes or unmutes the mic for the active call.
+     *
+     * @param muted true if the call is muted, false otherwise
+     */
+    public void setMute(boolean muted) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.setMute(muted);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Initiates an IMS call with the specified target and call profile.
+     * The session listener is called back upon defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param callee dialed string to make the call to
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+     */
+    public void start(String callee, ImsCallProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.start(callee, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Initiates an IMS conference call with the specified target and call profile.
+     * The session listener is called back upon defined session events.
+     * The method is only valid to call when the session state is in
+     * {@link ImsCallSession.State#IDLE}.
+     *
+     * @param participants participant list to initiate an IMS conference call
+     * @param profile call profile to make the call with the specified service type,
+     *      call type and media information
+     * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+     */
+    public void start(String[] participants, ImsCallProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.startConference(participants, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Accepts an incoming call or session update.
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be answered
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+     * @see Listener#callSessionStarted
+     */
+    public void accept(int callType, ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.accept(callType, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Rejects an incoming call or session update.
+     *
+     * @param reason reason code to reject an incoming call
+     * @see Listener#callSessionStartFailed
+     */
+    public void reject(int reason) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.reject(reason);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Terminates a call.
+     *
+     * @see Listener#callSessionTerminated
+     */
+    public void terminate(int reason) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.terminate(reason);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+     * @see Listener#callSessionHeld, Listener#callSessionHoldFailed
+     */
+    public void hold(ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.hold(profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Continues a call that's on hold. When it succeeds,
+     * {@link Listener#callSessionResumed} is called.
+     *
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call
+     * @see Listener#callSessionResumed, Listener#callSessionResumeFailed
+     */
+    public void resume(ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.resume(profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Merges the active & hold call. When it succeeds,
+     * {@link Listener#callSessionMergeStarted} is called.
+     *
+     * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed
+     */
+    public void merge() {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.merge();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+     *
+     * @param callType call type specified in {@link ImsCallProfile} to be updated
+     * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+     * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed
+     */
+    public void update(int callType, ImsStreamMediaProfile profile) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.update(callType, profile);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Extends this call to the conference call with the specified recipients.
+     *
+     * @param participants list to be invited to the conference call after extending the call
+     * @see Listener#callSessionConferenceExtended
+     * @see Listener#callSessionConferenceExtendFailed
+     */
+    public void extendToConference(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.extendToConference(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Requests the conference server to invite an additional participants to the conference.
+     *
+     * @param participants list to be invited to the conference call
+     * @see Listener#callSessionInviteParticipantsRequestDelivered
+     * @see Listener#callSessionInviteParticipantsRequestFailed
+     */
+    public void inviteParticipants(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.inviteParticipants(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Requests the conference server to remove the specified participants from the conference.
+     *
+     * @param participants participant list to be removed from the conference call
+     * @see Listener#callSessionRemoveParticipantsRequestDelivered
+     * @see Listener#callSessionRemoveParticipantsRequestFailed
+     */
+    public void removeParticipants(String[] participants) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.removeParticipants(participants);
+        } catch (RemoteException e) {
+        }
+    }
+
+
+    /**
+     * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    public void sendDtmf(char c, Message result) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendDtmf(c, result);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+     * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+     * and event flash to 16. Currently, event flash is not supported.
+     *
+     * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+     */
+    public void startDtmf(char c) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.startDtmf(c);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Stops a DTMF code.
+     */
+    public void stopDtmf() {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.stopDtmf();
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Sends an USSD message.
+     *
+     * @param ussdMessage USSD message to send
+     */
+    public void sendUssd(String ussdMessage) {
+        if (mClosed) {
+            return;
+        }
+
+        try {
+            miSession.sendUssd(ussdMessage);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Determines if the session is multiparty.
+     *
+     * @return {@code True} if the session is multiparty.
+     */
+    public boolean isMultiparty() {
+        if (mClosed) {
+            return false;
+        }
+
+        try {
+            return miSession.isMultiparty();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * A listener type for receiving notification on IMS call session events.
+     * When an event is generated for an {@link IImsCallSession},
+     * the application is notified by having one of the methods called on
+     * the {@link IImsCallSessionListener}.
+     */
+    private class IImsCallSessionListenerProxy extends ImsCallSessionListenerImplBase {
+        /**
+         * Notifies the result of the basic session operation (setup / terminate).
+         */
+        @Override
+        public void callSessionProgressing(IImsCallSession session,
+                ImsStreamMediaProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionProgressing(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionStarted(IImsCallSession session,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionStarted(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionStartFailed(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionTerminated(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the result of the call hold/resume operation.
+         */
+        @Override
+        public void callSessionHeld(IImsCallSession session,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionHeld(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionHoldFailed(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionHoldReceived(IImsCallSession session,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionResumed(IImsCallSession session,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionResumed(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionResumeFailed(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionResumeReceived(IImsCallSession session,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        /**
+         * Notifies the start of a call merge operation.
+         *
+         * @param session The call session.
+         * @param newSession The merged call session.
+         * @param profile The call profile.
+         */
+        @Override
+        public void callSessionMergeStarted(IImsCallSession session,
+                IImsCallSession newSession, ImsCallProfile profile) {
+            // This callback can be used for future use to add additional
+            // functionality that may be needed between conference start and complete
+            Log.d(TAG, "callSessionMergeStarted");
+        }
+
+        /**
+         * Notifies the successful completion of a call merge operation.
+         *
+         * @param newSession The call session.
+         */
+        @Override
+        public void callSessionMergeComplete(IImsCallSession newSession) {
+            if (mListener != null) {
+                if (newSession != null) {
+                    // Check if the active session is the same session that was
+                    // active before the merge request was sent.
+                    ImsCallSession validActiveSession = ImsCallSession.this;
+                    try {
+                        if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) {
+                            // New session created after conference
+                            validActiveSession = new ImsCallSession(newSession);
+                        }
+                    } catch (RemoteException rex) {
+                        Log.e(TAG, "callSessionMergeComplete: exception for getCallId!");
+                    }
+                    mListener.callSessionMergeComplete(validActiveSession);
+               } else {
+                   // Session already exists. Hence no need to pass
+                   mListener.callSessionMergeComplete(null);
+               }
+            }
+        }
+
+        /**
+         * Notifies of a failure to perform a call merge operation.
+         *
+         * @param session The call session.
+         * @param reasonInfo The merge failure reason.
+         */
+        @Override
+        public void callSessionMergeFailed(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the result of call upgrade / downgrade or any other call updates.
+         */
+        @Override
+        public void callSessionUpdated(IImsCallSession session,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionUpdated(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
+        public void callSessionUpdateFailed(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionUpdateReceived(IImsCallSession session,
+                ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+            }
+        }
+
+        /**
+         * Notifies the result of conference extension.
+         */
+        @Override
+        public void callSessionConferenceExtended(IImsCallSession session,
+                IImsCallSession newSession, ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtended(ImsCallSession.this,
+                        new ImsCallSession(newSession), profile);
+            }
+        }
+
+        @Override
+        public void callSessionConferenceExtendFailed(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionConferenceExtendReceived(IImsCallSession session,
+                IImsCallSession newSession, ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+                        new ImsCallSession(newSession), profile);
+            }
+        }
+
+        /**
+         * Notifies the result of the participant invitation / removal to/from
+         * the conference session.
+         */
+        @Override
+        public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+            if (mListener != null) {
+                mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
+            }
+        }
+
+        @Override
+        public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+                        reasonInfo);
+            }
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+            if (mListener != null) {
+                mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+            }
+        }
+
+        @Override
+        public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+                        reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the changes of the conference info. in the conference session.
+         */
+        @Override
+        public void callSessionConferenceStateUpdated(IImsCallSession session,
+                ImsConferenceState state) {
+            if (mListener != null) {
+                mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+            }
+        }
+
+        /**
+         * Notifies the incoming USSD message.
+         */
+        @Override
+        public void callSessionUssdMessageReceived(IImsCallSession session,
+                int mode, String ussdMessage) {
+            if (mListener != null) {
+                mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
+            }
+        }
+
+        /**
+         * Notifies of handover information for this call
+         */
+        @Override
+        public void callSessionHandover(IImsCallSession session,
+                                 int srcAccessTech, int targetAccessTech,
+                                 ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
+                        targetAccessTech, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies of handover failure info for this call
+         */
+        @Override
+        public void callSessionHandoverFailed(IImsCallSession session,
+                                       int srcAccessTech, int targetAccessTech,
+                                       ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
+                        targetAccessTech, reasonInfo);
+            }
+        }
+
+        /**
+         * Notifies the TTY mode received from remote party.
+         */
+        @Override
+        public void callSessionTtyModeReceived(IImsCallSession session,
+                int mode) {
+            if (mListener != null) {
+                mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+            }
+        }
+
+        /**
+         * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+         *
+         * @param session The call session.
+         * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+         *      otherwise.
+         */
+        public void callSessionMultipartyStateChanged(IImsCallSession session,
+                boolean isMultiParty) {
+
+            if (mListener != null) {
+                mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
+            }
+        }
+
+        @Override
+        public void callSessionSuppServiceReceived(IImsCallSession session,
+                ImsSuppServiceNotification suppServiceInfo ) {
+            if (mListener != null) {
+                mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
+            }
+        }
+
+    }
+
+    /**
+     * Provides a string representation of the {@link ImsCallSession}.  Primarily intended for
+     * use in log statements.
+     *
+     * @return String representation of session.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[ImsCallSession objId:");
+        sb.append(System.identityHashCode(this));
+        sb.append(" state:");
+        sb.append(State.toString(getState()));
+        sb.append(" callId:");
+        sb.append(getCallId());
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java b/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
new file mode 100644
index 0000000..432dc39
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.ims.internal;
+
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.Connection;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import android.view.Surface;
+
+import com.android.internal.os.SomeArgs;
+
+/**
+ * @hide
+ */
+public abstract class ImsVideoCallProvider {
+    private static final int MSG_SET_CALLBACK = 1;
+    private static final int MSG_SET_CAMERA = 2;
+    private static final int MSG_SET_PREVIEW_SURFACE = 3;
+    private static final int MSG_SET_DISPLAY_SURFACE = 4;
+    private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+    private static final int MSG_SET_ZOOM = 6;
+    private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+    private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+    private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+    private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+    private static final int MSG_SET_PAUSE_IMAGE = 11;
+
+    private final ImsVideoCallProviderBinder mBinder;
+
+    private IImsVideoCallCallback mCallback;
+
+    /**
+     * Default handler used to consolidate binder method calls onto a single thread.
+     */
+    private final Handler mProviderHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SET_CALLBACK:
+                    mCallback = (IImsVideoCallCallback) msg.obj;
+                    break;
+                case MSG_SET_CAMERA:
+                {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        onSetCamera((String) args.arg1);
+                        onSetCamera((String) args.arg1, args.argi1);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_SET_PREVIEW_SURFACE:
+                    onSetPreviewSurface((Surface) msg.obj);
+                    break;
+                case MSG_SET_DISPLAY_SURFACE:
+                    onSetDisplaySurface((Surface) msg.obj);
+                    break;
+                case MSG_SET_DEVICE_ORIENTATION:
+                    onSetDeviceOrientation(msg.arg1);
+                    break;
+                case MSG_SET_ZOOM:
+                    onSetZoom((Float) msg.obj);
+                    break;
+                case MSG_SEND_SESSION_MODIFY_REQUEST: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        VideoProfile fromProfile = (VideoProfile) args.arg1;
+                        VideoProfile toProfile = (VideoProfile) args.arg2;
+
+                        onSendSessionModifyRequest(fromProfile, toProfile);
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
+                case MSG_SEND_SESSION_MODIFY_RESPONSE:
+                    onSendSessionModifyResponse((VideoProfile) msg.obj);
+                    break;
+                case MSG_REQUEST_CAMERA_CAPABILITIES:
+                    onRequestCameraCapabilities();
+                    break;
+                case MSG_REQUEST_CALL_DATA_USAGE:
+                    onRequestCallDataUsage();
+                    break;
+                case MSG_SET_PAUSE_IMAGE:
+                    onSetPauseImage((Uri) msg.obj);
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    /**
+     * IImsVideoCallProvider stub implementation.
+     */
+    private final class ImsVideoCallProviderBinder extends IImsVideoCallProvider.Stub {
+        public void setCallback(IImsVideoCallCallback callback) {
+            mProviderHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget();
+        }
+
+        public void setCamera(String cameraId, int uid) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = cameraId;
+            args.argi1 = uid;
+            mProviderHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
+        }
+
+        public void setPreviewSurface(Surface surface) {
+            mProviderHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
+        }
+
+        public void setDisplaySurface(Surface surface) {
+            mProviderHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
+        }
+
+        public void setDeviceOrientation(int rotation) {
+            mProviderHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
+        }
+
+        public void setZoom(float value) {
+            mProviderHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
+        }
+
+        public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = fromProfile;
+            args.arg2 = toProfile;
+            mProviderHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
+        }
+
+        public void sendSessionModifyResponse(VideoProfile responseProfile) {
+            mProviderHandler.obtainMessage(
+                    MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
+        }
+
+        public void requestCameraCapabilities() {
+            mProviderHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
+        }
+
+        public void requestCallDataUsage() {
+            mProviderHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
+        }
+
+        public void setPauseImage(Uri uri) {
+            mProviderHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
+        }
+    }
+
+    public ImsVideoCallProvider() {
+        mBinder = new ImsVideoCallProviderBinder();
+    }
+
+    /**
+     * Returns binder object which can be used across IPC methods.
+     */
+    public final IImsVideoCallProvider getInterface() {
+        return mBinder;
+    }
+
+    /** @see Connection.VideoProvider#onSetCamera */
+    public abstract void onSetCamera(String cameraId);
+
+    /**
+     * Similar to {@link #onSetCamera(String)}, except includes the UID of the calling process which
+     * the IMS service uses when opening the camera.  This ensures camera permissions are verified
+     * by the camera service.
+     *
+     * @param cameraId The id of the camera to be opened.
+     * @param uid The uid of the caller, used when opening the camera for permission verification.
+     * @see Connection.VideoProvider#onSetCamera
+     */
+    public void onSetCamera(String cameraId, int uid) {
+    }
+
+    /** @see Connection.VideoProvider#onSetPreviewSurface */
+    public abstract void onSetPreviewSurface(Surface surface);
+
+    /** @see Connection.VideoProvider#onSetDisplaySurface */
+    public abstract void onSetDisplaySurface(Surface surface);
+
+    /** @see Connection.VideoProvider#onSetDeviceOrientation */
+    public abstract void onSetDeviceOrientation(int rotation);
+
+    /** @see Connection.VideoProvider#onSetZoom */
+    public abstract void onSetZoom(float value);
+
+    /** @see Connection.VideoProvider#onSendSessionModifyRequest */
+    public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
+            VideoProfile toProfile);
+
+    /** @see Connection.VideoProvider#onSendSessionModifyResponse */
+    public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
+
+    /** @see Connection.VideoProvider#onRequestCameraCapabilities */
+    public abstract void onRequestCameraCapabilities();
+
+    /** @see Connection.VideoProvider#onRequestCallDataUsage */
+    public abstract void onRequestCallDataUsage();
+
+    /** @see Connection.VideoProvider#onSetPauseImage */
+    public abstract void onSetPauseImage(Uri uri);
+
+    /** @see Connection.VideoProvider#receiveSessionModifyRequest */
+    public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
+        if (mCallback != null) {
+            try {
+                mCallback.receiveSessionModifyRequest(VideoProfile);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#receiveSessionModifyResponse */
+    public void receiveSessionModifyResponse(
+            int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
+        if (mCallback != null) {
+            try {
+                mCallback.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#handleCallSessionEvent */
+    public void handleCallSessionEvent(int event) {
+        if (mCallback != null) {
+            try {
+                mCallback.handleCallSessionEvent(event);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changePeerDimensions */
+    public void changePeerDimensions(int width, int height) {
+        if (mCallback != null) {
+            try {
+                mCallback.changePeerDimensions(width, height);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeCallDataUsage */
+    public void changeCallDataUsage(long dataUsage) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeCallDataUsage(dataUsage);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeCameraCapabilities */
+    public void changeCameraCapabilities(CameraCapabilities CameraCapabilities) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeCameraCapabilities(CameraCapabilities);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    /** @see Connection.VideoProvider#changeVideoQuality */
+    public void changeVideoQuality(int videoQuality) {
+        if (mCallback != null) {
+            try {
+                mCallback.changeVideoQuality(videoQuality);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+}
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index cffe8d5..493f238 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -118,6 +118,9 @@
   bool is_valid() const;
 };
 
+constexpr const uint8_t kAppPackageId = 0x7fu;
+constexpr const uint8_t kFrameworkPackageId = 0x01u;
+
 /**
  * A binary identifier representing a resource. Internally it
  * is a 32bit integer split as follows:
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 2d8e5a21..ca6738b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -533,6 +533,7 @@
     case android::Res_value::TYPE_REFERENCE:
     case android::Res_value::TYPE_ATTRIBUTE:
     case android::Res_value::TYPE_DYNAMIC_REFERENCE:
+    case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE:
       return android::ResTable_map::TYPE_REFERENCE;
 
     case android::Res_value::TYPE_STRING:
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index f75ed7a..2868b2a 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -88,10 +88,24 @@
 }
 
 bool Reference::Flatten(android::Res_value* out_value) const {
-  out_value->dataType = (reference_type == Reference::Type::kResource)
-                            ? android::Res_value::TYPE_REFERENCE
-                            : android::Res_value::TYPE_ATTRIBUTE;
-  out_value->data = util::HostToDevice32(id ? id.value().id : 0);
+  const ResourceId resid = id.value_or_default(ResourceId(0));
+  const bool dynamic =
+      (resid.package_id() != kFrameworkPackageId && resid.package_id() != kAppPackageId);
+
+  if (reference_type == Reference::Type::kResource) {
+    if (dynamic) {
+      out_value->dataType = android::Res_value::TYPE_DYNAMIC_REFERENCE;
+    } else {
+      out_value->dataType = android::Res_value::TYPE_REFERENCE;
+    }
+  } else {
+    if (dynamic) {
+      out_value->dataType = android::Res_value::TYPE_DYNAMIC_ATTRIBUTE;
+    } else {
+      out_value->dataType = android::Res_value::TYPE_ATTRIBUTE;
+    }
+  }
+  out_value->data = util::HostToDevice32(resid.id);
   return true;
 }
 
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 9c0f13c..68bdb95 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -533,9 +533,14 @@
   std::unique_ptr<MethodDefinition> rewrite_method;
 
   // Generate an onResourcesLoaded() callback if requested.
-  if (options_.generate_rewrite_callback) {
+  if (options_.rewrite_callback_options) {
     rewrite_method =
         util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
+    for (const std::string& package_to_callback :
+         options_.rewrite_callback_options.value().packages_to_callback) {
+      rewrite_method->AppendStatement(
+          StringPrintf("%s.R.onResourcesLoaded(p);", package_to_callback.data()));
+    }
   }
 
   for (const auto& package : table_->packages) {
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 178f558..4510430 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -33,13 +33,19 @@
 class ClassDefinition;
 class MethodDefinition;
 
+// Options for generating onResourcesLoaded callback in R.java.
+struct OnResourcesLoadedCallbackOptions {
+  // Other R classes to delegate the same callback to (with the same package ID).
+  std::vector<std::string> packages_to_callback;
+};
+
 struct JavaClassGeneratorOptions {
   // Specifies whether to use the 'final' modifier on resource entries. Default is true.
   bool use_final = true;
 
-  // Whether to generate code to rewrite the package ID of resources.
-  // Implies use_final == true. Default is false.
-  bool generate_rewrite_callback = false;
+  // If set, generates code to rewrite the package ID of resources.
+  // Implies use_final == true. Default is unset.
+  Maybe<OnResourcesLoadedCallbackOptions> rewrite_callback_options;
 
   enum class SymbolTypes {
     kAll,
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index bcb2d4f..271279f 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -381,7 +381,9 @@
 
   JavaClassGeneratorOptions options;
   options.use_final = false;
-  options.generate_rewrite_callback = true;
+  options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{
+      {"com.foo", "com.boo"},
+  };
   JavaClassGenerator generator(context.get(), table.get(), options);
 
   std::stringstream out;
@@ -389,7 +391,9 @@
 
   std::string actual = out.str();
 
-  EXPECT_NE(std::string::npos, actual.find("onResourcesLoaded"));
+  EXPECT_NE(std::string::npos, actual.find("void onResourcesLoaded"));
+  EXPECT_NE(std::string::npos, actual.find("com.foo.R.onResourcesLoaded"));
+  EXPECT_NE(std::string::npos, actual.find("com.boo.R.onResourcesLoaded"));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 4905216..dd8e14b 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -1097,18 +1097,15 @@
       return false;
     }
 
-    std::unique_ptr<ResourceTable> table =
-        LoadTablePbFromCollection(collection.get());
+    std::unique_ptr<ResourceTable> table = LoadTablePbFromCollection(collection.get());
     if (!table) {
-      context_->GetDiagnostics()->Error(DiagMessage(input)
-                                        << "invalid static library");
+      context_->GetDiagnostics()->Error(DiagMessage(input) << "invalid static library");
       return false;
     }
 
     ResourceTablePackage* pkg = table->FindPackageById(0x7f);
     if (!pkg) {
-      context_->GetDiagnostics()->Error(DiagMessage(input)
-                                        << "static library has no package");
+      context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package");
       return false;
     }
 
@@ -1125,11 +1122,9 @@
 
       pkg->name = "";
       if (override) {
-        result = table_merger_->MergeOverlay(Source(input), table.get(),
-                                             collection.get());
+        result = table_merger_->MergeOverlay(Source(input), table.get(), collection.get());
       } else {
-        result =
-            table_merger_->Merge(Source(input), table.get(), collection.get());
+        result = table_merger_->Merge(Source(input), table.get(), collection.get());
       }
 
     } else {
@@ -1782,17 +1777,21 @@
     }
 
     if (options_.generate_java_class_path) {
-      JavaClassGeneratorOptions options;
-      options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
-      options.javadoc_annotations = options_.javadoc_annotations;
+      // The set of packages whose R class to call in the main classes
+      // onResourcesLoaded callback.
+      std::vector<std::string> packages_to_callback;
+
+      JavaClassGeneratorOptions template_options;
+      template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+      template_options.javadoc_annotations = options_.javadoc_annotations;
 
       if (options_.package_type == PackageType::kStaticLib || options_.generate_non_final_ids) {
-        options.use_final = false;
+        template_options.use_final = false;
       }
 
       if (options_.package_type == PackageType::kSharedLib) {
-        options.use_final = false;
-        options.generate_rewrite_callback = true;
+        template_options.use_final = false;
+        template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
       }
 
       const StringPiece actual_package = context_->GetCompilationPackage();
@@ -1802,36 +1801,51 @@
         output_package = options_.custom_java_package.value();
       }
 
+      // Generate the private symbols if required.
       if (options_.private_symbols) {
+        packages_to_callback.push_back(options_.private_symbols.value());
+
         // If we defined a private symbols package, we only emit Public symbols
         // to the original package, and private and public symbols to the
         // private package.
-
-        options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
-        if (!WriteJavaFile(&final_table_, context_->GetCompilationPackage(),
-                           output_package, options)) {
-          return 1;
-        }
-
+        JavaClassGeneratorOptions options = template_options;
         options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
-        output_package = options_.private_symbols.value();
-      }
-
-      if (!WriteJavaFile(&final_table_, actual_package, output_package,
-                         options)) {
-        return 1;
-      }
-
-      for (const std::string& extra_package : options_.extra_java_packages) {
-        if (!WriteJavaFile(&final_table_, actual_package, extra_package,
+        if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
                            options)) {
           return 1;
         }
       }
+
+      // Generate all the symbols for all extra packages.
+      for (const std::string& extra_package : options_.extra_java_packages) {
+        packages_to_callback.push_back(extra_package);
+
+        JavaClassGeneratorOptions options = template_options;
+        options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+        if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
+          return 1;
+        }
+      }
+
+      // Generate the main public R class.
+      JavaClassGeneratorOptions options = template_options;
+
+      // Only generate public symbols if we have a private package.
+      if (options_.private_symbols) {
+        options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+      }
+
+      if (options.rewrite_callback_options) {
+        options.rewrite_callback_options.value().packages_to_callback =
+            std::move(packages_to_callback);
+      }
+
+      if (!WriteJavaFile(&final_table_, actual_package, output_package, options)) {
+        return 1;
+      }
     }
 
-    if (!WriteProguardFile(options_.generate_proguard_rules_path,
-                           proguard_keep_set)) {
+    if (!WriteProguardFile(options_.generate_proguard_rules_path, proguard_keep_set)) {
       return 1;
     }
 
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index d5c0dc4..313fe45 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -79,6 +79,17 @@
   return false;
 }
 
+static xml::XmlNodeAction::ActionFuncWithDiag RequiredAndroidAttribute(const std::string& attr) {
+  return [=](xml::Element* el, SourcePathDiagnostics* diag) -> bool {
+    if (el->FindAttribute(xml::kSchemaAndroid, attr) == nullptr) {
+      diag->Error(DiagMessage(el->line_number)
+                  << "<" << el->name << "> is missing required attribute 'android:" << attr << "'");
+      return false;
+    }
+    return true;
+  };
+}
+
 static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
   xml::Attribute* attr = el->FindAttribute({}, "package");
   if (!attr) {
@@ -272,6 +283,16 @@
 
   application_action["uses-library"].Action(RequiredNameIsJavaPackage);
   application_action["library"].Action(RequiredNameIsJavaPackage);
+
+  xml::XmlNodeAction& static_library_action = application_action["static-library"];
+  static_library_action.Action(RequiredNameIsJavaPackage);
+  static_library_action.Action(RequiredAndroidAttribute("version"));
+
+  xml::XmlNodeAction& uses_static_library_action = application_action["uses-static-library"];
+  uses_static_library_action.Action(RequiredNameIsJavaPackage);
+  uses_static_library_action.Action(RequiredAndroidAttribute("version"));
+  uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
+
   application_action["meta-data"] = meta_data_action;
   application_action["activity"] = component_action;
   application_action["activity-alias"] = component_action;
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 4a42826..0331313 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -122,8 +122,7 @@
           DiagMessage msg(entry.key.GetSource());
 
           // Call the matches method again, this time with a DiagMessage so we
-          // fill
-          // in the actual error message.
+          // fill in the actual error message.
           symbol->attribute->Matches(entry.value.get(), &msg);
           context_->GetDiagnostics()->Error(msg);
           error_ = true;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 7e7b9fb..9311091 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -39,14 +39,12 @@
 
 bool TableMerger::Merge(const Source& src, ResourceTable* table,
                         io::IFileCollection* collection) {
-  return MergeImpl(src, table, collection, false /* overlay */,
-                   true /* allow new */);
+  return MergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
 }
 
 bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table,
                                io::IFileCollection* collection) {
-  return MergeImpl(src, table, collection, true /* overlay */,
-                   options_.auto_add_overlay);
+  return MergeImpl(src, table, collection, true /* overlay */, options_.auto_add_overlay);
 }
 
 /**
@@ -55,25 +53,13 @@
 bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
                             io::IFileCollection* collection, bool overlay,
                             bool allow_new) {
-  const uint8_t desired_package_id = context_->GetPackageId();
-
   bool error = false;
   for (auto& package : table->packages) {
-    // Warn of packages with an unrelated ID.
-    const Maybe<ResourceId>& id = package->id;
-    if (id && id.value() != 0x0 && id.value() != desired_package_id) {
-      context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package "
-                                                        << package->name);
-      continue;
-    }
-
     // Only merge an empty package or the package we're building.
     // Other packages may exist, which likely contain attribute definitions.
     // This is because at compile time it is unknown if the attributes are
-    // simply
-    // uses of the attribute or definitions.
-    if (package->name.empty() ||
-        context_->GetCompilationPackage() == package->name) {
+    // simply uses of the attribute or definitions.
+    if (package->name.empty() || context_->GetCompilationPackage() == package->name) {
       FileMergeCallback callback;
       if (collection) {
         callback = [&](const ResourceNameRef& name,
@@ -83,8 +69,7 @@
           io::IFile* f = collection->FindFile(*old_file->path);
           if (!f) {
             context_->GetDiagnostics()->Error(DiagMessage(src)
-                                              << "file '" << *old_file->path
-                                              << "' not found");
+                                              << "file '" << *old_file->path << "' not found");
             return false;
           }
 
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 5d75e76..bcafbca 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -293,6 +293,11 @@
 
 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
     ResourceId id) {
+  if (!id.is_valid()) {
+    // Exit early and avoid the error logs from AssetManager.
+    return {};
+  }
+
   const android::ResTable& table = assets_.getResources(false);
   Maybe<ResourceName> maybe_name = GetResourceName(table, id);
   if (!maybe_name) {
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 29a921c..9158bdd 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -458,10 +458,14 @@
   }
 
   if (value->dataType == Res_value::TYPE_REFERENCE ||
-      value->dataType == Res_value::TYPE_ATTRIBUTE) {
-    const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE)
-                                     ? Reference::Type::kResource
-                                     : Reference::Type::kAttribute;
+      value->dataType == Res_value::TYPE_ATTRIBUTE ||
+      value->dataType == Res_value::TYPE_DYNAMIC_REFERENCE ||
+      value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
+    Reference::Type type = Reference::Type::kResource;
+    if (value->dataType == Res_value::TYPE_ATTRIBUTE ||
+        value->dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
+      type = Reference::Type::kAttribute;
+    }
 
     if (data == 0) {
       // A reference of 0, must be the magic @null reference.
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 3eb9934..a4ab64f 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1305,6 +1305,7 @@
             setConnectChoice(source.getConnectChoice());
             setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
             setHasEverConnected(source.getHasEverConnected());
+            setNotRecommended(source.isNotRecommended());
         }
 
         public void writeToParcel(Parcel dest) {
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 5f949747..632cfaf 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -66,4 +67,35 @@
 
         assertArrayEquals(bytes, rebytes);
     }
+
+    @Test
+    public void testNetworkSelectionStatusCopy() {
+        NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus();
+        networkSelectionStatus.setNotRecommended(true);
+
+        NetworkSelectionStatus copy = new NetworkSelectionStatus();
+        copy.copy(networkSelectionStatus);
+
+        assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended());
+    }
+
+    @Test
+    public void testNetworkSelectionStatusParcel() {
+        NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus();
+        networkSelectionStatus.setNotRecommended(true);
+
+        Parcel parcelW = Parcel.obtain();
+        networkSelectionStatus.writeToParcel(parcelW);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+
+        NetworkSelectionStatus copy = new NetworkSelectionStatus();
+        copy.readFromParcel(parcelR);
+
+        assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended());
+    }
 }