Merge "Check in "current" system API files." into lmp-mr1-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b5c0e90..f2be45c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -168,6 +168,13 @@
private static final int LOG_ON_PAUSE_CALLED = 30021;
private static final int LOG_ON_RESUME_CALLED = 30022;
+ /** Type for IActivityManager.serviceDoneExecuting: anonymous operation */
+ public static final int SERVICE_DONE_EXECUTING_ANON = 0;
+ /** Type for IActivityManager.serviceDoneExecuting: done with an onStart call */
+ public static final int SERVICE_DONE_EXECUTING_START = 1;
+ /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */
+ public static final int SERVICE_DONE_EXECUTING_STOP = 2;
+
private ContextImpl mSystemContext;
static IPackageManager sPackageManager;
@@ -2758,7 +2765,7 @@
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
- data.token, 0, 0, 0);
+ data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2787,7 +2794,7 @@
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
- data.token, 0, 0, 0);
+ data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
@@ -2815,7 +2822,7 @@
data.token, data.intent, doRebind);
} else {
ActivityManagerNative.getDefault().serviceDoneExecuting(
- data.token, 0, 0, 0);
+ data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
} catch (RemoteException ex) {
}
@@ -2897,7 +2904,7 @@
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
- data.token, 1, data.startId, res);
+ data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2928,7 +2935,7 @@
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
- token, 0, 0, 0);
+ token, SERVICE_DONE_EXECUTING_STOP, 0, 0);
} catch (RemoteException e) {
// nothing to do.
Slog.i(TAG, "handleStopService: unable to execute serviceDoneExecuting for "
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 99c5471..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..c27c101 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/design/wear/watchfaces.jd b/docs/html/design/wear/watchfaces.jd
index 99dc3dd..2a00802 100644
--- a/docs/html/design/wear/watchfaces.jd
+++ b/docs/html/design/wear/watchfaces.jd
@@ -101,7 +101,7 @@
<!-- H2: plan for all display modes -->
<div style="float:right;margin-top:35px;margin-left:20px">
<img src="{@docRoot}design/media/wear/Render_Interactive.png"
- width="200" height="195" alt="" style="margin-right:5px"/><br/>
+ width="200" height="195" alt="" style="margin-right:5px;margin-top:20px"/><br/>
<img src="{@docRoot}design/media/wear/Render_Ambient.png"
width="200" height="195" alt="" style="margin-right:5px"/>
</div>
@@ -118,11 +118,12 @@
<h3>Ambient mode</h3>
<p>Ambient mode helps the device conserve power. Your design should make clear to the user that
-the screen is in ambient mode by using only grayscale colors. Do not use a lot of white in ambient
-mode, since this distracting and hurts battery life on some screens. In this mode, the screen
-is only updated once every minute. Only show hours and minutes in ambient mode; do not show
-seconds. Your watch face is notified when the device switches to ambient mode, and you should
-thoughtfully design for it.</p>
+the screen is in ambient mode. The background color scheme is <em>strictly limited</em> to black,
+white, and grays. Your watch face may use some color elements on screens that support it
+provided it is unambiguous that the device is in ambient mode. You can use color elements for up
+to 5 percent of total pixels. In this mode, the screen is only updated once every minute. Only
+show hours and minutes in ambient mode; do not show seconds. Your watch face is notified when
+the device switches to ambient mode, and you should thoughtfully design for it.</p>
@@ -131,23 +132,29 @@
<p>Android Wear devices feature a variety of screen technologies, each with their own advantages
and considerations. One important consideration when designing the ambient mode display for your
-watch face is how it affects battery life and screen burn-in on some screens.</p>
-
-<p>You can configure your watch face to display different ambient designs depending on the kind
+watch face is how it affects battery life and screen burn-in on some screens.
+You can configure your watch face to display different ambient designs depending on the kind
of screen available on the device. Consider the best design for your watch faces on all
screens.</p>
<div class="layout-content-row" style="margin-top:20px">
<div class="layout-content-col span-9">
- <h3>Low bit</h3>
- <p>Pixels on some screens (including OLED and transflective LED) in ambient mode are either
- "on" or "off", also known as "low-bit". When designing for low-bit ambient mode, use only black
- and white, avoid grayscale colors, and disable antialiasing in your paint styles. Make sure to
- test your design on devices with low-bit ambient mode.</p>
+ <h3>Reduced color space</h3>
+ <p>Some displays use a reduced color space in ambient mode to save power.</p>
+ <p>One reduced color space power saving method is to use a "low-bit" mode. In low-bit mode,
+ the available colors are limited to black, white, blue, red, magenta, green, cyan, and yellow.
+ When designing for low-bit ambient mode, use a black or a white background. For OLED screens,
+ you must use a black background. Non-background pixels must be less than 10 percent of total
+ pixels. You can use low-bit color for up to 5 percent of pixels on screens that support it.
+ You should also disable antialiasing in your paint styles for this mode. Make sure to test
+ your design on devices with low-bit ambient mode.</p>
+ <p>Other displays save power in ambient mode by not producing any color. When designing for
+ displays which do not use color in ambient mode, the background may be either black or
+ white.</p>
</div>
<div class="layout-content-col span-4">
<img src="{@docRoot}design/media/wear/Render_LowBit.png" width="200"
- height="" alt="" style="margin-top:-30px;margin-left:13px">
+ height="" alt="" style="margin-top:45px;margin-left:13px">
</div>
</div>
@@ -164,7 +171,7 @@
</div>
<div class="layout-content-col span-4">
<img src="{@docRoot}design/media/wear/Render_1Bit.png" width="200"
- height="" alt="" style="margin-top:-30px;margin-left:13px">
+ height="" alt="" style="margin-top:-10px;margin-left:13px">
</div>
</div>
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/data-layer/messages.jd b/docs/html/training/wearables/data-layer/messages.jd
index 822e395..0ca55ba 100644
--- a/docs/html/training/wearables/data-layer/messages.jd
+++ b/docs/html/training/wearables/data-layer/messages.jd
@@ -39,16 +39,24 @@
</p>
<pre>
-Node node; // the connected device to send the message to
GoogleApiClient mGoogleApiClient;
-public static final START_ACTIVITY_PATH = "/start/MainActivity";
+public static final String START_ACTIVITY_PATH = "/start/MainActivity";
...
- SendMessageResult result = Wearable.MessageApi.sendMessage(
- mGoogleApiClient, node, START_ACTIVITY_PATH, null).await();
- if (!result.getStatus().isSuccess()) {
- Log.e(TAG, "ERROR: failed to send Message: " + result.getStatus());
- }
+private void sendStartActivityMessage(String nodeId) {
+ Wearable.MessageApi.sendMessage(
+ mGoogleApiClient, nodeId, START_ACTIVITY_PATH, new byte[0]).setResultCallback(
+ new ResultCallback<SendMessageResult>() {
+ @Override
+ public void onResult(SendMessageResult sendMessageResult) {
+ if (!sendMessageResult.getStatus().isSuccess()) {
+ Log.e(TAG, "Failed to send message with status code: "
+ + sendMessageResult.getStatus().getStatusCode());
+ }
+ }
+ }
+ );
+}
</pre>
<p>
diff --git a/docs/html/training/wearables/watch-faces/configuration.jd b/docs/html/training/wearables/watch-faces/configuration.jd
index edc7eac..5a4585d 100644
--- a/docs/html/training/wearables/watch-faces/configuration.jd
+++ b/docs/html/training/wearables/watch-faces/configuration.jd
@@ -92,7 +92,7 @@
<action android:name=
"com.example.android.wearable.watchface.CONFIG_DIGITAL" />
<category android:name=
- "com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
+ <strong>"com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION"</strong> />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
@@ -119,8 +119,21 @@
present users with elaborate color pickers to select the background color of a watch face.</p>
<p>To create a companion configuration activity, add a new activity to your handheld app module and
-declare the same intent filter for this activity as the one in <a href="#WearableActivity">Create
-a Wearable Configuration Activity</a>.</p>
+declare the following intent filter in the manifest file of the handheld app:</p>
+
+<pre>
+<activity
+ android:name=".DigitalWatchFaceCompanionConfigActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name=
+ "com.example.android.wearable.watchface.CONFIG_DIGITAL" />
+ <category android:name=
+ <strong>"com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION"</strong> />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+</activity>
+</pre>
<p>In your configuration activity, build a UI that provides options to customize all the
configurable elements of your watch face. When users make a selection, use the <a
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>
@Override
@@ -402,20 +402,17 @@
@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/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 9809606..1263447 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -661,7 +661,8 @@
mHasValidMask = true;
- if (maskType == MASK_NONE) {
+ final Rect bounds = getBounds();
+ if (maskType == MASK_NONE || bounds.isEmpty()) {
if (mMaskBuffer != null) {
mMaskBuffer.recycle();
mMaskBuffer = null;
@@ -674,7 +675,6 @@
}
// Ensure we have a correctly-sized buffer.
- final Rect bounds = getBounds();
if (mMaskBuffer == null
|| mMaskBuffer.getWidth() != bounds.width()
|| mMaskBuffer.getHeight() != bounds.height()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java b/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
index a9fdc86..67cfc59 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDualTileLabel.java
@@ -100,9 +100,6 @@
mFirstLineCaret.setImageDrawable(d);
if (d != null) {
final int h = d.getIntrinsicHeight();
- final LayoutParams lp = (LayoutParams) mSecondLine.getLayoutParams();
- lp.topMargin = h * 4 / 5;
- mSecondLine.setLayoutParams(lp);
mFirstLine.setMinHeight(h);
mFirstLine.setPadding(mHorizontalPaddingPx, 0, 0, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index a920624..e09024b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -30,6 +30,7 @@
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.SignalTileView;
+import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController.AccessPoint;
@@ -290,6 +291,11 @@
}
@Override
+ public void onSettingsActivityTriggered(Intent settingsIntent) {
+ mHost.startSettingsActivity(settingsIntent);
+ }
+
+ @Override
public void onDetailItemClick(Item item) {
if (item == null || item.tag == null) return;
final AccessPoint ap = (AccessPoint) item.tag;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index dc6af6a..ad4c211 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -125,7 +125,7 @@
Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
intent.putExtra(EXTRA_START_CONNECT_SSID, ap.ssid);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivityAsUser(intent, new UserHandle(mCurrentUser));
+ fireSettingsIntentCallback(intent);
return true;
} else {
WifiConfiguration config = new WifiConfiguration();
@@ -139,7 +139,13 @@
return false;
}
- private void fireCallback(AccessPoint[] aps) {
+ private void fireSettingsIntentCallback(Intent intent) {
+ for (AccessPointCallback callback : mCallbacks) {
+ callback.onSettingsActivityTriggered(intent);
+ }
+ }
+
+ private void fireAcccessPointsCallback(AccessPoint[] aps) {
for (AccessPointCallback callback : mCallbacks) {
callback.onAccessPointsChanged(aps);
}
@@ -208,7 +214,7 @@
aps.add(ap);
}
Collections.sort(aps, mByStrength);
- fireCallback(aps.toArray(new AccessPoint[aps.size()]));
+ fireAcccessPointsCallback(aps.toArray(new AccessPoint[aps.size()]));
}
private final ActionListener mConnectListener = new ActionListener() {
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/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index bcf08ff..3cffc85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import android.content.Intent;
+
public interface NetworkController {
boolean hasMobileDataFeature();
@@ -53,6 +55,7 @@
public interface AccessPointCallback {
void onAccessPointsChanged(AccessPoint[] accessPoints);
+ void onSettingsActivityTriggered(Intent settingsIntent);
}
public static class AccessPoint {
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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 10e4410..6d28382 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -19,11 +19,13 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import android.app.ActivityThread;
import android.os.Build;
import android.os.DeadObjectException;
import android.os.Handler;
@@ -34,6 +36,7 @@
import com.android.internal.app.ProcessStats;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.TransferPipe;
+import com.android.internal.util.FastPrintWriter;
import com.android.server.am.ActivityManagerService.ItemMatcher;
import com.android.server.am.ActivityManagerService.NeededUriGrants;
@@ -141,6 +144,19 @@
final ArrayList<ServiceRecord> mDestroyingServices
= new ArrayList<ServiceRecord>();
+ /** Amount of time to allow a last ANR message to exist before freeing the memory. */
+ static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours
+
+ String mLastAnrDump;
+
+ final Runnable mLastAnrDumpClearer = new Runnable() {
+ @Override public void run() {
+ synchronized (mAm) {
+ mLastAnrDump = null;
+ }
+ }
+ };
+
static final class DelayingProcess extends ArrayList<ServiceRecord> {
long timeoout;
}
@@ -1677,6 +1693,7 @@
try {
bumpServiceExecutingLocked(r, false, "destroy");
mDestroyingServices.add(r);
+ r.destroying = true;
mAm.updateOomAdjLocked(r.app);
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
@@ -1798,7 +1815,7 @@
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
- if (type == 1) {
+ if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart = true;
@@ -1847,6 +1864,20 @@
if (res == Service.START_STICKY_COMPATIBILITY) {
r.callStart = false;
}
+ } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
+ // This is the final call from destroying the service... we should
+ // actually be getting rid of the service at this point. Do some
+ // validation of its state, and ensure it will be fully removed.
+ if (!inDestroying) {
+ // Not sure what else to do with this... if it is not actually in the
+ // destroying list, we don't need to make sure to remove it from it.
+ Slog.wtfStack(TAG, "Service done with onDestroy, but not inDestroying: "
+ + r);
+ } else if (r.executeNesting != 1) {
+ Slog.wtfStack(TAG, "Service done with onDestroy, but executeNesting="
+ + r.executeNesting + ": " + r);
+ r.executeNesting = 1;
+ }
}
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
@@ -2390,23 +2421,15 @@
}
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
- StringBuilder sb = new StringBuilder();
- sb.append("sxecuting service ");
- sb.append(timeout.shortName);
- sb.append(" (execStart=");
- TimeUtils.formatDuration(timeout.executingStart - now, sb);
- sb.append(", nesting=");
- sb.append(timeout.executeNesting);
- sb.append(", destroyed=");
- TimeUtils.formatDuration(timeout.destroyTime - now, sb);
- sb.append(", fg=");
- sb.append(proc.execServicesFg);
- sb.append(", create=");
- TimeUtils.formatDuration(timeout.createTime - now, sb);
- sb.append(", proc=");
- sb.append(timeout.app != null ? timeout.app.toShortString() : "null");
- sb.append(")");
- anrMessage = sb.toString();
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new FastPrintWriter(sw, false, 1024);
+ pw.println(timeout);
+ timeout.dump(pw, " ");
+ pw.close();
+ mLastAnrDump = sw.toString();
+ mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
+ mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
+ anrMessage = "executing service " + timeout.shortName;
} else {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
@@ -2446,6 +2469,11 @@
pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
try {
+ if (mLastAnrDump != null) {
+ pw.println(" Last ANR service:");
+ pw.print(mLastAnrDump);
+ pw.println();
+ }
int[] users = mAm.getUsersLocked();
for (int user : users) {
ServiceMap smap = getServiceMap(user);
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/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8381c4a..1cb1bb8 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -105,6 +105,7 @@
long restartDelay; // delay until next restart attempt.
long restartTime; // time of last restart.
long nextRestartTime; // time when restartDelay will expire.
+ boolean destroying; // set when we have started destroying the service
long destroyTime; // time at which destory was initiated.
String stringName; // caching of toString
@@ -251,9 +252,11 @@
TimeUtils.formatDuration(executingStart, now, pw);
pw.println();
}
- if (destroyTime != 0) {
- pw.print(" destroyed=");
- TimeUtils.formatDuration(destroyTime, now, pw);
+ if (destroying || destroyTime != 0) {
+ pw.print(prefix); pw.print("destroying="); pw.print(destroying);
+ pw.print(" destroyTime=");
+ TimeUtils.formatDuration(destroyTime, now, pw);
+ pw.println();
}
if (crashCount != 0 || restartCount != 0
|| restartDelay != 0 || nextRestartTime != 0) {
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/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 435ab7f..ce5b9ab7 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -119,10 +119,9 @@
private void handleReportPowerStatus(int devicePowerStatus) {
if (isPowerOnOrTransient(getTvPowerStatus())) {
+ tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
if (isPowerOnOrTransient(devicePowerStatus)) {
sendSetStreamPath();
- } else {
- tv().updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
}
}
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
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() {