Merge "MediaSession: Allow all profiles to handle work profile case." into nyc-mr1-dev
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dcec07d..8d3cd48 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2793,8 +2793,8 @@
     <!-- [CHAR LIMIT=NONE] Message shown in upgrading dialog when doing an fstrim. -->
     <string name="android_upgrading_fstrim">Optimizing storage.</string>
 
-    <!-- [CHAR LIMIT=40] Title of notification that is shown when performing a system upgrade. -->
-    <string name="android_upgrading_notification_title">Android is upgrading</string>
+    <!-- [CHAR LIMIT=40] Title of notification that is shown when finishing a system upgrade. -->
+    <string name="android_upgrading_notification_title">Finishing Android update\u2026</string>
     <!-- [CHAR LIMIT=200] Body of notification that is shown when performing a system upgrade. -->
     <string name="android_upgrading_notification_body">Some apps may not work properly until the upgrade finishes</string>
 
@@ -4165,7 +4165,7 @@
 
     <!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
     <string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
-    <!-- [CHAR_LIMIT=30] Data saver: Title on first-time dialogFeature description -->
+    <!-- [CHAR_LIMIT=35] Data saver: Title on first-time dialog -->
     <string name="data_saver_enable_title">Turn on Data Saver?</string>
     <!-- [CHAR_LIMIT=16] Data saver: Button to turn it on on first-time dialog -->
     <string name="data_saver_enable_button">Turn on</string>
diff --git a/docs/html/topic/libraries/data-binding/index.jd b/docs/html/topic/libraries/data-binding/index.jd
index ec6e58c..454bb59 100644
--- a/docs/html/topic/libraries/data-binding/index.jd
+++ b/docs/html/topic/libraries/data-binding/index.jd
@@ -246,21 +246,21 @@
 </pre>
 <p>
   Expressions within the layout are written in the attribute properties using
-  the “<code>&amp;commat;{}</code>” syntax. Here, the TextView’s text is set to
+  the "<code>&commat;{}</code>" syntax. Here, the TextView's text is set to
   the firstName property of user:
 </p>
 
 <pre>
 &lt;TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
-          android:text="&amp;commat;{user.firstName}"/&gt;
+          android:text="&commat;{user.firstName}"/&gt;
 </pre>
 <h3 id="data_object">
   Data Object
 </h3>
 
 <p>
-  Let’s assume for now that you have a plain-old Java object (POJO) for User:
+  Let's assume for now that you have a plain-old Java object (POJO) for User:
 </p>
 
 <pre>
@@ -296,8 +296,8 @@
 </pre>
 <p>
   From the perspective of data binding, these two classes are equivalent. The
-  expression <strong><code>&amp;commat;{user.firstName}</code></strong> used
-  for the TextView’s <strong><code>android:text</code></strong> attribute will
+  expression <strong><code>&commat;{user.firstName}</code></strong> used
+  for the TextView's <strong><code>android:text</code></strong> attribute will
   access the <strong><code>firstName</code></strong> field in the former class
   and the <code>getFirstName()</code> method in the latter class.
   Alternatively, it will also be resolved to <code>firstName()</code> if that
@@ -310,10 +310,10 @@
 
 <p>
   By default, a Binding class will be generated based on the name of the layout
-  file, converting it to Pascal case and suffixing “Binding” to it. The above
+  file, converting it to Pascal case and suffixing "Binding" to it. The above
   layout file was <code>main_activity.xml</code> so the generate class was
   <code>MainActivityBinding</code>. This class holds all the bindings from the
-  layout properties (e.g. the <code>user</code> variable) to the layout’s Views
+  layout properties (e.g. the <code>user</code> variable) to the layout's Views
   and knows how to assign values for the binding expressions.The easiest means
   for creating the bindings is to do it while inflating:
 </p>
@@ -328,7 +328,7 @@
 }
 </pre>
 <p>
-  You’re done! Run the application and you’ll see Test User in the UI.
+  You're done! Run the application and you'll see Test User in the UI.
   Alternatively, you can get the view via:
 </p>
 
@@ -434,15 +434,15 @@
 </pre>
   Then you can bind the click event to your class as follows:
 <pre>
-  &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
-  &lt;layout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
+  &lt;?xml version="1.0" encoding="utf-8"?&gt;
+  &lt;layout xmlns:android="http://schemas.android.com/apk/res/android"&gt;
       &lt;data&gt;
-          &lt;variable name=&quot;task&quot; type=&quot;com.android.example.Task&quot; /&gt;
-          &lt;variable name=&quot;presenter&quot; type=&quot;com.android.example.Presenter&quot; /&gt;
+          &lt;variable name="task" type="com.android.example.Task" /&gt;
+          &lt;variable name="presenter" type="com.android.example.Presenter" /&gt;
       &lt;/data&gt;
-      &lt;LinearLayout android:layout_width=&quot;match_parent&quot; android:layout_height=&quot;match_parent&quot;&gt;
-          &lt;Button android:layout_width=&quot;wrap_content&quot; android:layout_height=&quot;wrap_content&quot;
-          android:onClick=&quot;@{() -&gt; presenter.onSaveClick(task)}&quot; /&gt;
+      &lt;LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"&gt;
+          &lt;Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+          android:onClick="@{() -&gt; presenter.onSaveClick(task)}" /&gt;
       &lt;/LinearLayout&gt;
   &lt;/layout&gt;
 </pre>
@@ -465,7 +465,7 @@
   above could be written as:
 </p>
 <pre>
