merge in lmp-mr1-release history after reset to lmp-mr1-dev
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7e15ec2..1ed709b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2692,7 +2692,7 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param target Component name of the agent to be enabled.
-     * @param options TrustAgent-specific feature bundle. If null for any admin, agent
+     * @param configuration TrustAgent-specific feature bundle. If null for any admin, agent
      * will be strictly disabled according to the state of the
      *  {@link #KEYGUARD_DISABLE_TRUST_AGENTS} flag.
      * <p>If {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is set and options is not null for all admins,
@@ -2700,10 +2700,11 @@
      * <p>Consult documentation for the specific TrustAgent to determine legal options parameters.
      */
     public void setTrustAgentConfiguration(ComponentName admin, ComponentName target,
-            PersistableBundle options) {
+            PersistableBundle configuration) {
         if (mService != null) {
             try {
-                mService.setTrustAgentConfiguration(admin, target, options, UserHandle.myUserId());
+                mService.setTrustAgentConfiguration(admin, target, configuration,
+                        UserHandle.myUserId());
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2715,7 +2716,12 @@
      * {@link #setTrustAgentConfiguration(ComponentName, ComponentName, PersistableBundle)} for
      * all device admins.
      *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with. If null,
+     * this function returns a list of configurations for all admins that declare
+     * {@link #KEYGUARD_DISABLE_TRUST_AGENTS}. If any admin declares
+     * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} but doesn't call
+     * {@link #setTrustAgentConfiguration(ComponentName, ComponentName, PersistableBundle)}
+     * for this {@param agent} or calls it with a null configuration, null is returned.
      * @param agent Which component to get enabled features for.
      * @return configuration for the given trust agent.
      */
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 9b80e74..64d0fcf 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -219,16 +219,21 @@
     }
 
     private void evalRequest(NetworkRequestInfo n) {
+        if (VDBG) log("evalRequest");
         if (n.requested == false && n.score < mScore &&
                 n.request.networkCapabilities.satisfiedByNetworkCapabilities(
                 mCapabilityFilter) && acceptRequest(n.request, n.score)) {
+            if (VDBG) log("  needNetworkFor");
             needNetworkFor(n.request, n.score);
             n.requested = true;
         } else if (n.requested == true &&
                 (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(
                 mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {
+            if (VDBG) log("  releaseNetworkFor");
             releaseNetworkFor(n.request);
             n.requested = false;
+        } else {
+            if (VDBG) log("  done");
         }
     }
 
diff --git a/core/java/com/android/internal/app/AlertActivity.java b/core/java/com/android/internal/app/AlertActivity.java
index 7456def..5566aa6 100644
--- a/core/java/com/android/internal/app/AlertActivity.java
+++ b/core/java/com/android/internal/app/AlertActivity.java
@@ -17,9 +17,14 @@
 package com.android.internal.app;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.DialogInterface;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.view.KeyEvent;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 
 /**
  * An activity that follows the visual style of an AlertDialog.
@@ -62,6 +67,19 @@
         }
     }
 
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        event.setClassName(Dialog.class.getName());
+        event.setPackageName(getPackageName());
+
+        ViewGroup.LayoutParams params = getWindow().getAttributes();
+        boolean isFullScreen = (params.width == ViewGroup.LayoutParams.MATCH_PARENT) &&
+                (params.height == ViewGroup.LayoutParams.MATCH_PARENT);
+        event.setFullScreen(isFullScreen);
+
+        return false;
+    }
+
     /**
      * Sets up the alert, including applying the parameters to the alert model,
      * and installing the alert's content.
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 9656a21..7c1308f 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -96,9 +96,12 @@
             final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser(
                         newIntent, MATCH_DEFAULT_ONLY, targetUserId);
 
-            // Only show a disclosure if this is a normal (non-OS) app
-            final boolean shouldShowDisclosure =
-                    !UserHandle.isSameApp(ri.activityInfo.applicationInfo.uid, Process.SYSTEM_UID);
+            // Don't show the disclosure if next activity is ResolverActivity or ChooserActivity
+            // as those will already have shown work / personal as neccesary etc.
+            final boolean shouldShowDisclosure = ri == null || ri.activityInfo == null ||
+                    !"android".equals(ri.activityInfo.packageName) ||
+                    !(ResolverActivity.class.getName().equals(ri.activityInfo.name)
+                    || ChooserActivity.class.getName().equals(ri.activityInfo.name));
 
             try {
                 startActivityAsCaller(newIntent, null, targetUserId);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 661acbe..649a59f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -343,7 +343,7 @@
                     }
 
                     final Intent intent = intentForDisplayResolveInfo(dri);
-                    onIntentSelected(dri.ri, intent, mAlwaysUseOption);
+                    onIntentSelected(dri.ri, intent, false);
                     finish();
                 }
             });
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 99c5471a..8a54d54 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -498,7 +498,7 @@
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4525890122209673621">"Ermöglicht der App, sich selbst zu starten, sobald das System gestartet wurde. Dadurch kann es länger dauern, bis der Fernseher gestartet wird, und durch die ständige Aktivität der App wird die gesamte Leistung des Geräts beeinträchtigt."</string>
     <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"Ermöglicht der App, sich selbst zu starten, sobald das System gebootet wurde. Dadurch kann es länger dauern, bis das Telefon gestartet wird, und durch die ständige Aktivität der App wird die gesamte Leistung des Telefons beeinträchtigt."</string>
     <string name="permlab_broadcastSticky" msgid="7919126372606881614">"Dauerhaften Broadcast senden"</string>
-    <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Ermöglicht der App, weiluerhafte Broadcasts zu senden, die auch nach Ende des Broadcasts bestehen bleiben. Ein zu intensiver Einsatz kann das Tablet langsam oder instabil machen, weil zu viel Arbeitsspeicher belegt wird."</string>
+    <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Ermöglicht der App, dauerhafte Broadcasts zu senden, die auch nach Ende des Broadcasts bestehen bleiben. Ein zu intensiver Einsatz kann das Tablet langsam oder instabil machen, weil zu viel Arbeitsspeicher belegt wird."</string>
     <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Ermöglicht der App, dauerhafte Broadcasts zu senden, die auch nach Ende des Broadcasts bestehen bleiben. Ein zu intensiver Einsatz kann das Gerät langsam oder instabil machen, weil zu viel Arbeitsspeicher belegt wird."</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Ermöglicht der App, dauerhafte Broadcasts zu senden, die auch nach Ende des Broadcasts bestehen bleiben. Ein zu intensiver Einsatz kann das Telefon langsam oder instabil machen, weil zu viel Arbeitsspeicher belegt wird."</string>
     <string name="permlab_readContacts" msgid="8348481131899886131">"Kontakte lesen"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 864f27b..b44944c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -271,8 +271,8 @@
     <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"Acessar o cartão SD."</string>
     <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"Recursos de acessibilidade"</string>
     <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"Recursos que a tecnologia assistencial pode solicitar."</string>
-    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar conteúdo da janela"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecionar o conteúdo da janela com a qual você está interagindo."</string>
+    <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Recuperar cont. da janela"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Inspecionar o conteúdo da janela com que você está interagindo."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Ativar Explorar por toque"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="5800552516779249356">"Itens tocados serão falados em voz alta e a tela poderá ser explorada por meio de gestos."</string>
     <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"Ativar acessibilidade na Web aprimorada"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 22fa0f4..97659a2 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1606,7 +1606,7 @@
     <string name="data_usage_warning_body" msgid="2814673551471969954">"Додирните за преглед кор. и под."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Нема више 2G-3G података"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Нема више 4G података"</string>
-    <string name="data_usage_mobile_limit_title" msgid="557158376602636112">"Нема више подат. за мобил. уређ."</string>
+    <string name="data_usage_mobile_limit_title" msgid="557158376602636112">"Нема више података за мобилне"</string>
     <string name="data_usage_wifi_limit_title" msgid="5803363779034792676">"Нема више Wi-Fi података"</string>
     <string name="data_usage_limit_body" msgid="291731708279614081">"Потрошили сте податке за овај месец"</string>
     <string name="data_usage_3g_limit_snoozed_title" msgid="7026739121138005231">"Прекорачен пренос 2G-3G података"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index c1b6c2d..704a73d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1060,7 +1060,7 @@
     <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Bu sayfada kal"</string>
     <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nBu sayfadan ayrılmak istediğinizden emin misiniz?"</string>
     <string name="save_password_label" msgid="6860261758665825069">"Onayla"</string>
-    <string name="double_tap_toast" msgid="4595046515400268881">"İpucu: Yakınlaştırmak ve uzaklaştırmak için iki kez hafifçe vurun."</string>
+    <string name="double_tap_toast" msgid="4595046515400268881">"İpucu: Yakınlaştırmak ve uzaklaştırmak için iki kez hafifçe dokunun."</string>
     <string name="autofill_this_form" msgid="4616758841157816676">"Otomatik Doldur"</string>
     <string name="setup_autofill" msgid="7103495070180590814">"Otomatik doldurma ayarla"</string>
     <string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string>
@@ -1805,7 +1805,7 @@
     <string name="reason_unknown" msgid="6048913880184628119">"bilinmiyor"</string>
     <string name="reason_service_unavailable" msgid="7824008732243903268">"Yazdırma hizmeti etkin değil"</string>
     <string name="print_service_installed_title" msgid="2246317169444081628">"<xliff:g id="NAME">%s</xliff:g> hizmeti yüklendi"</string>
-    <string name="print_service_installed_message" msgid="5897362931070459152">"Etkinleştirmek için hafifçe vurun"</string>
+    <string name="print_service_installed_message" msgid="5897362931070459152">"Etkinleştirmek için hafifçe dokunun"</string>
     <string name="restr_pin_enter_admin_pin" msgid="783643731895143970">"Yönetici PIN\'ini girin"</string>
     <string name="restr_pin_enter_pin" msgid="3395953421368476103">"PIN\'i girin"</string>
     <string name="restr_pin_incorrect" msgid="8571512003955077924">"Yanlış"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index aeb8753..24ec7ce 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -269,13 +269,13 @@
     <string name="permgrouplab_storage" msgid="1971118770546336966">"存储"</string>
     <string name="permgroupdesc_storage" product="nosdcard" msgid="7442318502446874999">"访问USB存储设备。"</string>
     <string name="permgroupdesc_storage" product="default" msgid="9203302214915355774">"访问SD卡。"</string>
-    <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"辅助功能"</string>
+    <string name="permgrouplab_accessibilityFeatures" msgid="7919025602283593907">"无障碍功能"</string>
     <string name="permgroupdesc_accessibilityFeatures" msgid="4205196881678144335">"辅助技术可请求启用的功能。"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"检索窗口内容"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"检查您正与其进行互动的窗口的内容。"</string>
     <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"启用触摸浏览"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="5800552516779249356">"设备可大声读出用户触摸的内容,而用户可以通过手势浏览屏幕。"</string>
-    <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"启用网页辅助增强功能"</string>
+    <string name="capability_title_canRequestEnhancedWebAccessibility" msgid="1739881766522594073">"启用网页无障碍增强功能"</string>
     <string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"安装脚本以方便访问应用的内容。"</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"监测您输入的文字"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包含个人数据,例如信用卡号和密码。"</string>
@@ -353,8 +353,8 @@
     <string name="permdesc_dump" msgid="1778299088692290329">"允许应用检索系统的内部状态。恶意应用可能会检索一般情况下绝不需要检索的多种私人信息和安全信息。"</string>
     <string name="permlab_retrieve_window_content" msgid="8022588608994589938">"检索屏幕内容"</string>
     <string name="permdesc_retrieve_window_content" msgid="3193269069469700265">"允许应用检索活动窗口的内容。恶意应用可能会检索整个窗口的内容,并检查其中除密码以外的所有文字。"</string>
-    <string name="permlab_temporary_enable_accessibility" msgid="2312612135127310254">"暂时启用辅助功能"</string>
-    <string name="permdesc_temporary_enable_accessibility" msgid="8079456293182975464">"允许应用在设备上暂时启用辅助功能。恶意应用可能会在未经用户同意的情况下擅自启用辅助功能。"</string>
+    <string name="permlab_temporary_enable_accessibility" msgid="2312612135127310254">"暂时启用无障碍功能"</string>
+    <string name="permdesc_temporary_enable_accessibility" msgid="8079456293182975464">"允许应用在设备上暂时启用无障碍功能。恶意应用可能会在未经用户同意的情况下擅自启用无障碍功能。"</string>
     <string name="permlab_retrieveWindowToken" msgid="7154762602367758602">"检索窗口令牌"</string>
     <string name="permdesc_retrieveWindowToken" msgid="668173747687795074">"允许应用检索窗口令牌。恶意软件可能会借此在未经授权的情况下冒充系统与应用窗口进行互动。"</string>
     <string name="permlab_frameStats" msgid="7056374987314361639">"检索框架统计信息"</string>
@@ -409,8 +409,8 @@
     <string name="permdesc_readInputState" msgid="8387754901688728043">"允许应用记录您所按的键,包括与其他应用进行交互(如输入密码)时按的键。普通应用绝不需要此权限。"</string>
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"绑定至输入法"</string>
     <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"允许应用绑定至输入法的顶级接口。普通应用绝不需要此权限。"</string>
-    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"绑定至辅助服务"</string>
-    <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"允许应用绑定至辅助服务的顶级接口。普通应用绝不需要此权限。"</string>
+    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"绑定至无障碍服务"</string>
+    <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"允许应用绑定至无障碍服务的顶级接口。普通应用绝不需要此权限。"</string>
     <string name="permlab_bindPrintService" msgid="8462815179572748761">"绑定至打印服务"</string>
     <string name="permdesc_bindPrintService" msgid="7960067623209111135">"允许应用绑定至打印服务的顶级接口。普通应用绝不需要此权限。"</string>
     <string name="permlab_bindPrintSpoolerService" msgid="6807762783744125954">"绑定至打印处理服务"</string>
@@ -1500,7 +1500,7 @@
     <string name="forward_intent_to_work" msgid="621480743856004612">"您目前是在工作资料内使用此应用"</string>
     <string name="input_method_binding_label" msgid="1283557179944992649">"输入法"</string>
     <string name="sync_binding_label" msgid="3687969138375092423">"同步"</string>
-    <string name="accessibility_binding_label" msgid="4148120742096474641">"辅助功能"</string>
+    <string name="accessibility_binding_label" msgid="4148120742096474641">"无障碍"</string>
     <string name="wallpaper_binding_label" msgid="1240087844304687662">"壁纸"</string>
     <string name="chooser_wallpaper" msgid="7873476199295190279">"更改壁纸"</string>
     <string name="notification_listener_binding_label" msgid="2014162835481906429">"通知侦听器"</string>
@@ -1707,9 +1707,9 @@
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"删除"</string>
     <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"要将音量调高到推荐水平以上吗?\n\n长时间保持高音量可能会损伤听力。"</string>
-    <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"持续按住双指即可启用辅助功能。"</string>
-    <string name="accessibility_enabled" msgid="1381972048564547685">"辅助功能已启用。"</string>
-    <string name="enable_accessibility_canceled" msgid="3833923257966635673">"已取消辅助功能。"</string>
+    <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"持续按住双指即可启用无障碍功能。"</string>
+    <string name="accessibility_enabled" msgid="1381972048564547685">"无障碍功能已启用。"</string>
+    <string name="enable_accessibility_canceled" msgid="3833923257966635673">"已取消无障碍功能。"</string>
     <string name="user_switched" msgid="3768006783166984410">"当前用户是<xliff:g id="NAME">%1$s</xliff:g>。"</string>
     <string name="user_switching_message" msgid="2871009331809089783">"正在切换为<xliff:g id="NAME">%1$s</xliff:g>…"</string>
     <string name="owner_name" msgid="2716755460376028154">"机主"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index ddf2168..c27c1015 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -121,7 +121,7 @@
     <string name="roamingText7" msgid="7112078724097233605">"漫遊 - 聯盟合作夥伴"</string>
     <string name="roamingText8" msgid="5989569778604089291">"漫遊 - Google Premium 合作夥伴"</string>
     <string name="roamingText9" msgid="7969296811355152491">"漫遊 - 完整服務功能"</string>
-    <string name="roamingText10" msgid="3992906999815316417">"漫遊 - 部份服務功能"</string>
+    <string name="roamingText10" msgid="3992906999815316417">"漫遊 - 部分服務功能"</string>
     <string name="roamingText11" msgid="4154476854426920970">"漫遊橫幅開啟"</string>
     <string name="roamingText12" msgid="1189071119992726320">"漫遊橫幅關閉"</string>
     <string name="roamingTextSearching" msgid="8360141885972279963">"正在搜尋服務"</string>
diff --git a/docs/html/training/articles/security-tips.jd b/docs/html/training/articles/security-tips.jd
index e05b44c..3215a0e 100644
--- a/docs/html/training/articles/security-tips.jd
+++ b/docs/html/training/articles/security-tips.jd
@@ -445,7 +445,17 @@
 headers like <code>no-cache</code> can also be used to indicate that an application should
 not cache particular content.</p>
 
-
+<p>Devices running platforms older than Android 4.4 (API level 19)
+use a version of {@link android.webkit webkit} that has a number of security issues.
+As a workaround, if your app is running on these devices, it
+should confirm that {@link android.webkit.WebView} objects display only trusted
+content. You should also use the updatable security {@link
+java.security.Provider Provider} object to make sure your app isn’t exposed to
+potential vulnerabilities in SSL, as described in <a
+href="{@docRoot}training/articles/security-gms-provider.html">Updating Your
+Security Provider to Protect Against SSL Exploits</a>. If your application must
+render content from the open web, consider providing your own renderer so
+you can keep it up to date with the latest security patches.</p>
 
 
 <h3 id="Credentials">Handling Credentials</h3>
diff --git a/docs/html/training/wearables/watch-faces/drawing.jd b/docs/html/training/wearables/watch-faces/drawing.jd
index 9afdd80..3c5da34 100644
--- a/docs/html/training/wearables/watch-faces/drawing.jd
+++ b/docs/html/training/wearables/watch-faces/drawing.jd
@@ -192,13 +192,14 @@
 
 <h3 id="Timer">Initialize the custom timer</h3>
 
-<p>As a watch face developer, you can decide how often you want to update your watch face by
+<p>As a watch face developer, you decide how often you want to update your watch face by
 providing a custom timer that ticks with the required frequency while the device is in
-interactive mode. This enables you to create custom animations and other visual effects. In
-ambient mode, you should disable the timer to let the CPU sleep and update the watch face
-only when the time changes. For more information, see
-<a href="{@docRoot}training/wearables/watch-faces/performance.html">Optimizing Performance and
-Battery Life</a>.</p>
+interactive mode. This enables you to create custom animations and other visual effects.
+</p>
+
+<p class="note"><strong>Note:</strong> In ambient mode, the system does not reliably call the
+custom timer. To update the watch face in ambient mode, see <a href="#TimeTick">Update the watch
+face in ambient mode</a>.</p>
 
 <p>An example timer definition from the <code>AnalogWatchFaceService</code> class that ticks once
 every second is shown in <a href="#Variables">Declare variables</a>. In the
@@ -210,9 +211,8 @@
 <li>The device is in interactive mode.</li>
 </ul>
 
-<p>The timer should not run under any other conditions, since this watch face does not
-draw the second hand in ambient mode to conserve power. The <code>AnalogWatchFaceService</code>
-class schedules the next timer tick if required as follows:</p>
+<p>The <code>AnalogWatchFaceService</code> class schedules the next timer tick if required as
+follows:</p>
 
 <pre>
 private void updateTimer() {
@@ -281,15 +281,15 @@
 
 
 
-<h3 id="TimeTick">Invalidate the canvas when the time changes</h3>
+<h3 id="TimeTick">Update the watch face in ambient mode</h3>
 
-<p>The system calls the <code>Engine.onTimeTick()</code> method every minute. In ambient mode,
-it is usually sufficient to update your watch face once per minute. To update your watch face
-more often while in interactive mode, you provide a custom timer as described in
+<p>In ambient mode, the system calls the <code>Engine.onTimeTick()</code> method every minute.
+It is usually sufficient to update your watch face once per minute in this mode. To update your
+watch face while in interactive mode, you must provide a custom timer as described in
 <a href="#Timer">Initialize the custom timer</a>.</p>
 
-<p>Most watch face implementations just invalidate the canvas to redraw the watch face when
-the time changes:</p>
+<p>In ambient mode, most watch face implementations simply invalidate the canvas to redraw the watch
+face in the <code>Engine.onTimeTick()</code> method:</p>
 
 <pre>
 &#64;Override
@@ -402,20 +402,17 @@
 &#64;Override
 public void onAmbientModeChanged(boolean inAmbientMode) {
 
-    boolean wasInAmbientMode = isInAmbientMode();
     super.onAmbientModeChanged(inAmbientMode);
 
-    if (inAmbientMode != wasInAmbientMode) {
-        if (mLowBitAmbient) {
-            boolean antiAlias = !inAmbientMode;
-            mHourPaint.setAntiAlias(antiAlias);
-            mMinutePaint.setAntiAlias(antiAlias);
-            mSecondPaint.setAntiAlias(antiAlias);
-            mTickPaint.setAntiAlias(antiAlias);
-        }
-        invalidate();
-        updateTimer();
+    if (mLowBitAmbient) {
+        boolean antiAlias = !inAmbientMode;
+        mHourPaint.setAntiAlias(antiAlias);
+        mMinutePaint.setAntiAlias(antiAlias);
+        mSecondPaint.setAntiAlias(antiAlias);
+        mTickPaint.setAntiAlias(antiAlias);
     }
+    invalidate();
+    updateTimer();
 }
 </pre>
 
@@ -478,7 +475,7 @@
     float hrLength = centerX - 80;
 
     // Only draw the second hand in interactive mode.
-    if (!mAmbient) {
+    if (!isInAmbientMode()) {
         float secX = (float) Math.sin(secRot) * secLength;
         float secY = (float) -Math.cos(secRot) * secLength;
         canvas.drawLine(centerX, centerY, centerX + secX, centerY +
diff --git a/docs/html/training/wearables/watch-faces/service.jd b/docs/html/training/wearables/watch-faces/service.jd
index 7ab575e..35366c5 100644
--- a/docs/html/training/wearables/watch-faces/service.jd
+++ b/docs/html/training/wearables/watch-faces/service.jd
@@ -64,42 +64,15 @@
 
 <h3 id="Dependencies">Dependencies</h3>
 
-<p>For the handheld app, edit the <code>build.gradle</code> file in the <code>mobile</code> module
-to add these dependencies:</p>
-
-<pre>
-apply plugin: 'com.android.application'
-
-android { ... }
-
-dependencies {
-    ...
-    wearApp project(':wear')
-    compile 'com.google.android.gms:play-services:6.5.+'
-}
-</pre>
-
-<p>For the wearable app, edit the <code>build.gradle</code> file in the <code>wear</code> module
-to add these dependencies:</p>
-
-<pre>
-apply plugin: 'com.android.application'
-
-android { ... }
-
-dependencies {
-    ...
-    compile 'com.google.android.support:wearable:1.1.+'
-    compile 'com.google.android.gms:play-services-wearable:6.5.+'
-}
-</pre>
-
 <p>The Wearable Support Library provides the necessary classes that you extend to create watch
 face implementations. The Google Play services client libraries (<code>play-services</code> and
 <code>play-services-wearable</code>) are required to sync data items between the companion device
 and the wearable with the <a href="{@docRoot}training/wearables/data-layer/index.html">Wearable
 Data Layer API</a>.</p>
 
+<p>Android Studio automatically adds the required entries in your <code>build.gradle</code>
+files when you create the project in the instructions above.</p>
+
 <h3 id="Reference">Wearable Support Library API Reference</h3>
 
 <p>The reference documentation provides detailed information about the classes you use to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 5f7d452..5eff5a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -110,7 +110,6 @@
                 intent.putExtra(EXTRA_RUN_PROVISION, true);
                 intent.putExtra(EXTRA_ENABLE_WIFI_TETHER, true);
                 intent.setComponent(ComponentName.unflattenFromString(tetherEnable));
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
             } else {
                 int wifiState = mWifiManager.getWifiState();
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 3cda6de..4e89566 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -1262,6 +1262,10 @@
 
     private void copyTo(Object array, Element.DataType dt, int arrayLen) {
         Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
+        if (dt.mSize * arrayLen < mSize) {
+            throw new RSIllegalArgumentException(
+                "Size of output array cannot be smaller than size of allocation.");
+        }
         mRS.validate();
         mRS.nAllocationRead(getID(mRS), array, dt);
         Trace.traceEnd(RenderScript.TRACE_TAG);
diff --git a/rs/java/android/renderscript/ScriptIntrinsicHistogram.java b/rs/java/android/renderscript/ScriptIntrinsicHistogram.java
index 95b610a..4ecac99 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicHistogram.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicHistogram.java
@@ -91,9 +91,9 @@
             throw new RSIllegalArgumentException(
                 "Input vector size must be >= output vector size.");
         }
-        if (ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
-            ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
-            throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+        if (!ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
+            !ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+            throw new RSIllegalArgumentException("Input type must be U8 or U8_4.");
         }
 
         forEach(0, ain, null, null, opt);
@@ -187,9 +187,9 @@
         if (mOut.getType().getElement().getVectorSize() != 1) {
             throw new RSIllegalArgumentException("Output vector size must be one.");
         }
-        if (ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
-            ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
-            throw new RSIllegalArgumentException("Output type must be U32 or I32.");
+        if (!ain.getType().getElement().isCompatible(Element.U8(mRS)) &&
+            !ain.getType().getElement().isCompatible(Element.U8_4(mRS))) {
+            throw new RSIllegalArgumentException("Input type must be U8 or U8_4.");
         }
 
         forEach(1, ain, null, null, opt);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 61a7073..944f1c0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -907,7 +907,10 @@
             // network is blocked; clone and override state
             info = new NetworkInfo(info);
             info.setDetailedState(DetailedState.BLOCKED, null, null);
-            if (DBG) log("returning Blocked NetworkInfo");
+            if (DBG) {
+                log("returning Blocked NetworkInfo for ifname=" +
+                        lp.getInterfaceName() + ", uid=" + uid);
+            }
         }
         if (info != null && mLockdownTracker != null) {
             info = mLockdownTracker.augmentNetworkInfo(info);
@@ -2324,6 +2327,9 @@
             if (nri.isRequest) {
                 // Find all networks that are satisfying this request and remove the request
                 // from their request lists.
+                // TODO - it's my understanding that for a request there is only a single
+                // network satisfying it, so this loop is wasteful
+                boolean wasKept = false;
                 for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                     if (nai.networkRequests.get(nri.request.requestId) != null) {
                         nai.networkRequests.remove(nri.request.requestId);
@@ -2335,19 +2341,39 @@
                         if (unneeded(nai)) {
                             if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
                             teardownUnneededNetwork(nai);
+                        } else {
+                            // suspect there should only be one pass through here
+                            // but if any were kept do the check below
+                            wasKept |= true;
                         }
                     }
                 }
 
+                NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+                if (nai != null) {
+                    mNetworkForRequestId.remove(nri.request.requestId);
+                }
                 // Maintain the illusion.  When this request arrived, we might have pretended
                 // that a network connected to serve it, even though the network was already
                 // connected.  Now that this request has gone away, we might have to pretend
                 // that the network disconnected.  LegacyTypeTracker will generate that
                 // phantom disconnect for this type.
-                NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
-                if (nai != null) {
-                    mNetworkForRequestId.remove(nri.request.requestId);
-                    if (nri.request.legacyType != TYPE_NONE) {
+                if (nri.request.legacyType != TYPE_NONE && nai != null) {
+                    boolean doRemove = true;
+                    if (wasKept) {
+                        // check if any of the remaining requests for this network are for the
+                        // same legacy type - if so, don't remove the nai
+                        for (int i = 0; i < nai.networkRequests.size(); i++) {
+                            NetworkRequest otherRequest = nai.networkRequests.valueAt(i);
+                            if (otherRequest.legacyType == nri.request.legacyType &&
+                                    isRequest(otherRequest)) {
+                                if (DBG) log(" still have other legacy request - leaving");
+                                doRemove = false;
+                            }
+                        }
+                    }
+
+                    if (doRemove) {
                         mLegacyTypeTracker.remove(nri.request.legacyType, nai);
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c3a344f..4e932c1 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1525,14 +1525,14 @@
                 // Now the task above it has to return to the home task instead.
                 final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
                 mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
-            } else {
-                if (DEBUG_STATES && isOnHomeDisplay()) Slog.d(TAG,
+            } else if (!isOnHomeDisplay()) {
+                return false;
+            } else if (!isHomeStack()){
+                if (DEBUG_STATES) Slog.d(TAG,
                         "resumeTopActivityLocked: Launching home next");
-                // Only resume home if on home display
                 final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
                         HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
-                return isOnHomeDisplay() &&
-                        mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
+                return mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
             }
         }
 
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index ef86c6c..9566f93 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -20,6 +20,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -45,8 +46,10 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.IState;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -63,6 +66,8 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
 
 /**
  * @hide
@@ -1377,6 +1382,112 @@
             }
         }
 
+        private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
+        private SimChangeBroadcastReceiver mBroadcastReceiver = null;
+
+        // keep consts in sync with packages/apps/Settings TetherSettings.java
+        private static final int WIFI_TETHERING      = 0;
+        private static final int USB_TETHERING       = 1;
+        private static final int BLUETOOTH_TETHERING = 2;
+
+        // keep consts in sync with packages/apps/Settings TetherService.java
+        private static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+        private static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+
+        private void startListeningForSimChanges() {
+            if (DBG) Log.d(TAG, "startListeningForSimChanges");
+            if (mBroadcastReceiver == null) {
+                mBroadcastReceiver = new SimChangeBroadcastReceiver(
+                        mSimBcastGenerationNumber.incrementAndGet());
+                final IntentFilter filter = new IntentFilter();
+                filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+
+                mContext.registerReceiver(mBroadcastReceiver, filter);
+            }
+        }
+
+        private void stopListeningForSimChanges() {
+            if (DBG) Log.d(TAG, "stopListeningForSimChanges");
+            if (mBroadcastReceiver != null) {
+                mSimBcastGenerationNumber.incrementAndGet();
+                mContext.unregisterReceiver(mBroadcastReceiver);
+                mBroadcastReceiver = null;
+            }
+        }
+
+        class SimChangeBroadcastReceiver extends BroadcastReceiver {
+            // used to verify this receiver is still current
+            final private int mGenerationNumber;
+
+            // we're interested in edge-triggered LOADED notifications, so
+            // ignore LOADED unless we saw an ABSENT state first
+            private boolean mSimAbsentSeen = false;
+
+            public SimChangeBroadcastReceiver(int generationNumber) {
+                super();
+                mGenerationNumber = generationNumber;
+            }
+
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (DBG) {
+                    Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
+                            ", current generationNumber=" + mSimBcastGenerationNumber.get());
+                }
+                if (mGenerationNumber != mSimBcastGenerationNumber.get()) return;
+
+                final String state =
+                        intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+
+                Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" +
+                        mSimAbsentSeen);
+                if (!mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state)) {
+                    mSimAbsentSeen = true;
+                }
+
+                if (mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state)) {
+                    mSimAbsentSeen = false;
+                    try {
+                        if (mContext.getResources().getString(com.android.internal.R.string.
+                                config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
+                            final String tetherService = mContext.getResources().getString(
+                                    com.android.internal.R.string.config_wifi_tether_enable);
+                            ArrayList<Integer> tethered = new ArrayList<Integer>();
+                            synchronized (mPublicSync) {
+                                Set ifaces = mIfaces.keySet();
+                                for (Object iface : ifaces) {
+                                    TetherInterfaceSM sm = mIfaces.get(iface);
+                                    if (sm != null && sm.isTethered()) {
+                                        if (isUsb((String)iface)) {
+                                            tethered.add(new Integer(USB_TETHERING));
+                                        } else if (isWifi((String)iface)) {
+                                            tethered.add(new Integer(WIFI_TETHERING));
+                                        } else if (isBluetooth((String)iface)) {
+                                            tethered.add(new Integer(BLUETOOTH_TETHERING));
+                                        }
+                                    }
+                                }
+                            }
+                            for (int tetherType : tethered) {
+                                Intent startProvIntent = new Intent();
+                                startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+                                startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
+                                startProvIntent.setComponent(
+                                        ComponentName.unflattenFromString(tetherService));
+                                mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+                            }
+                            Log.d(TAG, "re-evaluate provisioning");
+                        } else {
+                            Log.d(TAG, "no prov-check needed for new SIM");
+                        }
+                    } catch (Resources.NotFoundException e) {
+                        Log.d(TAG, "no prov-check needed for new SIM");
+                        // not defined, do nothing
+                    }
+                }
+            }
+        }
+
         class InitialState extends TetherMasterUtilState {
             @Override
             public void enter() {
@@ -1413,6 +1524,7 @@
             @Override
             public void enter() {
                 turnOnMasterTetherSettings(); // may transition us out
+                startListeningForSimChanges();
 
                 mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; // better try something first pass
                                                         // or crazy tests cases will fail
@@ -1422,6 +1534,7 @@
             @Override
             public void exit() {
                 turnOffUpstreamMobileConnection();
+                stopListeningForSimChanges();
                 notifyTetheredOfNewUpstreamIface(null);
             }
             @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index a8f6954..ce52920 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -55,6 +55,10 @@
         mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
                 mAddress, mService.getVendorId()));
         startQueuedActions();
+
+        // Switch TV input after bootup.
+        setActiveSource(true);
+        maySendActiveSource(Constants.ADDR_TV);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 60c0193..2af56fe 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -667,7 +667,7 @@
             public void onServiceDied() {
                 synchronized (mImplLock) {
                     mAudioSource = null;
-                    mAudioSink = null;
+                    mAudioSink.clear();
                     mAudioPatch = null;
                 }
             }
@@ -675,7 +675,7 @@
         private int mOverrideAudioType = AudioManager.DEVICE_NONE;
         private String mOverrideAudioAddress = "";
         private AudioDevicePort mAudioSource;
-        private AudioDevicePort mAudioSink;
+        private List<AudioDevicePort> mAudioSink = new ArrayList<>();
         private AudioPatch mAudioPatch = null;
         private float mCommittedVolume = 0.0f;
         private float mSourceVolume = 0.0f;
@@ -691,22 +691,23 @@
             mAudioManager.registerAudioPortUpdateListener(mAudioListener);
             if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
                 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
-                mAudioSink = findAudioSinkFromAudioPolicy();
+                findAudioSinkFromAudioPolicy(mAudioSink);
             }
         }
 
-        private AudioDevicePort findAudioSinkFromAudioPolicy() {
+        private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
+            sinks.clear();
             ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
-            if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
-                int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
-                for (AudioPort port : devicePorts) {
-                    AudioDevicePort devicePort = (AudioDevicePort) port;
-                    if ((devicePort.type() & sinkDevice) != 0) {
-                        return devicePort;
-                    }
+            if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
+                return;
+            }
+            int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
+            for (AudioPort port : devicePorts) {
+                AudioDevicePort devicePort = (AudioDevicePort) port;
+                if ((devicePort.type() & sinkDevice) != 0) {
+                    sinks.add(devicePort);
                 }
             }
-            return null;
         }
 
         private AudioDevicePort findAudioDevicePort(int type, String address) {
@@ -792,7 +793,7 @@
             // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
             // because Java won't evaluate the latter if the former is true.
 
-            if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) {
+            if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
                 if (mAudioPatch != null) {
                     mAudioManager.releaseAudioPatch(mAudioPatch);
                     mAudioPatch = null;
@@ -831,45 +832,53 @@
             }
 
             AudioPortConfig sourceConfig = mAudioSource.activeConfig();
-            AudioPortConfig sinkConfig = mAudioSink.activeConfig();
+            List<AudioPortConfig> sinkConfigs = new ArrayList<>();
             AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
             boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
 
-            int sinkSamplingRate = mDesiredSamplingRate;
-            int sinkChannelMask = mDesiredChannelMask;
-            int sinkFormat = mDesiredFormat;
-            // If sinkConfig != null and values are set to default, fill in the sinkConfig values.
-            if (sinkConfig != null) {
-                if (sinkSamplingRate == 0) {
-                    sinkSamplingRate = sinkConfig.samplingRate();
+            for (AudioDevicePort audioSink : mAudioSink) {
+                AudioPortConfig sinkConfig = audioSink.activeConfig();
+                int sinkSamplingRate = mDesiredSamplingRate;
+                int sinkChannelMask = mDesiredChannelMask;
+                int sinkFormat = mDesiredFormat;
+                // If sinkConfig != null and values are set to default,
+                // fill in the sinkConfig values.
+                if (sinkConfig != null) {
+                    if (sinkSamplingRate == 0) {
+                        sinkSamplingRate = sinkConfig.samplingRate();
+                    }
+                    if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
+                        sinkChannelMask = sinkConfig.channelMask();
+                    }
+                    if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
+                        sinkChannelMask = sinkConfig.format();
+                    }
                 }
-                if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
-                    sinkChannelMask = sinkConfig.channelMask();
-                }
-                if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
-                    sinkChannelMask = sinkConfig.format();
-                }
-            }
 
-            if (sinkConfig == null
-                    || sinkConfig.samplingRate() != sinkSamplingRate
-                    || sinkConfig.channelMask() != sinkChannelMask
-                    || sinkConfig.format() != sinkFormat) {
-                // Check for compatibility and reset to default if necessary.
-                if (!intArrayContains(mAudioSink.samplingRates(), sinkSamplingRate)
-                        && mAudioSink.samplingRates().length > 0) {
-                    sinkSamplingRate = mAudioSink.samplingRates()[0];
+                if (sinkConfig == null
+                        || sinkConfig.samplingRate() != sinkSamplingRate
+                        || sinkConfig.channelMask() != sinkChannelMask
+                        || sinkConfig.format() != sinkFormat) {
+                    // Check for compatibility and reset to default if necessary.
+                    if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
+                            && audioSink.samplingRates().length > 0) {
+                        sinkSamplingRate = audioSink.samplingRates()[0];
+                    }
+                    if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
+                        sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
+                    }
+                    if (!intArrayContains(audioSink.formats(), sinkFormat)) {
+                        sinkFormat = AudioFormat.ENCODING_DEFAULT;
+                    }
+                    sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
+                            sinkFormat, null);
+                    shouldRecreateAudioPatch = true;
                 }
-                if (!intArrayContains(mAudioSink.channelMasks(), sinkChannelMask)) {
-                    sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
-                }
-                if (!intArrayContains(mAudioSink.formats(), sinkFormat)) {
-                    sinkFormat = AudioFormat.ENCODING_DEFAULT;
-                }
-                sinkConfig = mAudioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
-                        sinkFormat, null);
-                shouldRecreateAudioPatch = true;
+                sinkConfigs.add(sinkConfig);
             }
+            // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
+            // non-empty at the beginning of this method.
+            AudioPortConfig sinkConfig = sinkConfigs.get(0);
             if (sourceConfig == null || sourceGainConfig != null) {
                 int sourceSamplingRate = 0;
                 if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
@@ -899,7 +908,7 @@
                 mAudioManager.createAudioPatch(
                         audioPatchArray,
                         new AudioPortConfig[] { sourceConfig },
-                        new AudioPortConfig[] { sinkConfig });
+                        sinkConfigs.toArray(new AudioPortConfig[0]));
                 mAudioPatch = audioPatchArray[0];
                 if (sourceGainConfig != null) {
                     mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
@@ -977,17 +986,24 @@
             if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
                 return false;
             }
-            AudioDevicePort previousSink = mAudioSink;
+            List<AudioDevicePort> previousSink = mAudioSink;
+            mAudioSink = new ArrayList<>();
             if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
-                mAudioSink = findAudioSinkFromAudioPolicy();
+                findAudioSinkFromAudioPolicy(mAudioSink);
             } else {
                 AudioDevicePort audioSink =
                         findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
                 if (audioSink != null) {
-                    mAudioSink = audioSink;
+                    mAudioSink.add(audioSink);
                 }
             }
-            return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink);
+
+            // Returns true if mAudioSink and previousSink differs.
+            if (mAudioSink.size() != previousSink.size()) {
+                return true;
+            }
+            previousSink.removeAll(mAudioSink);
+            return !previousSink.isEmpty();
         }
 
         private void handleAudioSinkUpdated() {