diff options
| -rw-r--r-- | apct-tests/perftests/multiuser/Android.bp | 1 | ||||
| -rw-r--r-- | apct-tests/perftests/multiuser/AndroidTest.xml | 37 | ||||
| -rw-r--r-- | apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto | 154 | ||||
| -rw-r--r-- | api/Android.bp | 38 | ||||
| -rw-r--r-- | api/api_versions_trimmer_unittests.py | 2 | ||||
| -rwxr-xr-x | api/merge_annotation_zips.py | 71 | ||||
| -rw-r--r-- | api/merge_annotation_zips_test.py | 124 | ||||
| -rw-r--r-- | core/java/android/net/nsd/NsdManager.java | 32 | ||||
| -rw-r--r-- | core/java/android/os/BaseBundle.java | 2 | ||||
| -rw-r--r-- | core/java/android/os/Parcel.java | 2 | ||||
| -rw-r--r-- | core/java/android/permission/OWNERS | 3 | ||||
| -rw-r--r-- | core/java/android/util/ArrayMap.java | 2 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 5 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 1 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/BundleTest.java | 205 | ||||
| -rw-r--r-- | services/core/java/com/android/server/NsdService.java | 37 | ||||
| -rw-r--r-- | services/core/java/com/android/server/hdmi/HdmiControlService.java | 6 | ||||
| -rw-r--r-- | tests/StagedInstallTest/Android.bp | 2 |
18 files changed, 702 insertions, 22 deletions
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp index 91e207433f3d..c967e51e16f9 100644 --- a/apct-tests/perftests/multiuser/Android.bp +++ b/apct-tests/perftests/multiuser/Android.bp @@ -31,5 +31,6 @@ android_test { ], platform_apis: true, test_suites: ["device-tests"], + data: ["trace_configs/*"], certificate: "platform", } diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml index 9117561fe1e5..bec3cc9272c3 100644 --- a/apct-tests/perftests/multiuser/AndroidTest.xml +++ b/apct-tests/perftests/multiuser/AndroidTest.xml @@ -16,14 +16,51 @@ <configuration description="Runs MultiUserPerfTests metric instrumentation."> <option name="test-suite-tag" value="apct" /> <option name="test-suite-tag" value="apct-metric-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="MultiUserPerfTests.apk" /> <option name="test-file-name" value="MultiUserPerfDummyApp.apk" /> </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" key="trace_config_multi_user.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config_multi_user.textproto" value="/sdcard/sample.textproto" /> + </target_preparer> + + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path" /> + </metrics_collector> + + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.perftests.multiuser" /> <option name="hidden-api-checks" value="false"/> + + <!-- Listener related args for collecting the traces and waiting for the device to stabilize. --> + <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" /> + <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. --> + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + + <!-- ProcLoadListener related arguments --> + <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run --> + <option name="instrumentation-arg" key="procload-collector:per_run" value="true" /> + <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" /> + <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" /> + <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" /> + + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" /> + <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" /> + + <option name="instrumentation-arg" key="newRunListenerMode" value="true" /> + </test> </configuration> diff --git a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto new file mode 100644 index 000000000000..93b06e84e130 --- /dev/null +++ b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto @@ -0,0 +1,154 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 1000 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 10000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers { + size_kb: 32768 + fill_policy: RING_BUFFER +} + +# procfs polling +buffers { + size_kb: 8192 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.ftrace" + target_buffer: 0 + ftrace_config { + # These parameters affect only the kernel trace buffer size and how + # frequently it gets moved into the userspace buffer defined above. + buffer_size_kb: 16384 + drain_period_ms: 250 + + # Store certain high-volume "sched" ftrace events in a denser format + # (falling back to the default format if not supported by the tracer). + compact_sched { + enabled: true + } + + # Enables symbol name resolution against /proc/kallsyms + symbolize_ksyms: true + + # We need to do process tracking to ensure kernel ftrace events targeted at short-lived + # threads are associated correctly + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + ftrace_events: "sched/sched_process_exit" + ftrace_events: "sched/sched_process_free" + + # Memory events + ftrace_events: "rss_stat" + ftrace_events: "ion_heap_shrink" + ftrace_events: "ion_heap_grow" + ftrace_events: "ion/ion_stat" + ftrace_events: "dmabuf_heap/dma_heap_stat" + ftrace_events: "oom_score_adj_update" + ftrace_events: "gpu_mem/gpu_mem_total" + + # Old (kernel) LMK + ftrace_events: "lowmemorykiller/lowmemory_kill" + + atrace_apps: "*" + + atrace_categories: "am" + atrace_categories: "binder_driver" + atrace_categories: "bionic" + atrace_categories: "dalvik" + atrace_categories: "input" + atrace_categories: "pm" + atrace_categories: "res" + atrace_categories: "rro" + atrace_categories: "ss" + atrace_categories: "view" + atrace_categories: "wm" + + atrace_categories: "freq" + atrace_categories: "sched" + atrace_categories: "sync" + atrace_categories: "workq" + + } + } +} + +data_sources: { + config { + name: "android.gpu.memory" + target_buffer: 0 + } +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 1 + process_stats_config { + proc_stats_poll_ms: 10000 + } + } +} + +data_sources { + config { + name: "linux.sys_stats" + target_buffer: 1 + sys_stats_config { + meminfo_period_ms: 1000 + meminfo_counters: MEMINFO_MEM_TOTAL + meminfo_counters: MEMINFO_MEM_FREE + meminfo_counters: MEMINFO_MEM_AVAILABLE + meminfo_counters: MEMINFO_BUFFERS + meminfo_counters: MEMINFO_CACHED + meminfo_counters: MEMINFO_SWAP_CACHED + meminfo_counters: MEMINFO_ACTIVE + meminfo_counters: MEMINFO_INACTIVE + meminfo_counters: MEMINFO_ACTIVE_ANON + meminfo_counters: MEMINFO_INACTIVE_ANON + meminfo_counters: MEMINFO_ACTIVE_FILE + meminfo_counters: MEMINFO_INACTIVE_FILE + meminfo_counters: MEMINFO_UNEVICTABLE + meminfo_counters: MEMINFO_SWAP_TOTAL + meminfo_counters: MEMINFO_SWAP_FREE + meminfo_counters: MEMINFO_DIRTY + meminfo_counters: MEMINFO_WRITEBACK + meminfo_counters: MEMINFO_ANON_PAGES + meminfo_counters: MEMINFO_MAPPED + meminfo_counters: MEMINFO_SHMEM + } + } +} + +data_sources: { + config: { + name: "android.surfaceflinger.frametimeline" + } +} diff --git a/api/Android.bp b/api/Android.bp index 8dff60af8bbd..0acd759bc73e 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -24,9 +24,8 @@ package { default_applicable_licenses: ["frameworks_base_license"], } -python_binary_host { - name: "api_versions_trimmer", - srcs: ["api_versions_trimmer.py"], +python_defaults { + name: "python3_version_defaults", version: { py2: { enabled: false, @@ -38,6 +37,12 @@ python_binary_host { }, } +python_binary_host { + name: "api_versions_trimmer", + srcs: ["api_versions_trimmer.py"], + defaults: ["python3_version_defaults"], +} + python_test_host { name: "api_versions_trimmer_unittests", main: "api_versions_trimmer_unittests.py", @@ -45,17 +50,28 @@ python_test_host { "api_versions_trimmer_unittests.py", "api_versions_trimmer.py", ], + defaults: ["python3_version_defaults"], test_options: { unit_test: true, }, - version: { - py2: { - enabled: false, - }, - py3: { - enabled: true, - embedded_launcher: false, - }, +} + +python_binary_host { + name: "merge_annotation_zips", + srcs: ["merge_annotation_zips.py"], + defaults: ["python3_version_defaults"], +} + +python_test_host { + name: "merge_annotation_zips_test", + main: "merge_annotation_zips_test.py", + srcs: [ + "merge_annotation_zips.py", + "merge_annotation_zips_test.py", + ], + defaults: ["python3_version_defaults"], + test_options: { + unit_test: true, }, } diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py index 4eb929ea1b5d..d2e5b7d1a07e 100644 --- a/api/api_versions_trimmer_unittests.py +++ b/api/api_versions_trimmer_unittests.py @@ -304,4 +304,4 @@ sultCallback;Landroid/os/Handler;)Z" since="24"/> if __name__ == "__main__": - unittest.main() + unittest.main(verbosity=2) diff --git a/api/merge_annotation_zips.py b/api/merge_annotation_zips.py new file mode 100755 index 000000000000..9c67d7bded76 --- /dev/null +++ b/api/merge_annotation_zips.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Script to merge annotation XML files (created by e.g. metalava).""" + +from pathlib import Path +import sys +import xml.etree.ElementTree as ET +import zipfile + + +def validate_xml_assumptions(root): + """Verify the format of the annotations XML matches expectations""" + prevName = "" + assert root.tag == 'root' + for child in root: + assert child.tag == 'item', 'unexpected tag: %s' % child.tag + assert list(child.attrib.keys()) == ['name'], 'unexpected attribs: %s' % child.attrib.keys() + assert prevName < child.get('name'), 'items unexpectedly not strictly sorted (possibly duplicate entries)' + prevName = child.get('name') + + +def merge_xml(a, b): + """Merge two annotation xml files""" + for xml in [a, b]: + validate_xml_assumptions(xml) + a.extend(b[:]) + a[:] = sorted(a[:], key=lambda x: x.get('name')) + validate_xml_assumptions(a) + + +def merge_zip_file(out_dir, zip_file): + """Merge the content of the zip_file into out_dir""" + for filename in zip_file.namelist(): + path = Path(out_dir, filename) + if path.exists(): + existing_xml = ET.parse(path) + with zip_file.open(filename) as other_file: + other_xml = ET.parse(other_file) + merge_xml(existing_xml.getroot(), other_xml.getroot()) + existing_xml.write(path, encoding='UTF-8', xml_declaration=True) + else: + zip_file.extract(filename, out_dir) + + +def main(): + out_dir = Path(sys.argv[1]) + zip_filenames = sys.argv[2:] + + assert not out_dir.exists() + out_dir.mkdir() + for zip_filename in zip_filenames: + with zipfile.ZipFile(zip_filename) as zip_file: + merge_zip_file(out_dir, zip_file) + + +if __name__ == "__main__": + main() diff --git a/api/merge_annotation_zips_test.py b/api/merge_annotation_zips_test.py new file mode 100644 index 000000000000..26795c47af9e --- /dev/null +++ b/api/merge_annotation_zips_test.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import io +from pathlib import Path +import tempfile +import unittest +import zipfile + +import merge_annotation_zips + + +zip_a = { + 'android/provider/annotations.xml': + """<?xml version="1.0" encoding="UTF-8"?> +<root> + <item name="android.provider.BlockedNumberContract boolean isBlocked(android.content.Context, java.lang.String)"> + <annotation name="androidx.annotation.WorkerThread"/> + </item> + <item name="android.provider.SimPhonebookContract.SimRecords android.net.Uri getItemUri(int, int, int) 2"> + <annotation name="androidx.annotation.IntRange"> + <val name="from" val="1" /> + </annotation> + </item> +</root>""", + 'android/os/annotations.xml': + """<?xml version="1.0" encoding="UTF-8"?> +<root> + <item name="android.app.ActionBar void setCustomView(int) 0"> + <annotation name="androidx.annotation.LayoutRes"/> + </item> +</root> +""" +} + +zip_b = { + 'android/provider/annotations.xml': + """<?xml version="1.0" encoding="UTF-8"?> +<root> + <item name="android.provider.MediaStore QUERY_ARG_MATCH_FAVORITE"> + <annotation name="androidx.annotation.IntDef"> + <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" /> + <val name="flag" val="true" /> + </annotation> + </item> + <item name="android.provider.MediaStore QUERY_ARG_MATCH_PENDING"> + <annotation name="androidx.annotation.IntDef"> + <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" /> + <val name="flag" val="true" /> + </annotation> + </item> +</root>""" +} + +zip_c = { + 'android/app/annotations.xml': + """<?xml version="1.0" encoding="UTF-8"?> +<root> + <item name="android.app.ActionBar void setCustomView(int) 0"> + <annotation name="androidx.annotation.LayoutRes"/> + </item> +</root>""" +} + +merged_provider = """<?xml version='1.0' encoding='UTF-8'?> +<root> + <item name="android.provider.BlockedNumberContract boolean isBlocked(android.content.Context, java.lang.String)"> + <annotation name="androidx.annotation.WorkerThread" /> + </item> + <item name="android.provider.MediaStore QUERY_ARG_MATCH_FAVORITE"> + <annotation name="androidx.annotation.IntDef"> + <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" /> + <val name="flag" val="true" /> + </annotation> + </item> + <item name="android.provider.MediaStore QUERY_ARG_MATCH_PENDING"> + <annotation name="androidx.annotation.IntDef"> + <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" /> + <val name="flag" val="true" /> + </annotation> + </item> +<item name="android.provider.SimPhonebookContract.SimRecords android.net.Uri getItemUri(int, int, int) 2"> + <annotation name="androidx.annotation.IntRange"> + <val name="from" val="1" /> + </annotation> + </item> +</root>""" + + + +class MergeAnnotationZipsTest(unittest.TestCase): + + def test_merge_zips(self): + with tempfile.TemporaryDirectory() as out_dir: + for zip_content in [zip_a, zip_b, zip_c]: + f = io.BytesIO() + with zipfile.ZipFile(f, "w") as zip_file: + for filename, content in zip_content.items(): + zip_file.writestr(filename, content) + merge_annotation_zips.merge_zip_file(out_dir, zip_file) + + # Unchanged + self.assertEqual(zip_a['android/os/annotations.xml'], Path(out_dir, 'android/os/annotations.xml').read_text()) + self.assertEqual(zip_c['android/app/annotations.xml'], Path(out_dir, 'android/app/annotations.xml').read_text()) + + # Merged + self.assertEqual(merged_provider, Path(out_dir, 'android/provider/annotations.xml').read_text()) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 5a25cfc00cd4..ae8d0101947d 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -23,6 +23,9 @@ import static com.android.internal.util.Preconditions.checkStringNotEmpty; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; @@ -126,6 +129,24 @@ public final class NsdManager { private static final boolean DBG = false; /** + * When enabled, apps targeting < Android 12 are considered legacy for + * the NSD native daemon. + * The platform will only keep the daemon running as long as there are + * any legacy apps connected. + * + * After Android 12, directly communicate with native daemon might not + * work since the native damon won't always stay alive. + * Use the NSD APIs from NsdManager as the replacement is recommended. + * An another alternative could be bundling your own mdns solutions instead of + * depending on the system mdns native daemon. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) + public static final long RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS = 191844585L; + + /** * Broadcast intent action to indicate whether network service discovery is * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state * information as int. @@ -203,6 +224,9 @@ public final class NsdManager { public static final int DAEMON_CLEANUP = BASE + 21; /** @hide */ + public static final int DAEMON_STARTUP = BASE + 22; + + /** @hide */ public static final int ENABLE = BASE + 24; /** @hide */ public static final int DISABLE = BASE + 25; @@ -232,6 +256,8 @@ public final class NsdManager { EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE"); EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED"); EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED"); + EVENT_NAMES.put(DAEMON_CLEANUP, "DAEMON_CLEANUP"); + EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP"); EVENT_NAMES.put(ENABLE, "ENABLE"); EVENT_NAMES.put(DISABLE, "DISABLE"); EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT"); @@ -494,6 +520,12 @@ public final class NsdManager { } catch (InterruptedException e) { fatal("Interrupted wait at init"); } + if (CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) { + return; + } + // Only proactively start the daemon if the target SDK < S, otherwise the internal service + // would automatically start/stop the native daemon as needed. + mAsyncChannel.sendMessage(DAEMON_STARTUP); } private static void fatal(String msg) { diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index fb21ce30415f..64d54b820a92 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -431,7 +431,7 @@ public class BaseBundle { * * @hide */ - public static boolean kindofEquals(BaseBundle a, BaseBundle b) { + public static boolean kindofEquals(@Nullable BaseBundle a, @Nullable BaseBundle b) { return (a == b) || (a != null && a.kindofEquals(b)); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 7f2e2b115986..e809912f15df 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -3363,7 +3363,7 @@ public final class Parcel { object = readValue(type, loader); int actual = dataPosition() - start; if (actual != length) { - Log.w(TAG, + Slog.wtfStack(TAG, "Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type) + " consumed " + actual + " bytes, but " + length + " expected."); } diff --git a/core/java/android/permission/OWNERS b/core/java/android/permission/OWNERS index 19a3a8bd514a..b5466b6d3cb5 100644 --- a/core/java/android/permission/OWNERS +++ b/core/java/android/permission/OWNERS @@ -1,11 +1,12 @@ # Bug component: 137825 -eugenesusla@google.com evanseverson@google.com evanxinchen@google.com ewol@google.com guojing@google.com jaysullivan@google.com +olekarg@google.com +pyuli@google.com ntmyren@google.com svetoslavganov@android.com svetoslavganov@google.com diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 4cf0a36d24bb..418d92c44290 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -645,7 +645,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { e.fillInStackTrace(); Log.w(TAG, "New hash " + hash + " is before end of array hash " + mHashes[index-1] - + " at index " + index + " key " + key, e); + + " at index " + index + (DEBUG ? " key " + key : ""), e); put(key, value); return; } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 14d314775482..ac1912143dd7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3026,6 +3026,11 @@ and one pSIM) --> <integer name="config_num_physical_slots">1</integer> + <!-- When a radio power off request is received, we will delay completing the request until + either IMS moves to the deregistered state or the timeout defined by this configuration + elapses. If 0, this feature is disabled and we do not delay radio power off requests.--> + <integer name="config_delay_for_ims_dereg_millis">0</integer> + <!--Thresholds for LTE dbm in status bar--> <integer-array translatable="false" name="config_lteDbmThresholds"> <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 2be5152ce2d9..aebad6afc042 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -476,6 +476,7 @@ <java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" /> <java-symbol type="string" name="config_deviceSpecificAudioService" /> <java-symbol type="integer" name="config_num_physical_slots" /> + <java-symbol type="integer" name="config_delay_for_ims_dereg_millis" /> <java-symbol type="array" name="config_integrityRuleProviderPackages" /> <java-symbol type="bool" name="config_useAssistantVolume" /> <java-symbol type="string" name="config_bandwidthEstimateSource" /> diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java index 4cc70ba6448d..9d2cab3f7026 100644 --- a/core/tests/coretests/src/android/os/BundleTest.java +++ b/core/tests/coretests/src/android/os/BundleTest.java @@ -16,16 +16,24 @@ package android.os; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import android.util.Log; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Objects; + /** * Unit tests for bundle that requires accessing hidden APS. Tests that can be written only with * public APIs should go in the CTS counterpart. @@ -35,6 +43,14 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class BundleTest { + private Log.TerribleFailureHandler mWtfHandler; + + @After + public void tearDown() throws Exception { + if (mWtfHandler != null) { + Log.setWtfHandler(mWtfHandler); + } + } /** * Take a bundle, write it to a parcel and return the parcel. @@ -217,4 +233,193 @@ public class BundleTest { // return true assertTrue(BaseBundle.kindofEquals(bundle1, bundle2)); } + + @Test + public void kindofEquals_lazyValues() { + Parcelable p1 = new CustomParcelable(13, "Tiramisu"); + Parcelable p2 = new CustomParcelable(13, "Tiramisu"); + + // 2 maps with live objects + Bundle a = new Bundle(); + a.putParcelable("key1", p1); + Bundle b = new Bundle(); + b.putParcelable("key1", p2); + assertTrue(Bundle.kindofEquals(a, b)); + + // 2 identical parcels + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(getClass().getClassLoader()); + assertTrue(Bundle.kindofEquals(a, b)); + + // 2 lazy values with identical parcels inside + a.isEmpty(); + b.isEmpty(); + assertTrue(Bundle.kindofEquals(a, b)); + + // 1 lazy value vs 1 live object + a.getParcelable("key1"); + assertFalse(Bundle.kindofEquals(a, b)); + + // 2 live objects + b.getParcelable("key1"); + assertTrue(Bundle.kindofEquals(a, b)); + } + + @Test + public void kindofEquals_lazyValuesWithIdenticalParcels_returnsTrue() { + Parcelable p1 = new CustomParcelable(13, "Tiramisu"); + Parcelable p2 = new CustomParcelable(13, "Tiramisu"); + Bundle a = new Bundle(); + a.putParcelable("key", p1); + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + Bundle b = new Bundle(); + b.putParcelable("key", p2); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(getClass().getClassLoader()); + // 2 lazy values with identical parcels inside + a.isEmpty(); + b.isEmpty(); + + assertTrue(Bundle.kindofEquals(a, b)); + } + + @Test + public void kindofEquals_lazyValuesAndDifferentClassLoaders_returnsFalse() { + Parcelable p1 = new CustomParcelable(13, "Tiramisu"); + Parcelable p2 = new CustomParcelable(13, "Tiramisu"); + Bundle a = new Bundle(); + a.putParcelable("key", p1); + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + Bundle b = new Bundle(); + b.putParcelable("key", p2); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(Bundle.class.getClassLoader()); // BCP + // 2 lazy values with identical parcels inside + a.isEmpty(); + b.isEmpty(); + + assertFalse(Bundle.kindofEquals(a, b)); + } + + @Test + public void kindofEquals_lazyValuesOfDifferentTypes_returnsFalse() { + Parcelable p = new CustomParcelable(13, "Tiramisu"); + Parcelable[] ps = {p}; + Bundle a = new Bundle(); + a.putParcelable("key", p); + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + Bundle b = new Bundle(); + b.putParcelableArray("key", ps); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(getClass().getClassLoader()); + a.isEmpty(); + b.isEmpty(); + + assertFalse(Bundle.kindofEquals(a, b)); + } + + @Test + public void kindofEquals_lazyValuesWithDifferentLengths_returnsFalse() { + Parcelable p1 = new CustomParcelable(13, "Tiramisu"); + Parcelable p2 = new CustomParcelable(13, "Tiramisuuuuuuuu"); + Bundle a = new Bundle(); + a.putParcelable("key", p1); + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + Bundle b = new Bundle(); + b.putParcelable("key", p2); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(getClass().getClassLoader()); + a.isEmpty(); + b.isEmpty(); + + assertFalse(Bundle.kindofEquals(a, b)); + } + + @Test + public void readWriteLengthMismatch_logsWtf() throws Exception { + mWtfHandler = Log.setWtfHandler((tag, e, system) -> { + throw new RuntimeException(e); + }); + Parcelable parcelable = new CustomParcelable(13, "Tiramisu").setHasLengthMismatch(true); + Bundle bundle = new Bundle(); + bundle.putParcelable("p", parcelable); + bundle.readFromParcel(getParcelledBundle(bundle)); + bundle.setClassLoader(getClass().getClassLoader()); + RuntimeException e = assertThrows(RuntimeException.class, () -> bundle.getParcelable("p")); + assertThat(e.getCause()).isInstanceOf(Log.TerribleFailure.class); + } + + private static class CustomParcelable implements Parcelable { + public final int integer; + public final String string; + public boolean hasLengthMismatch; + + CustomParcelable(int integer, String string) { + this.integer = integer; + this.string = string; + } + + protected CustomParcelable(Parcel in) { + integer = in.readInt(); + string = in.readString(); + hasLengthMismatch = in.readBoolean(); + } + + public CustomParcelable setHasLengthMismatch(boolean hasLengthMismatch) { + this.hasLengthMismatch = hasLengthMismatch; + return this; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(integer); + out.writeString(string); + out.writeBoolean(hasLengthMismatch); + if (hasLengthMismatch) { + out.writeString("extra-write"); + } + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof CustomParcelable)) { + return false; + } + CustomParcelable + that = (CustomParcelable) other; + return integer == that.integer + && hasLengthMismatch == that.hasLengthMismatch + && string.equals(that.string); + } + + @Override + public int hashCode() { + return Objects.hash(integer, string, hasLengthMismatch); + } + + public static final Creator<CustomParcelable> CREATOR = new Creator<CustomParcelable>() { + @Override + public CustomParcelable createFromParcel(Parcel in) { + return new CustomParcelable(in); + } + @Override + public CustomParcelable[] newArray(int size) { + return new CustomParcelable[size]; + } + }; + } } diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java index a9f3a1b63b40..462ed5c70d36 100644 --- a/services/core/java/com/android/server/NsdService.java +++ b/services/core/java/com/android/server/NsdService.java @@ -82,6 +82,8 @@ public class NsdService extends INsdManager.Stub { private static final int INVALID_ID = 0; private int mUniqueId = 1; + // The count of the connected legacy clients. + private int mLegacyClientCount = 0; private class NsdStateMachine extends StateMachine { @@ -107,7 +109,9 @@ public class NsdService extends INsdManager.Stub { sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs); } private void maybeScheduleStop() { - if (!isAnyRequestActive()) { + // The native daemon should stay alive and can't be cleanup + // if any legacy client connected. + if (!isAnyRequestActive() && mLegacyClientCount == 0) { scheduleStop(); } } @@ -175,11 +179,11 @@ public class NsdService extends INsdManager.Stub { if (cInfo != null) { cInfo.expungeAllRequests(); mClients.remove(msg.replyTo); + if (cInfo.isLegacy()) { + mLegacyClientCount -= 1; + } } - //Last client - if (mClients.size() == 0) { - scheduleStop(); - } + maybeScheduleStop(); break; case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: AsyncChannel ac = new AsyncChannel(); @@ -208,6 +212,17 @@ public class NsdService extends INsdManager.Stub { case NsdManager.DAEMON_CLEANUP: mDaemon.maybeStop(); break; + // This event should be only sent by the legacy (target SDK < S) clients. + // Mark the sending client as legacy. + case NsdManager.DAEMON_STARTUP: + cInfo = mClients.get(msg.replyTo); + if (cInfo != null) { + cancelStop(); + cInfo.setLegacy(); + mLegacyClientCount += 1; + maybeStartDaemon(); + } + break; case NsdManager.NATIVE_DAEMON_EVENT: default: Slog.e(TAG, "Unhandled " + msg); @@ -863,6 +878,9 @@ public class NsdService extends INsdManager.Stub { /* A map from client id to the type of the request we had received */ private final SparseIntArray mClientRequests = new SparseIntArray(); + // The target SDK of this client < Build.VERSION_CODES.S + private boolean mIsLegacy = false; + private ClientInfo(AsyncChannel c, Messenger m) { mChannel = c; mMessenger = m; @@ -875,6 +893,7 @@ public class NsdService extends INsdManager.Stub { sb.append("mChannel ").append(mChannel).append("\n"); sb.append("mMessenger ").append(mMessenger).append("\n"); sb.append("mResolvedService ").append(mResolvedService).append("\n"); + sb.append("mIsLegacy ").append(mIsLegacy).append("\n"); for(int i = 0; i< mClientIds.size(); i++) { int clientID = mClientIds.keyAt(i); sb.append("clientId ").append(clientID). @@ -884,6 +903,14 @@ public class NsdService extends INsdManager.Stub { return sb.toString(); } + private boolean isLegacy() { + return mIsLegacy; + } + + private void setLegacy() { + mIsLegacy = true; + } + // Remove any pending requests from the global map when we get rid of a client, // and send cancellations to the daemon. private void expungeAllRequests() { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index e951053b6ec7..b049d012f0ae 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -556,6 +556,12 @@ public class HdmiControlService extends SystemService { // on boot, if device is interactive, set HDMI CEC state as powered on as well if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) { mPowerStatus = HdmiControlManager.POWER_STATUS_ON; + // Start all actions that were queued because the device was in standby + if (mAddressAllocated) { + for (HdmiCecLocalDevice localDevice : getAllLocalDevices()) { + localDevice.startQueuedActions(); + } + } } } diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index 840a588bfe88..086ef95877e6 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -50,7 +50,7 @@ java_test_host { "cts-install-lib-host", ], data: [ - ":com.android.apex.cts.shim.v2_prebuilt", + ":StagedInstallTestApexV2", ":TestAppAv1", ], test_suites: ["general-tests"], |