-  android:onClick=&quot;@{(view) -&gt; presenter.onSaveClick(task)}&quot;
+  android:onClick="@{(view) -&gt; presenter.onSaveClick(task)}"
 </pre>
 Or if you wanted to use the parameter in the expression, it could work as follows:
 <pre>
@@ -474,7 +474,7 @@
 }
 </pre>
 <pre>
-  android:onClick=&quot;@{(theView) -&gt; presenter.onSaveClick(theView, task)}&quot;
+  android:onClick="@{(theView) -&gt; presenter.onSaveClick(theView, task)}"
 </pre>
 You can use a lambda expression with more than one parameter:
 <pre>
@@ -483,8 +483,8 @@
 }
 </pre>
 <pre>
-  &lt;CheckBox android:layout_width=&quot;wrap_content&quot; android:layout_height=&quot;wrap_content&quot;
-        android:onCheckedChanged=&quot;@{(cb, isChecked) -&gt; presenter.completeChanged(task, isChecked)}&quot; /&gt;
+  &lt;CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
+        android:onCheckedChanged="@{(cb, isChecked) -&gt; presenter.completeChanged(task, isChecked)}" /&gt;
 </pre>
 <p>
   If the event you are listening to returns a value whose type is not {@code
@@ -498,7 +498,7 @@
 }
 </pre>
 <pre>
-  android:onLongClick=&quot;@{(theView) -&gt; presenter.onLongClick(theView, task)}&quot;
+  android:onLongClick="@{(theView) -&gt; presenter.onLongClick(theView, task)}"
 </pre>
 <p>
 If the expression cannot be evaluated due to {@code null} objects, Data Binding returns
@@ -510,7 +510,7 @@
 {@code void} as a symbol.
 </p>
 <pre>
-  android:onClick=&quot;@{(v) -&gt; v.isVisible() ? doSomething() : void}&quot;
+  android:onClick="@{(v) -&gt; v.isVisible() ? doSomething() : void}"
 </pre>
 
 <h5>Avoid Complex Listeners</h5>
@@ -580,7 +580,7 @@
 </pre>
 <p>
   When there are class name conflicts, one of the classes may be renamed to an
-  “alias:”
+  "alias:"
 </p>
 
 <pre>
@@ -601,7 +601,7 @@
     &lt;import type="com.example.User"/&gt;
     &lt;import type="java.util.List"/&gt;
     &lt;variable name="user" type="User"/&gt;
-    &lt;variable name="userList" type="List&amp;lt;User&gt;"/&gt;
+    &lt;variable name="userList" type="List&lt;User&gt;"/&gt;
 &lt;/data&gt;
 </pre>
 <p class="caution">
@@ -695,7 +695,7 @@
 <p>
   By default, a Binding class is generated based on the name of the layout
   file, starting it with upper-case, removing underscores ( _ ) and
-  capitalizing the following letter and then suffixing “Binding”. This class
+  capitalizing the following letter and then suffixing "Binding". This class
   will be placed in a databinding package under the module package. For
   example, the layout file <code>contact_item.xml</code> will generate
   <code>ContactItemBinding</code>. If the module package is
@@ -718,7 +718,7 @@
   This generates the binding class as <code>ContactItem</code> in the
   databinding package in the module package. If the class should be generated
   in a different package within the module package, it may be prefixed with
-  “.”:
+  ".":
 </p>
 
 <pre>
@@ -741,7 +741,7 @@
 </h3>
 
 <p>
-  Variables may be passed into an included layout&apos;s binding from the
+  Variables may be passed into an included layout's binding from the
   containing layout by using the application namespace and the variable name in
   an attribute:
 </p>
@@ -855,8 +855,8 @@
 
 <pre>
 android:text="&commat;{String.valueOf(index + 1)}"
-android:visibility="&commat;{age &amp;lt; 13 ? View.GONE : View.VISIBLE}"
-android:transitionName=&apos;&commat;{"image_" + id}&apos;
+android:visibility="&commat;{age &lt; 13 ? View.GONE : View.VISIBLE}"
+android:transitionName='&commat;{"image_" + id}'
 </pre>
 <h4 id="missing_operations">
   Missing Operations
@@ -945,9 +945,9 @@
     &lt;import type="android.util.SparseArray"/&gt;
     &lt;import type="java.util.Map"/&gt;
     &lt;import type="java.util.List"/&gt;
-    &lt;variable name="list" type="List&amp;lt;String&gt;"/&gt;
-    &lt;variable name="sparse" type="SparseArray&amp;lt;String&gt;"/&gt;
-    &lt;variable name="map" type="Map&amp;lt;String, String&gt;"/&gt;
+    &lt;variable name="list" type="List&lt;String&gt;"/&gt;
+    &lt;variable name="sparse" type="SparseArray&lt;String&gt;"/&gt;
+    &lt;variable name="map" type="Map&lt;String, String&gt;"/&gt;
     &lt;variable name="index" type="int"/&gt;
     &lt;variable name="key" type="String"/&gt;
 &lt;/data&gt;
@@ -969,17 +969,17 @@
 </p>
 
 <pre>
-android:text=&apos;&commat;{map["firstName"]}&apos;
+android:text='&commat;{map["firstName"]}'
 </pre>
 <p>
   It is also possible to use double quotes to surround the attribute value.
