diff options
17 files changed, 512 insertions, 62 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/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 6da02f5c9ff5..fb21ce30415f 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -260,6 +260,9 @@ public class BaseBundle { /** * Returns the value for key {@code key}. * + * This call should always be made after {@link #unparcel()} or inside a lock after making sure + * {@code mMap} is not null. + * * @hide */ final Object getValue(String key) { @@ -270,15 +273,15 @@ public class BaseBundle { /** * Returns the value for a certain position in the array map. * + * This call should always be made after {@link #unparcel()} or inside a lock after making sure + * {@code mMap} is not null. + * * @hide */ final Object getValueAt(int i) { Object object = mMap.valueAt(i); if (object instanceof Supplier<?>) { - Supplier<?> supplier = (Supplier<?>) object; - synchronized (this) { - object = supplier.get(); - } + object = ((Supplier<?>) object).get(); mMap.setValueAt(i, object); } return object; diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index a870c04f9cd3..c575c80e41fc 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -69,3 +69,6 @@ per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS # UpdateEngine per-file *UpdateEngine* = file:/platform/system/update_engine:/OWNERS + +# VINTF +per-file Vintf* = file:/platform/system/libvintf:/OWNERS diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index a2716d211bbf..7f2e2b115986 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -16,6 +16,8 @@ package android.os; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; @@ -3412,12 +3414,19 @@ public final class Parcel { private final int mLength; private final int mType; @Nullable private final ClassLoader mLoader; - @Nullable private Parcel mSource; @Nullable private Object mObject; - @Nullable private Parcel mValueParcel; + @Nullable private volatile Parcel mValueParcel; + + /** + * This goes from non-null to null once. Always check the nullability of this object before + * performing any operations, either involving itself or mObject since the happens-before + * established by this volatile will guarantee visibility of either. We can assume this + * parcel won't change anymore. + */ + @Nullable private volatile Parcel mSource; LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) { - mSource = source; + mSource = requireNonNull(source); mPosition = position; mLength = length; mType = type; @@ -3426,38 +3435,41 @@ public final class Parcel { @Override public Object get() { - if (mObject == null) { - int restore = mSource.dataPosition(); - try { - mSource.setDataPosition(mPosition); - mObject = mSource.readValue(mLoader); - } finally { - mSource.setDataPosition(restore); - } - mSource = null; - if (mValueParcel != null) { - mValueParcel.recycle(); - mValueParcel = null; + Parcel source = mSource; + if (source != null) { + synchronized (source) { + int restore = source.dataPosition(); + try { + source.setDataPosition(mPosition); + mObject = source.readValue(mLoader); + } finally { + source.setDataPosition(restore); + } + mSource = null; } } return mObject; } public void writeToParcel(Parcel out) { - if (mObject == null) { - out.appendFrom(mSource, mPosition, mLength + 8); + Parcel source = mSource; + if (source != null) { + out.appendFrom(source, mPosition, mLength + 8); } else { out.writeValue(mObject); } } public boolean hasFileDescriptors() { - return getValueParcel().hasFileDescriptors(); + Parcel source = mSource; + return (source != null) + ? getValueParcel(source).hasFileDescriptors() + : Parcel.hasFileDescriptors(mObject); } @Override public String toString() { - return mObject == null + return (mSource != null) ? "Supplier{" + valueTypeToString(mType) + "@" + mPosition + "+" + mLength + '}' : "Supplier{" + mObject + "}"; } @@ -3476,41 +3488,49 @@ public final class Parcel { return false; } LazyValue value = (LazyValue) other; - // Check if they are either both serialized or both deserialized - if ((mObject == null) != (value.mObject == null)) { + // Check if they are either both serialized or both deserialized. + Parcel source = mSource; + Parcel otherSource = value.mSource; + if ((source == null) != (otherSource == null)) { return false; } - // If both are deserialized, compare the live objects - if (mObject != null) { - return mObject.equals(value.mObject); + // If both are deserialized, compare the live objects. + if (source == null) { + // Note that here it's guaranteed that both mObject references contain valid values + // (possibly null) since mSource will have provided the memory barrier for those and + // once deserialized we never go back to serialized state. + return Objects.equals(mObject, value.mObject); } - // Better safely fail here since this could mean we get different objects + // Better safely fail here since this could mean we get different objects. if (!Objects.equals(mLoader, value.mLoader)) { return false; } - // Otherwise compare metadata prior to comparing payload + // Otherwise compare metadata prior to comparing payload. if (mType != value.mType || mLength != value.mLength) { return false; } - // Finally we compare the payload - return getValueParcel().compareData(value.getValueParcel()) == 0; + // Finally we compare the payload. + return getValueParcel(source).compareData(value.getValueParcel(otherSource)) == 0; } @Override public int hashCode() { - return Objects.hash(mObject, mLoader, mType, mLength); + // Accessing mSource first to provide memory barrier for mObject + return Objects.hash(mSource == null, mObject, mLoader, mType, mLength); } /** This extracts the parcel section responsible for the object and returns it. */ - private Parcel getValueParcel() { - if (mValueParcel == null) { - mValueParcel = Parcel.obtain(); + private Parcel getValueParcel(Parcel source) { + Parcel parcel = mValueParcel; + if (parcel == null) { + parcel = Parcel.obtain(); // mLength is the length of object representation, excluding the type and length. // mPosition is the position of the entire value container, right before the type. // So, we add 4 bytes for the type + 4 bytes for the length written. - mValueParcel.appendFrom(mSource, mPosition, mLength + 8); + parcel.appendFrom(source, mPosition, mLength + 8); + mValueParcel = parcel; } - return mValueParcel; + return parcel; } } diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java index bf0b655fe574..1f11197afeee 100644 --- a/core/java/android/os/VintfObject.java +++ b/core/java/android/os/VintfObject.java @@ -97,8 +97,11 @@ public class VintfObject { * ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0", * "android.hardware.camera.device@3.2"]. There are no duplicates. * - * For AIDL HALs, the version is stripped away - * (e.g. "android.hardware.light"). + * For AIDL HALs, the version is a single number + * (e.g. "android.hardware.light@1"). Historically, this API strips the + * version number for AIDL HALs (e.g. "android.hardware.light"). Users + * of this API must be able to handle both for backwards compatibility. + * * @hide */ @TestApi 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/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java index ea39f6d71573..fcaeeffe8e99 100644 --- a/core/java/android/widget/QuickContactBadge.java +++ b/core/java/android/widget/QuickContactBadge.java @@ -396,8 +396,9 @@ public class QuickContactBadge extends ImageView implements OnClickListener { // Prompt user to add this person to contacts final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri); if (extras != null) { - extras.remove(EXTRA_URI_CONTENT); - intent.putExtras(extras); + Bundle bundle = new Bundle(extras); + bundle.remove(EXTRA_URI_CONTENT); + intent.putExtras(bundle); } getContext().startActivity(intent); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index c24973d4f2d4..8926e20ae5ea 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -242,7 +242,7 @@ public class HdmiCecMessageValidator { mValidationInfo.append(opcode, new ValidationInfo(validator, addrType)); } - int isValid(HdmiCecMessage message) { + int isValid(HdmiCecMessage message, boolean isMessageReceived) { int opcode = message.getOpcode(); ValidationInfo info = mValidationInfo.get(opcode); if (info == null) { @@ -256,6 +256,22 @@ public class HdmiCecMessageValidator { HdmiLogger.warning("Unexpected source: " + message); return ERROR_SOURCE; } + + if (isMessageReceived) { + // Check if the source's logical address and local device's logical + // address are the same. + for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) { + synchronized (device.mLock) { + if (message.getSource() == device.getDeviceInfo().getLogicalAddress() + && message.getSource() != Constants.ADDR_UNREGISTERED) { + HdmiLogger.warning( + "Unexpected source: message sent from device itself, " + message); + return ERROR_SOURCE; + } + } + } + } + // Check the destination field. if (message.getDestination() == Constants.ADDR_BROADCAST) { if ((info.addressType & DEST_BROADCAST) == 0) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index cca8be8a1bea..e951053b6ec7 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1122,7 +1122,7 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) { assertRunOnServiceThread(); - if (mMessageValidator.isValid(command) == HdmiCecMessageValidator.OK) { + if (mMessageValidator.isValid(command, false) == HdmiCecMessageValidator.OK) { mCecController.sendCommand(command, callback); } else { HdmiLogger.error("Invalid message type:" + command); @@ -1153,7 +1153,7 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly boolean handleCecCommand(HdmiCecMessage message) { assertRunOnServiceThread(); - int errorCode = mMessageValidator.isValid(message); + int errorCode = mMessageValidator.isValid(message, true); if (errorCode != HdmiCecMessageValidator.OK) { // We'll not response on the messages with the invalid source or destination // or with parameter length shorter than specified in the standard. @@ -3353,8 +3353,8 @@ public class HdmiControlService extends SystemService { invokeInputChangeListener(info); } - void setMhlInputChangeEnabled(boolean enabled) { - mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled)); + void setMhlInputChangeEnabled(boolean enabled) { + mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled)); synchronized (mLock) { mMhlInputChangeEnabled = enabled; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java index 3e5cbea6a2a4..c45d084ac7c9 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java @@ -125,7 +125,7 @@ public class HdmiCecLocalDeviceTest { mMessageValidator = new HdmiCecMessageValidator(mHdmiControlService) { @Override - int isValid(HdmiCecMessage message) { + int isValid(HdmiCecMessage message, boolean isMessageReceived) { return HdmiCecMessageValidator.OK; } }; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java index ae7f422817e5..ae99dab6ed4e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -629,7 +629,7 @@ public class HdmiCecMessageValidatorTest { } private IntegerSubject assertMessageValidity(String message) { - return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message))); + return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message), false)); } /** |