-  When doing so, String literals should either use the &amp;quot; or back quote
+  When doing so, String literals should either use the ' or back quote
   (`).
 </p>
 
 <pre>
 android:text="&commat;{map[`firstName`}"
-android:text="&commat;{map[&amp;quot;firstName&amp;quot;]}"
+android:text="&commat;{map['firstName']}"
 </pre>
 <h4 id="resources">
   Resources
@@ -1216,7 +1216,7 @@
 }
 </pre>
 <p>
-  That&apos;s it! To access the value, use the set and get accessor methods:
+  That's it! To access the value, use the set and get accessor methods:
 </p>
 
 <pre>
@@ -1247,15 +1247,15 @@
 <pre>
 &lt;data&gt;
     &lt;import type="android.databinding.ObservableMap"/&gt;
-    &lt;variable name="user" type="ObservableMap&amp;lt;String, Object&gt;"/&gt;
+    &lt;variable name="user" type="ObservableMap&lt;String, Object&gt;"/&gt;
 &lt;/data&gt;

 &lt;TextView
-   android:text=&apos;&commat;{user["lastName"]}&apos;
+   android:text='&commat;{user["lastName"]}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/&gt;
 &lt;TextView
-   android:text=&apos;&commat;{String.valueOf(1 + (Integer)user["age"])}&apos;
+   android:text='&commat;{String.valueOf(1 + (Integer)user["age"])}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/&gt;
 </pre>
@@ -1277,15 +1277,15 @@
 &lt;data&gt;
     &lt;import type="android.databinding.ObservableList"/&gt;
     &lt;import type="com.example.my.app.Fields"/&gt;
-    &lt;variable name="user" type="ObservableList&amp;lt;Object&gt;"/&gt;
+    &lt;variable name="user" type="ObservableList&lt;Object&gt;"/&gt;
 &lt;/data&gt;

 &lt;TextView
-   android:text=&apos;&commat;{user[Fields.LAST_NAME]}&apos;
+   android:text='&commat;{user[Fields.LAST_NAME]}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/&gt;
 &lt;TextView
-   android:text=&apos;&commat;{String.valueOf(1 + (Integer)user[Fields.AGE])}&apos;
+   android:text='&commat;{String.valueOf(1 + (Integer)user[Fields.AGE])}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/&gt;
 </pre>
@@ -1428,7 +1428,7 @@
 
 <p>
   When inflating another layout, a binding must be established for the new
-  layout. Therefore, the <code>ViewStubProxy</code> must listen to the <code>ViewStub</code>&apos;s
+  layout. Therefore, the <code>ViewStubProxy</code> must listen to the <code>ViewStub</code>'s
   {@link android.view.ViewStub.OnInflateListener} and establish the binding at that time. Since
   only one can exist, the <code>ViewStubProxy</code> allows the developer to set an
   <code>OnInflateListener</code> on it that it will call after establishing the binding.
@@ -1443,9 +1443,9 @@
 </h4>
 
 <p>
-  At times, the specific binding class won&apos;t be known. For example, a
+  At times, the specific binding class won't be known. For example, a
   {@link android.support.v7.widget.RecyclerView.Adapter} operating against arbitrary layouts
-  won&apos;t know the specific binding class. It still must assign the binding value during the
+  won't know the specific binding class. It still must assign the binding value during the
   {@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder}.
 </p>
 
@@ -1499,13 +1499,13 @@
 For an attribute, data binding tries to find the method setAttribute. The
 namespace for the attribute does not matter, only the attribute name itself.
 <p>
-  For example, an expression associated with TextView&apos;s attribute
+  For example, an expression associated with TextView's attribute
   <strong><code>android:text</code></strong> will look for a setText(String).
   If the expression returns an int, data binding will search for a setText(int)
   method. Be careful to have the expression return the correct type, casting if
   necessary. Note that data binding will work even if no attribute exists with
   the given name. You can then easily "create" attributes for any setter by
-  using data binding. For example, support DrawerLayout doesn&apos;t have any
+  using data binding. For example, support DrawerLayout doesn't have any
   attributes, but plenty of setters. You can use the automatic setters to use
   one of these.
 </p>
@@ -1522,7 +1522,7 @@
 </h3>
 
 <p>
-  Some attributes have setters that don&apos;t match by name. For these
+  Some attributes have setters that don't match by name. For these
   methods, an attribute may be associated with the setter through
   {@link android.databinding.BindingMethods} annotation. This must be associated with
   a class and contains {@link android.databinding.BindingMethod} annotations, one for
@@ -1591,8 +1591,8 @@
 }
 </pre>
 <pre>
-&lt;ImageView app:imageUrl=“&commat;{venue.imageUrl}”
-app:error=“&commat;{&commat;drawable/venueError}”/&gt;
+&lt;ImageView app:imageUrl="&commat;{venue.imageUrl}"
+app:error="&commat;{&commat;drawable/venueError}"/&gt;
 </pre>
 
 <p>
@@ -1747,7 +1747,7 @@
 
 <pre>
 &lt;TextView
-   android:text=&apos;&commat;{userMap["lastName"]}&apos;
+   android:text='&commat;{userMap["lastName"]}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/&gt;
 </pre>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
index 2f58de5..c06e849 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
@@ -737,9 +737,11 @@
         public void updateInstalledServices(List<PrintServiceInfo> services) {
             mInstalledServices.clear();
 
-            final int numServices = services.size();
-            for (int i = 0; i < numServices; i++) {
-                mInstalledServices.add(services.get(i).getComponentName().getPackageName());
+            if (services != null) {
+                final int numServices = services.size();
+                for (int i = 0; i < numServices; i++) {
+                    mInstalledServices.add(services.get(i).getComponentName().getPackageName());
+                }
             }
 
             filterRecommendations();
diff --git a/packages/SettingsLib/res/layout/usage_view.xml b/packages/SettingsLib/res/layout/usage_view.xml
index aa1a046..1d56668 100644
--- a/packages/SettingsLib/res/layout/usage_view.xml
+++ b/packages/SettingsLib/res/layout/usage_view.xml
@@ -71,9 +71,11 @@
         android:id="@+id/bottom_label_group"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingStart="@dimen/usage_graph_labels_width"
         android:orientation="horizontal">
-
+        <Space
+            android:id="@+id/bottom_label_space"
+            android:layout_width="@dimen/usage_graph_labels_width"
+            android:layout_height="wrap_content"/>
         <include android:id="@+id/label_start"
             layout="@layout/usage_side_label" />
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java b/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
index ee1821d..c6a45bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
@@ -71,10 +71,11 @@
                 layout.addView(labels);
                 // Set gravity.
                 labels.setGravity(Gravity.END);
-                // Swap the bottom label padding
+                // Swap the bottom space order.
                 LinearLayout bottomLabels = (LinearLayout) findViewById(R.id.bottom_label_group);
-                bottomLabels.setPadding(bottomLabels.getPaddingRight(), bottomLabels.getPaddingTop(),
-                        bottomLabels.getPaddingLeft(), bottomLabels.getPaddingBottom());
+                View bottomSpace = bottomLabels.findViewById(R.id.bottom_label_space);
+                bottomLabels.removeView(bottomSpace);
+                bottomLabels.addView(bottomSpace);
             } else if (gravity != Gravity.START) {
                 throw new IllegalArgumentException("Unsupported gravity " + gravity);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 63f726b..95cb672 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -176,8 +176,18 @@
         mCurrentView = currentView.findViewById(mId);
     }
 
+    public void setCarMode(boolean carMode) {
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            final View view = mViews.get(i);
+            if (view instanceof ButtonInterface) {
+                ((ButtonInterface) view).setCarMode(carMode);
+            }
+        }
+    }
+
     /**
-     * Interface for ImageView button actions.
+     * Interface for button actions.
      */
     public interface ButtonInterface {
         void setImageResource(@DrawableRes int resId);
@@ -187,5 +197,7 @@
         void abortCurrentGesture();
 
         void setLandscape(boolean landscape);
+
+        void setCarMode(boolean carMode);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 23aeae8..0f4d9ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -624,6 +624,7 @@
                 mCarMode = true;
                 uiCarModeChanged = true;
             }
+            getHomeButton().setCarMode(mCarMode);
         }
         return uiCarModeChanged;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 3df7590..61bac2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -270,6 +270,11 @@
     public void setLandscape(boolean landscape) {
         //no op
     }
+
+    @Override
+    public void setCarMode(boolean carMode) {
+        // no op
+    }
 }
 
 
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 a1265fb..83463e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -471,6 +471,10 @@
             }
             mServiceState = state;
             mDataNetType = state.getDataNetworkType();
+            if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
+                    mServiceState.isUsingCarrierAggregation()) {
+                mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
+            }
             updateTelephony();
         }
 
@@ -482,6 +486,10 @@
             }
             mDataState = state;
             mDataNetType = networkType;
+            if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
+                    mServiceState.isUsingCarrierAggregation()) {
+                mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
+            }
             updateTelephony();
         }
 
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index b24edb9..f4f6b66 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -130,7 +130,7 @@
             switch (msg.what) {
                 case MSG_SHOW:
                     final CharSequence title = context
-                            .getText(R.string.android_upgrading_title);
+                            .getText(R.string.android_upgrading_notification_title);
 
                     final Intent intent = new Intent();
                     intent.setClassName("com.android.settings",
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index a42d0cd..5d209fc 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.job.controllers;
 
+import android.annotation.UserIdInt;
 import android.app.job.JobInfo;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -23,6 +24,7 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -35,6 +37,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Controller for monitoring changes to content URIs through a ContentObserver.
@@ -59,7 +62,11 @@
     private static volatile ContentObserverController sController;
 
     final private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
-    ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> mObservers = new ArrayMap<>();
+    /**
+     * Per-userid {@link JobInfo.TriggerContentUri} keyed ContentObserver cache.
+     */
+    SparseArray<ArrayMap<JobInfo.TriggerContentUri, ObserverInstance>> mObservers =
+            new SparseArray<>();
     final Handler mHandler;
 
     public static ContentObserverController get(JobSchedulerService taskManagerService) {
@@ -203,18 +210,21 @@
 
     final class ObserverInstance extends ContentObserver {
         final JobInfo.TriggerContentUri mUri;
+        final @UserIdInt int mUserId;
         final ArraySet<JobInstance> mJobs = new ArraySet<>();
 
-        public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri) {
+        public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri,
+                @UserIdInt int userId) {
             super(handler);
             mUri = uri;
+            mUserId = userId;
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
             if (DEBUG) {
                 Slog.i(TAG, "onChange(self=" + selfChange + ") for " + uri
-                        + " when mUri=" + mUri);
+                        + " when mUri=" + mUri + " mUserId=" + mUserId);
             }
             synchronized (mLock) {
                 final int N = mJobs.size();
@@ -258,27 +268,38 @@
 
         boolean mTriggerPending;
 
+        // This constructor must be called with the master job scheduler lock held.
         JobInstance(JobStatus jobStatus) {
             mJobStatus = jobStatus;
             mExecuteRunner = new TriggerRunnable(this);
             mTimeoutRunner = new TriggerRunnable(this);
             final JobInfo.TriggerContentUri[] uris = jobStatus.getJob().getTriggerContentUris();
+            final int sourceUserId = jobStatus.getSourceUserId();
+            ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
+                    mObservers.get(sourceUserId);
+            if (observersOfUser == null) {
+                observersOfUser = new ArrayMap<>();
+                mObservers.put(sourceUserId, observersOfUser);
+            }
             if (uris != null) {
                 for (JobInfo.TriggerContentUri uri : uris) {
-                    ObserverInstance obs = mObservers.get(uri);
+                    ObserverInstance obs = observersOfUser.get(uri);
                     if (obs == null) {
-                        obs = new ObserverInstance(mHandler, uri);
-                        mObservers.put(uri, obs);
+                        obs = new ObserverInstance(mHandler, uri, jobStatus.getSourceUserId());
+                        observersOfUser.put(uri, obs);
                         final boolean andDescendants = (uri.getFlags() &
                                 JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
                         if (DEBUG) {
                             Slog.v(TAG, "New observer " + obs + " for " + uri.getUri()
-                                    + " andDescendants=" + andDescendants);
+                                    + " andDescendants=" + andDescendants
+                                    + " sourceUserId=" + sourceUserId);
                         }
                         mContext.getContentResolver().registerContentObserver(
                                 uri.getUri(),
                                 andDescendants,
-                                obs);
+                                obs,
+                                sourceUserId
+                        );
                     } else {
                         if (DEBUG) {
                             final boolean andDescendants = (uri.getFlags() &
@@ -342,7 +363,11 @@
                         Slog.i(TAG, "Unregistering observer " + obs + " for " + obs.mUri.getUri());
                     }
                     mContext.getContentResolver().unregisterContentObserver(obs);
-                    mObservers.remove(obs.mUri);
+                    ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observerOfUser =
+                            mObservers.get(obs.mUserId);
+                    if (observerOfUser !=  null) {
+                        observerOfUser.remove(obs.mUri);
+                    }
                 }
             }
         }
@@ -366,60 +391,66 @@
         int N = mObservers.size();
         if (N > 0) {
             pw.println("  Observers:");
-            for (int i = 0; i < N; i++) {
-                ObserverInstance obs = mObservers.valueAt(i);
-                int M = obs.mJobs.size();
-                boolean shouldDump = false;
-                for (int j=0; j<M; j++) {
-                    JobInstance inst = obs.mJobs.valueAt(j);
-                    if (inst.mJobStatus.shouldDump(filterUid)) {
-                        shouldDump = true;
-                        break;
+            for (int userIdx = 0; userIdx < N; userIdx++) {
+                final int userId = mObservers.keyAt(userIdx);
+                ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observersOfUser =
+                        mObservers.get(userId);
+                int numbOfObserversPerUser = observersOfUser.size();
+                for (int observerIdx = 0 ; observerIdx < numbOfObserversPerUser; observerIdx++) {
+                    ObserverInstance obs = observersOfUser.valueAt(observerIdx);
+                    int M = obs.mJobs.size();
+                    boolean shouldDump = false;
+                    for (int j = 0; j < M; j++) {
+                        JobInstance inst = obs.mJobs.valueAt(j);
+                        if (inst.mJobStatus.shouldDump(filterUid)) {
+                            shouldDump = true;
+                            break;
+                        }
                     }
-                }
-                if (!shouldDump) {
-                    continue;
-                }
-                pw.print("    ");
-                JobInfo.TriggerContentUri trigger = mObservers.keyAt(i);
-                pw.print(trigger.getUri());
-                pw.print(" 0x");
-                pw.print(Integer.toHexString(trigger.getFlags()));
-                pw.print(" (");
-                pw.print(System.identityHashCode(obs));
-                pw.println("):");
-                pw.println("      Jobs:");
-                for (int j=0; j<M; j++) {
-                    JobInstance inst = obs.mJobs.valueAt(j);
-                    pw.print("        #");
-                    inst.mJobStatus.printUniqueId(pw);
-                    pw.print(" from ");
-                    UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid());
-                    if (inst.mChangedAuthorities != null) {
-                        pw.println(":");
-                        if (inst.mTriggerPending) {
-                            pw.print("          Trigger pending: update=");
-                            TimeUtils.formatDuration(
-                                    inst.mJobStatus.getTriggerContentUpdateDelay(), pw);
-                            pw.print(", max=");
-                            TimeUtils.formatDuration(
-                                    inst.mJobStatus.getTriggerContentMaxDelay(), pw);
+                    if (!shouldDump) {
+                        continue;
+                    }
+                    pw.print("    ");
+                    JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx);
+                    pw.print(trigger.getUri());
+                    pw.print(" 0x");
+                    pw.print(Integer.toHexString(trigger.getFlags()));
+                    pw.print(" (");
+                    pw.print(System.identityHashCode(obs));
+                    pw.println("):");
+                    pw.println("      Jobs:");
+                    for (int j = 0; j < M; j++) {
+                        JobInstance inst = obs.mJobs.valueAt(j);
+                        pw.print("        #");
+                        inst.mJobStatus.printUniqueId(pw);
+                        pw.print(" from ");
+                        UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid());
+                        if (inst.mChangedAuthorities != null) {
+                            pw.println(":");
+                            if (inst.mTriggerPending) {
+                                pw.print("          Trigger pending: update=");
+                                TimeUtils.formatDuration(
+                                        inst.mJobStatus.getTriggerContentUpdateDelay(), pw);
+                                pw.print(", max=");
+                                TimeUtils.formatDuration(
+                                        inst.mJobStatus.getTriggerContentMaxDelay(), pw);
+                                pw.println();
+                            }
+                            pw.println("          Changed Authorities:");
+                            for (int k = 0; k < inst.mChangedAuthorities.size(); k++) {
+                                pw.print("          ");
+                                pw.println(inst.mChangedAuthorities.valueAt(k));
+                            }
+                            if (inst.mChangedUris != null) {
+                                pw.println("          Changed URIs:");
+                                for (int k = 0; k < inst.mChangedUris.size(); k++) {
+                                    pw.print("          ");
+                                    pw.println(inst.mChangedUris.valueAt(k));
+                                }
+                            }
+                        } else {
                             pw.println();
                         }
-                        pw.println("          Changed Authorities:");
-                        for (int k=0; k<inst.mChangedAuthorities.size(); k++) {
-                            pw.print("          ");
-                            pw.println(inst.mChangedAuthorities.valueAt(k));
-                        }
-                        if (inst.mChangedUris != null) {
-                            pw.println("          Changed URIs:");
-                            for (int k = 0; k<inst.mChangedUris.size(); k++) {
-                                pw.print("          ");
-                                pw.println(inst.mChangedUris.valueAt(k));
-                            }
-                        }
-                    } else {
-                        pw.println();
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 77c69c9..c5f3cfd3b 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -56,16 +56,9 @@
     // TODO: Evaluate the need for WeakReferences here.
 
     /**
-     * The list of packages to dexopt.
+     * The list of dexopt invocations for all work.
      */
-    private List<PackageParser.Package> mDexoptPackages;
-
-    /**
-     * The list of dexopt invocations for the current package (which will no longer be in
-     * mDexoptPackages). This can be more than one as a package may have multiple code paths,
-     * e.g., in the split-APK case.
-     */
-    private List<String> mCommandsForCurrentPackage;
+    private List<String> mDexoptCommands;
 
     private int completeSize;
 
@@ -94,15 +87,43 @@
 
     @Override
     public synchronized void prepare() throws RemoteException {
-        if (mDexoptPackages != null) {
+        if (mDexoptCommands != null) {
             throw new IllegalStateException("already called prepare()");
         }
         synchronized (mPackageManagerService.mPackages) {
-            mDexoptPackages = PackageManagerServiceUtils.getPackagesForDexopt(
+            // Important: the packages we need to run with ab-ota compiler-reason.
+            List<PackageParser.Package> important = PackageManagerServiceUtils.getPackagesForDexopt(
                     mPackageManagerService.mPackages.values(), mPackageManagerService);
+            // Others: we should optimize this with the (first-)boot compiler-reason.
+            List<PackageParser.Package> others =
+                    new ArrayList<>(mPackageManagerService.mPackages.values());
+            others.removeAll(important);
+
+            // Pre-size the array list by over-allocating by a factor of 1.5.
+            mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
+
+            for (PackageParser.Package p : important) {
+                // Make sure that core apps are optimized according to their own "reason".
+                // If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed
+                // (by default is speed-profile) they will be interepreted/JITed. This in itself is
+                // not a problem as we will end up doing profile guided compilation. However, some
+                // core apps may be loaded by system server which doesn't JIT and we need to make
+                // sure we don't interpret-only
+                int compilationReason = p.coreApp
+                        ? PackageManagerService.REASON_CORE_APP
+                        : PackageManagerService.REASON_AB_OTA;
+                mDexoptCommands.addAll(generatePackageDexopts(p, compilationReason));
+            }
+            for (PackageParser.Package p : others) {
+                // We assume here that there are no core apps left.
+                if (p.coreApp) {
+                    throw new IllegalStateException("Found a core app that's not important");
+                }
+                mDexoptCommands.addAll(
+                        generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT));
+            }
         }
-        completeSize = mDexoptPackages.size();
-        mCommandsForCurrentPackage = null;
+        completeSize = mDexoptCommands.size();
     }
 
     @Override
@@ -110,87 +131,52 @@
         if (DEBUG_DEXOPT) {
             Log.i(TAG, "Cleaning up OTA Dexopt state.");
         }
-        mDexoptPackages = null;
-        mCommandsForCurrentPackage = null;
+        mDexoptCommands = null;
     }
 
     @Override
     public synchronized boolean isDone() throws RemoteException {
-        if (mDexoptPackages == null) {
+        if (mDexoptCommands == null) {
             throw new IllegalStateException("done() called before prepare()");
         }
 
-        return mDexoptPackages.isEmpty() && (mCommandsForCurrentPackage == null);
+        return mDexoptCommands.isEmpty();
     }
 
     @Override
     public synchronized float getProgress() throws RemoteException {
-        // We approximate by number of packages here. We could track all compiles, if we
-        // generated them ahead of time. Right now we're trying to conserve memory.
+        // Approximate the progress by the amount of already completed commands.
         if (completeSize == 0) {
             return 1f;
         }
-        int packagesLeft = mDexoptPackages.size() + (mCommandsForCurrentPackage != null ? 1 : 0);
-        return (completeSize - packagesLeft) / ((float)completeSize);
-    }
-
-    /**
-     * Return the next dexopt command for the current package. Enforces the invariant
-     */
-    private String getNextPackageDexopt() {
-        if (mCommandsForCurrentPackage != null) {
-            String next = mCommandsForCurrentPackage.remove(0);
-            if (mCommandsForCurrentPackage.isEmpty()) {
-                mCommandsForCurrentPackage = null;
-            }
-            return next;
-        }
-        return null;
+        int commandsLeft = mDexoptCommands.size();
+        return (completeSize - commandsLeft) / ((float)completeSize);
     }
 
     @Override
     public synchronized String nextDexoptCommand() throws RemoteException {
-        if (mDexoptPackages == null) {
+        if (mDexoptCommands == null) {
             throw new IllegalStateException("dexoptNextPackage() called before prepare()");
         }
 
-        // Get the next command.
-        for (;;) {
-            // Check whether there's one for the current package.
-            String next = getNextPackageDexopt();
-            if (next != null) {
-                return next;
-            }
+        if (mDexoptCommands.isEmpty()) {
+            return "(all done)";
+        }
 
-            // Move to the next package, if possible.
-            if (mDexoptPackages.isEmpty()) {
-                return "Nothing to do";
-            }
+        String next = mDexoptCommands.remove(0);
 
-            PackageParser.Package nextPackage = mDexoptPackages.remove(0);
-
-            if (DEBUG_DEXOPT) {
-                Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt.");
-            }
-
-            // Generate the next mPackageDexopts state. Ignore errors, this loop is strongly
-            // monotonically increasing, anyways.
-            generatePackageDexopts(nextPackage);
-
-            // Invariant check: mPackageDexopts is null or not empty.
-            if (mCommandsForCurrentPackage != null && mCommandsForCurrentPackage.isEmpty()) {
-                cleanup();
-                throw new IllegalStateException("mPackageDexopts empty for " + nextPackage);
-            }
+        if (IsFreeSpaceAvailable()) {
+            return next;
+        } else {
+            mDexoptCommands.clear();
+            return "(no free space)";
         }
     }
 
     /**
-     * Generate all dexopt commands for the given package and place them into mPackageDexopts.
-     * Returns true on success, false in an error situation like low disk space.
+     * Check for low space. Returns true if there's space left.
      */
-    private synchronized boolean generatePackageDexopts(PackageParser.Package nextPackage) {
-        // Check for low space.
+    private boolean IsFreeSpaceAvailable() {
         // TODO: If apps are not installed in the internal /data partition, we should compare
         //       against that storage's free capacity.
         File dataDir = Environment.getDataDirectory();
@@ -200,12 +186,14 @@
             throw new IllegalStateException("Invalid low memory threshold");
         }
         long usableSpace = dataDir.getUsableSpace();
-        if (usableSpace < lowThreshold) {
-            Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " +
-                    usableSpace);
-            return false;
-        }
+        return (usableSpace >= lowThreshold);
+    }
 
+    /**
+     * Generate all dexopt commands for the given package.
+     */
+    private synchronized List<String> generatePackageDexopts(PackageParser.Package pkg,
+            int compilationReason) {
         // Use our custom connection that just collects the commands.
         RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection();
         Installer collectingInstaller = new Installer(mContext, collectingConnection);
@@ -213,71 +201,22 @@
         // Use the package manager install and install lock here for the OTA dex optimizer.
         PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
                 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
-        // Make sure that core apps are optimized according to their own "reason".
-        // If the core apps are not preopted in the B OTA, and REASON_AB_OTA is not speed
-        // (by default is speed-profile) they will be interepreted/JITed. This in itself is not a
-        // problem as we will end up doing profile guided compilation. However, some core apps may
-        // be loaded by system server which doesn't JIT and we need to make sure we don't
-        // interpret-only
-        int compilationReason = nextPackage.coreApp
-                ? PackageManagerService.REASON_CORE_APP
-                : PackageManagerService.REASON_AB_OTA;
 
-        optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
+        optimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                 null /* ISAs */, false /* checkProfiles */,
                 getCompilerFilterForReason(compilationReason),
                 null /* CompilerStats.PackageStats */);
 
-        mCommandsForCurrentPackage = collectingConnection.commands;
-        if (mCommandsForCurrentPackage.isEmpty()) {
-            mCommandsForCurrentPackage = null;
-        }
-
-        return true;
+        return collectingConnection.commands;
     }
 
     @Override
     public synchronized void dexoptNextPackage() throws RemoteException {
-        if (mDexoptPackages == null) {
-            throw new IllegalStateException("dexoptNextPackage() called before prepare()");
-        }
-        if (mDexoptPackages.isEmpty()) {
-            // Tolerate repeated calls.
-            return;
-        }
-
-        PackageParser.Package nextPackage = mDexoptPackages.remove(0);
-
-        if (DEBUG_DEXOPT) {
-            Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt.");
-        }
-
-        // Check for low space.
-        // TODO: If apps are not installed in the internal /data partition, we should compare
-        //       against that storage's free capacity.
-        File dataDir = Environment.getDataDirectory();
-        @SuppressWarnings("deprecation")
-        long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
-        if (lowThreshold == 0) {
-            throw new IllegalStateException("Invalid low memory threshold");
-        }
-        long usableSpace = dataDir.getUsableSpace();
-        if (usableSpace < lowThreshold) {
-            Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " +
-                    usableSpace);
-            return;
-        }
-
-        PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
-                mPackageManagerService.mInstaller, mPackageManagerService.mInstallLock, mContext);
-        optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, null /* ISAs */,
-                false /* checkProfiles */,
-                getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA),
-                mPackageManagerService.getOrCreateCompilerPackageStats(nextPackage));
+        throw new UnsupportedOperationException();
     }
 
     private void moveAbArtifacts(Installer installer) {
-        if (mDexoptPackages != null) {
+        if (mDexoptCommands != null) {
             throw new IllegalStateException("Should not be ota-dexopting when trying to move.");
         }
 
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 8be5dfb..d2d5c28 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1071,14 +1071,15 @@
                                 Region.Op.REVERSE_DIFFERENCE);
                     }
 
-                    // We figured out what is touchable for the entire screen - done.
-                    if (unaccountedSpace.isEmpty()) {
-                        break;
-                    }
-
                     // If a window is modal it prevents other windows from being touched
                     if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                             | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+                        // Account for all space in the task, whether the windows in it are
+                        // touchable or not. The modal window blocks all touches from the task's
+                        // area.
+                        unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
+                                Region.Op.REVERSE_DIFFERENCE);
+
                         if (task != null) {
                             // If the window is associated with a particular task, we can skip the
                             // rest of the windows for that task.
@@ -1090,6 +1091,10 @@
                             break;
                         }
                     }
+                    // We figured out what is touchable for the entire screen - done.
+                    if (unaccountedSpace.isEmpty()) {
+                        break;
+                    }
                 }
 
                 // Always report the focused window.
diff --git a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
index c0bf9b3..7a3b461 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
@@ -130,6 +130,10 @@
                     // Install on user 0 so that the package is cached when demo user is re-created
                     installExistingPackage(basePackageName, UserHandle.USER_SYSTEM, counter);
                 } else if (returnCode == PackageManager.INSTALL_FAILED_ALREADY_EXISTS) {
+                    // This can only happen in first session after a reboot
+                    if (!mApkToPackageMap.containsKey(apkName)) {
+                        mApkToPackageMap.put(apkName, basePackageName);
+                    }
                     installExistingPackage(basePackageName, userId, counter);
                 }
             }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 8446dd0..6151e5b 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -240,6 +240,8 @@
 
     private boolean mIsDataRoamingFromRegistration;
 
+    private boolean mIsUsingCarrierAggregation;
+
     /**
      * get String description of roaming type
      * @hide
@@ -318,6 +320,7 @@
         mCdmaEriIconMode = s.mCdmaEriIconMode;
         mIsEmergencyOnly = s.mIsEmergencyOnly;
         mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
+        mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
     }
 
     /**
@@ -346,6 +349,7 @@
         mCdmaEriIconMode = in.readInt();
         mIsEmergencyOnly = in.readInt() != 0;
         mIsDataRoamingFromRegistration = in.readInt() != 0;
+        mIsUsingCarrierAggregation = in.readInt() != 0;
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -371,6 +375,7 @@
         out.writeInt(mCdmaEriIconMode);
         out.writeInt(mIsEmergencyOnly ? 1 : 0);
         out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0);
+        out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
     }
 
     public int describeContents() {
@@ -680,7 +685,8 @@
                 && equalsHandlesNulls(mCdmaDefaultRoamingIndicator,
                         s.mCdmaDefaultRoamingIndicator)
                 && mIsEmergencyOnly == s.mIsEmergencyOnly
-                && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration);
+                && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
+                && mIsUsingCarrierAggregation == s.mIsUsingCarrierAggregation);
     }
 
     /**
@@ -788,7 +794,8 @@
                 + " RoamInd=" + mCdmaRoamingIndicator
                 + " DefRoamInd=" + mCdmaDefaultRoamingIndicator
                 + " EmergOnly=" + mIsEmergencyOnly
-                + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration);
+                + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration
+                + " IsUsingCarrierAggregation=" + mIsUsingCarrierAggregation);
     }
 
     private void setNullState(int state) {
@@ -815,6 +822,7 @@
         mCdmaEriIconMode = -1;
         mIsEmergencyOnly = false;
         mIsDataRoamingFromRegistration = false;
+        mIsUsingCarrierAggregation = false;
     }
 
     public void setStateOutOfService() {
@@ -988,6 +996,7 @@
         mCdmaDefaultRoamingIndicator = m.getInt("cdmaDefaultRoamingIndicator");
         mIsEmergencyOnly = m.getBoolean("emergencyOnly");
         mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
+        mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
     }
 
     /**
@@ -1017,21 +1026,42 @@
         m.putInt("cdmaDefaultRoamingIndicator", mCdmaDefaultRoamingIndicator);
         m.putBoolean("emergencyOnly", Boolean.valueOf(mIsEmergencyOnly));
         m.putBoolean("isDataRoamingFromRegistration", Boolean.valueOf(mIsDataRoamingFromRegistration));
+        m.putBoolean("isUsingCarrierAggregation", Boolean.valueOf(mIsUsingCarrierAggregation));
     }
 
     /** @hide */
     public void setRilVoiceRadioTechnology(int rt) {
+        if (rt == RIL_RADIO_TECHNOLOGY_LTE_CA) {
+            rt = RIL_RADIO_TECHNOLOGY_LTE;
+        }
+
         this.mRilVoiceRadioTechnology = rt;
     }
 
     /** @hide */
     public void setRilDataRadioTechnology(int rt) {
+        if (rt == RIL_RADIO_TECHNOLOGY_LTE_CA) {
+            rt = RIL_RADIO_TECHNOLOGY_LTE;
+            this.mIsUsingCarrierAggregation = true;
+        } else {
+            this.mIsUsingCarrierAggregation = false;
+        }
         this.mRilDataRadioTechnology = rt;
         if (VDBG) Rlog.d(LOG_TAG, "[ServiceState] setRilDataRadioTechnology=" +
                 mRilDataRadioTechnology);
     }
 
     /** @hide */
+    public boolean isUsingCarrierAggregation() {
+        return mIsUsingCarrierAggregation;
+    }
+
+    /** @hide */
+    public void setIsUsingCarrierAggregation(boolean ca) {
+        mIsUsingCarrierAggregation = ca;
+    }
+
+    /** @hide */
     public void setCssIndicator(int css) {
         this.mCssIndicator = (css != 0);
     }