Merge "Delete some ide autogen files"
diff --git a/Android.bp b/Android.bp
index 0277af0..1fbdc15 100644
--- a/Android.bp
+++ b/Android.bp
@@ -118,7 +118,6 @@
":libbluetooth-binder-aidl",
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
- ":packagemanager_aidl",
":libupdate_engine_aidl",
":resourcemanager_aidl",
":storaged_aidl",
@@ -209,6 +208,7 @@
name: "framework-internal-utils",
static_libs: [
"apex_aidl_interface-java",
+ "packagemanager_aidl-java",
"framework-protos",
"updatable-driver-protos",
"ota_metadata_proto_java",
@@ -357,9 +357,6 @@
srcs: [
"core/java/android/net/annotations/PolicyDirection.java",
"core/java/com/android/internal/util/HexDump.java",
- "core/java/com/android/internal/util/IState.java",
- "core/java/com/android/internal/util/State.java",
- "core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
"telephony/java/android/telephony/Annotation.java",
@@ -408,11 +405,9 @@
srcs: [
"core/java/android/content/pm/BaseParceledListSlice.java",
"core/java/android/content/pm/ParceledListSlice.java",
- "core/java/android/os/HandlerExecutor.java",
"core/java/com/android/internal/util/AsyncChannel.java",
"core/java/com/android/internal/util/AsyncService.java",
"core/java/com/android/internal/util/Protocol.java",
- "core/java/com/android/internal/util/Preconditions.java",
"telephony/java/android/telephony/Annotation.java",
":net-utils-framework-wifi-common-srcs",
],
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 65a6547..1767678 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -77,22 +77,16 @@
// Module sources
":art.module.public.api{.public.stubs.source}",
":conscrypt.module.public.api{.public.stubs.source}",
- ":framework-connectivity-sources",
- ":framework-mediaprovider-sources",
- ":framework-permission-sources",
- ":framework-sdkextensions-sources",
- ":framework-statsd-sources",
- ":framework-tethering-srcs",
- ":framework-wifi-updatable-sources",
":i18n.module.public.api{.public.stubs.source}",
- ":ike-srcs",
- ":updatable-media-srcs",
// No longer part of the stubs, but are included in the docs.
":android-test-base-sources",
":android-test-mock-sources",
":android-test-runner-sources",
],
+ arg_files: [
+ "core/res/AndroidManifest.xml",
+ ],
libs: framework_docs_only_libs,
create_doc_stubs: true,
annotations_enabled: true,
@@ -106,6 +100,7 @@
merge_annotations_dirs: [
"metalava-manual",
],
+ write_sdk_values: true,
}
droidstubs {
@@ -114,6 +109,24 @@
args: metalava_framework_docs_args,
}
+// Defaults module for doc-stubs targets that use module source code as input.
+// This is the default/normal.
+stubs_defaults {
+ name: "framework-doc-stubs-sources-default",
+ defaults: ["framework-doc-stubs-default"],
+ srcs: [
+ ":framework-connectivity-sources",
+ ":framework-mediaprovider-sources",
+ ":framework-permission-sources",
+ ":framework-sdkextensions-sources",
+ ":framework-statsd-sources",
+ ":framework-tethering-srcs",
+ ":framework-wifi-updatable-sources",
+ ":ike-srcs",
+ ":updatable-media-srcs",
+ ],
+}
+
droidstubs {
name: "android-non-updatable-doc-stubs-system",
defaults: ["android-non-updatable-doc-stubs-defaults"],
@@ -123,26 +136,46 @@
droidstubs {
name: "framework-doc-stubs",
- defaults: ["framework-doc-stubs-default"],
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
+ defaults: ["framework-doc-stubs-sources-default"],
args: metalava_framework_docs_args,
- write_sdk_values: true,
}
droidstubs {
name: "framework-doc-system-stubs",
- defaults: ["framework-doc-stubs-default"],
- arg_files: [
- "core/res/AndroidManifest.xml",
- ],
+ defaults: ["framework-doc-stubs-sources-default"],
args: metalava_framework_docs_args +
" --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
- write_sdk_values: true,
api_levels_sdk_type: "system",
}
+// Experimental target building doc stubs with module stub source code as input.
+// This is intended to eventually replace framework-doc-stubs, once all diffs
+// have been eliminated.
+droidstubs {
+ name: "framework-doc-stubs-module-stubs",
+ defaults: ["framework-doc-stubs-default"],
+ args: metalava_framework_docs_args,
+ srcs: [
+ ":android.net.ipsec.ike{.public.stubs.source}",
+ ":framework-connectivity{.public.stubs.source}",
+ ":framework-media{.public.stubs.source}",
+ ":framework-mediaprovider{.public.stubs.source}",
+ ":framework-permission{.public.stubs.source}",
+ ":framework-sdkextensions{.public.stubs.source}",
+ ":framework-statsd{.public.stubs.source}",
+ ":framework-tethering{.public.stubs.source}",
+ ":framework-wifi{.public.stubs.source}",
+ ],
+ aidl: {
+ local_include_dirs: [
+ "apex/media/aidl/stable",
+ ],
+ include_dirs: [
+ "packages/modules/Connectivity/framework/aidl-export",
+ ],
+ },
+}
+
/////////////////////////////////////////////////////////////////////
// API docs are created from the generated stub source files
// using droiddoc
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 91e2074..c967e51 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -31,5 +31,6 @@
],
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 9117561..bec3cc9 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 0000000..93b06e8
--- /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 fbfbc7c..0acd759 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,9 +24,8 @@
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 {
+ 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,24 +50,35 @@
"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,
},
}
metalava_cmd = "$(location metalava)"
// Silence reflection warnings. See b/168689341
metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
-metalava_cmd += " --no-banner --format=v2 "
+metalava_cmd += " --quiet --no-banner --format=v2 "
genrule {
name: "current-api-xml",
@@ -118,13 +134,13 @@
":android-incompatibilities.api.public.latest",
":frameworks-base-api-current.txt",
],
- out: ["stdout.txt"],
+ out: ["updated-baseline.txt"],
tools: ["metalava"],
cmd: metalava_cmd +
"--check-compatibility:api:released $(location :android.api.public.latest) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.public.latest) " +
- "$(location :frameworks-base-api-current.txt) " +
- "> $(genDir)/stdout.txt",
+ "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
+ "$(location :frameworks-base-api-current.txt)",
}
genrule {
@@ -231,14 +247,14 @@
":frameworks-base-api-current.txt",
":frameworks-base-api-system-current.txt",
],
- out: ["stdout.txt"],
+ out: ["updated-baseline.txt"],
tools: ["metalava"],
cmd: metalava_cmd +
"--check-compatibility:api:released $(location :android.api.system.latest) " +
"--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " +
- "$(location :frameworks-base-api-system-current.txt) " +
- "> $(genDir)/stdout.txt",
+ "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
+ "$(location :frameworks-base-api-system-current.txt)",
}
genrule {
@@ -320,7 +336,7 @@
":frameworks-base-api-current.txt",
":frameworks-base-api-module-lib-current.txt",
],
- out: ["stdout.txt"],
+ out: ["updated-baseline.txt"],
tools: ["metalava"],
cmd: metalava_cmd +
"--check-compatibility:api:released $(location :android.api.module-lib.latest) " +
@@ -329,8 +345,8 @@
// MODULE_LIBS -> public.
"--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
"--baseline:compatibility:released $(location :android-incompatibilities.api.module-lib.latest) " +
- "$(location :frameworks-base-api-module-lib-current.txt) " +
- "> $(genDir)/stdout.txt",
+ "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
+ "$(location :frameworks-base-api-module-lib-current.txt)",
}
genrule {
diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py
index 4eb929e..d2e5b7d 100644
--- a/api/api_versions_trimmer_unittests.py
+++ b/api/api_versions_trimmer_unittests.py
@@ -304,4 +304,4 @@
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 0000000..9c67d7b
--- /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 0000000..26795c47
--- /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/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index c4e0e1f..8fcaeb1 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,6 +17,8 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
+#include <sys/types.h>
+
#include <string>
namespace android::idmap2::utils {
diff --git a/core/api/current.txt b/core/api/current.txt
index bc61d15..5c07254 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -954,7 +954,7 @@
field public static final int measureWithLargestChild = 16843476; // 0x10102d4
field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
field public static final int mediaRouteTypes = 16843694; // 0x10103ae
- field public static final int memtagMode = 16844313; // 0x1010619
+ field public static final int memtagMode = 16844324; // 0x1010624
field public static final int menuCategory = 16843230; // 0x10101de
field public static final int mimeGroup = 16844309; // 0x1010615
field public static final int mimeType = 16842790; // 0x1010026
@@ -978,7 +978,7 @@
field public static final int multiArch = 16843918; // 0x101048e
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
- field public static final int nativeHeapZeroInitialized = 16844314; // 0x101061a
+ field public static final int nativeHeapZeroInitialized = 16844325; // 0x1010625
field public static final int navigationBarColor = 16843858; // 0x1010452
field public static final int navigationBarDividerColor = 16844141; // 0x101056d
field public static final int navigationContentDescription = 16843969; // 0x10104c1
@@ -8657,6 +8657,15 @@
field public static final int TELEPHONY = 4194304; // 0x400000
}
+ public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method public void close();
+ method protected void finalize();
+ method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+ method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED = "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED";
+ }
+
public final class BluetoothDevice implements android.os.Parcelable {
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
@@ -8703,6 +8712,7 @@
field public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
field public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
field public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
+ field public static final String EXTRA_IS_COORDINATED_SET_MEMBER = "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER";
field public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
field public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
field public static final String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT";
@@ -29936,6 +29946,11 @@
ctor public OperationCanceledException(String);
}
+ public interface OutcomeReceiver<R, E extends java.lang.Throwable> {
+ method public default void onError(@NonNull E);
+ method public void onResult(@NonNull R);
+ }
+
public final class Parcel {
method public void appendFrom(android.os.Parcel, int, int);
method @Nullable public android.os.IBinder[] createBinderArray();
@@ -29961,6 +29976,7 @@
method public boolean hasFileDescriptors();
method public byte[] marshall();
method @NonNull public static android.os.Parcel obtain();
+ method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
method @Nullable public Object[] readArray(@Nullable ClassLoader);
method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
method public void readBinderArray(@NonNull android.os.IBinder[]);
@@ -29983,12 +29999,15 @@
method public int readInt();
method public void readIntArray(@NonNull int[]);
method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
+ method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>);
method public long readLong();
method public void readLongArray(@NonNull long[]);
method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
+ method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
+ method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
@@ -30127,7 +30146,7 @@
public interface Parcelable {
method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CONTENTS_FILE_DESCRIPTOR = 1; // 0x1
field public static final int PARCELABLE_WRITE_RETURN_VALUE = 1; // 0x1
}
@@ -41031,6 +41050,7 @@
method public String getNetworkOperator();
method public String getNetworkOperatorName();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getNetworkSlicingConfiguration(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.data.SlicingConfig,android.telephony.TelephonyManager.SlicingException>);
method public String getNetworkSpecifier();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
method @Deprecated public int getPhoneCount();
@@ -41264,6 +41284,13 @@
field public static final int ERROR_TIMEOUT = 1; // 0x1
}
+ public static class TelephonyManager.SlicingException extends java.lang.Exception {
+ ctor public TelephonyManager.SlicingException(int);
+ method public int getErrorCode();
+ field public static final int ERROR_MODEM_ERROR = 2; // 0x2
+ field public static final int ERROR_TIMEOUT = 1; // 0x1
+ }
+
public abstract static class TelephonyManager.UssdResponseCallback {
ctor public TelephonyManager.UssdResponseCallback();
method public void onReceiveUssdResponse(android.telephony.TelephonyManager, String, CharSequence);
@@ -41439,6 +41466,88 @@
method @NonNull public android.telephony.data.ApnSetting.Builder setUser(@Nullable String);
}
+ public final class NetworkSliceInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0xffffffff, to=0xfffffe) public int getMappedHplmnSliceDifferentiator();
+ method public int getMappedHplmnSliceServiceType();
+ method @IntRange(from=0xffffffff, to=0xfffffe) public int getSliceDifferentiator();
+ method public int getSliceServiceType();
+ method public int getStatus();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NetworkSliceInfo> CREATOR;
+ field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff
+ field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1
+ field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3
+ field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0
+ field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2
+ field public static final int SLICE_STATUS_ALLOWED = 2; // 0x2
+ field public static final int SLICE_STATUS_CONFIGURED = 1; // 0x1
+ field public static final int SLICE_STATUS_DEFAULT_CONFIGURED = 5; // 0x5
+ field public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN = 3; // 0x3
+ field public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA = 4; // 0x4
+ field public static final int SLICE_STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class NetworkSliceInfo.Builder {
+ ctor public NetworkSliceInfo.Builder();
+ method @NonNull public android.telephony.data.NetworkSliceInfo build();
+ method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=0xffffffff, to=0xfffffe) int);
+ method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceServiceType(int);
+ method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceDifferentiator(@IntRange(from=0xffffffff, to=0xfffffe) int);
+ method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceServiceType(int);
+ method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setStatus(int);
+ }
+
+ public final class RouteSelectionDescriptor implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getDataNetworkName();
+ method @IntRange(from=0x0, to=0xff) public int getPrecedence();
+ method public int getSessionType();
+ method @NonNull public java.util.List<android.telephony.data.NetworkSliceInfo> getSliceInfo();
+ method public int getSscMode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.RouteSelectionDescriptor> CREATOR;
+ field public static final int ROUTE_SSC_MODE_1 = 1; // 0x1
+ field public static final int ROUTE_SSC_MODE_2 = 2; // 0x2
+ field public static final int ROUTE_SSC_MODE_3 = 3; // 0x3
+ field public static final int SESSION_TYPE_IPV4 = 0; // 0x0
+ field public static final int SESSION_TYPE_IPV4V6 = 2; // 0x2
+ field public static final int SESSION_TYPE_IPV6 = 1; // 0x1
+ }
+
+ public final class SlicingConfig implements android.os.Parcelable {
+ ctor public SlicingConfig();
+ method public int describeContents();
+ method @NonNull public java.util.List<android.telephony.data.NetworkSliceInfo> getSliceInfo();
+ method @NonNull public java.util.List<android.telephony.data.UrspRule> getUrspRules();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SlicingConfig> CREATOR;
+ }
+
+ public final class TrafficDescriptor implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getDataNetworkName();
+ method @Nullable public String getOsAppId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR;
+ }
+
+ public static final class TrafficDescriptor.Builder {
+ ctor public TrafficDescriptor.Builder();
+ method @NonNull public android.telephony.data.TrafficDescriptor build();
+ method @NonNull public android.telephony.data.TrafficDescriptor.Builder setDataNetworkName(@NonNull String);
+ method @NonNull public android.telephony.data.TrafficDescriptor.Builder setOsAppId(@NonNull String);
+ }
+
+ public final class UrspRule implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0x0, to=0xff) public int getPrecedence();
+ method @NonNull public java.util.List<android.telephony.data.RouteSelectionDescriptor> getRouteSelectionDescriptor();
+ method @NonNull public java.util.List<android.telephony.data.TrafficDescriptor> getTrafficDescriptors();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.UrspRule> CREATOR;
+ }
+
}
package android.telephony.emergency {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cd41e58..e601583 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1507,8 +1507,24 @@
method public void onOobData(int, @NonNull android.bluetooth.OobData);
}
+ public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<java.lang.Integer> getAllGroupIds(@Nullable android.os.ParcelUuid);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.Map getGroupUuidMapByDevice(@Nullable android.bluetooth.BluetoothDevice);
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.UUID groupLock(int, @Nullable java.util.concurrent.Executor, @Nullable android.bluetooth.BluetoothCsipSetCoordinator.ClientLockCallback);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean groupUnlock(@NonNull java.util.UUID);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CSIS_DEVICE_AVAILABLE = "android.bluetooth.action.CSIS_DEVICE_AVAILABLE";
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE = "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE";
+ }
+
+ public static interface BluetoothCsipSetCoordinator.ClientLockCallback {
+ method public void onGroupLockSet(int, int, boolean);
+ }
+
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean fetchUuidsWithSdp(int);
method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
@@ -1560,7 +1576,10 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean startScoUsingVirtualVoiceCall();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean stopScoUsingVirtualVoiceCall();
}
public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
@@ -7241,6 +7260,7 @@
public class SystemConfigManager {
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+ method @NonNull public java.util.List<java.lang.String> getEnabledComponentOverrides(@NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public int[] getSystemPermissionUids(@NonNull String);
}
@@ -9671,6 +9691,25 @@
field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
+ public final class ModemActivityInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+ method public long getIdleTimeMillis();
+ method public static int getNumTxPowerLevels();
+ method public long getReceiveTimeMillis();
+ method public long getSleepTimeMillis();
+ method public long getTimestampMillis();
+ method public long getTransmitDurationMillisAtPowerLevel(int);
+ method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+ field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+ field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+ field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+ field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+ field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+ }
+
public final class NetworkRegistrationInfo implements android.os.Parcelable {
method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
method public int getRegistrationState();
@@ -10337,6 +10376,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
@@ -10509,6 +10549,14 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
+ method public int getErrorCode();
+ field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2
+ field public static final int ERROR_MODEM_RESPONSE_ERROR = 3; // 0x3
+ field public static final int ERROR_PHONE_NOT_AVAILABLE = 1; // 0x1
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ }
+
public final class ThermalMitigationRequest implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.telephony.DataThrottlingRequest getDataThrottlingRequest();
@@ -10772,32 +10820,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR;
}
- public final class NetworkSliceInfo implements android.os.Parcelable {
- method public int describeContents();
- method @IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator();
- method public int getMappedHplmnSliceServiceType();
- method @IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator();
- method public int getSliceServiceType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NetworkSliceInfo> CREATOR;
- field public static final int MAX_SLICE_DIFFERENTIATOR = 16777214; // 0xfffffe
- field public static final int MIN_SLICE_DIFFERENTIATOR = -1; // 0xffffffff
- field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff
- field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1
- field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3
- field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0
- field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2
- }
-
- public static final class NetworkSliceInfo.Builder {
- ctor public NetworkSliceInfo.Builder();
- method @NonNull public android.telephony.data.NetworkSliceInfo build();
- method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
- method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceServiceType(int);
- method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
- method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceServiceType(int);
- }
-
public final class NrQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
method public int describeContents();
method @NonNull public java.time.Duration getBitRateWindowDuration();
@@ -10854,15 +10876,6 @@
method @NonNull public android.telephony.data.ThrottleStatus.Builder setTransportType(int);
}
- public final class TrafficDescriptor implements android.os.Parcelable {
- ctor public TrafficDescriptor(@Nullable String, @Nullable String);
- method public int describeContents();
- method @Nullable public String getDnn();
- method @Nullable public String getOsAppId();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR;
- }
-
}
package android.telephony.euicc {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6613947..d905bbe 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1649,6 +1649,28 @@
field public static final String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
}
+ public final class ModemActivityInfo implements android.os.Parcelable {
+ ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
+ method public int describeContents();
+ method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+ method public long getIdleTimeMillis();
+ method public static int getNumTxPowerLevels();
+ method public long getReceiveTimeMillis();
+ method public long getSleepTimeMillis();
+ method public long getTimestampMillis();
+ method public long getTransmitDurationMillisAtPowerLevel(int);
+ method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+ method public boolean isEmpty();
+ method public boolean isValid();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+ field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+ field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+ field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+ field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+ field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+ }
+
public class PhoneNumberUtils {
method public static int getMinMatchForTest();
method public static void setMinMatchForTest(int);
diff --git a/core/java/Android.bp b/core/java/Android.bp
index eee696b..3b2d883 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -332,13 +332,29 @@
filegroup {
name: "framework-cellbroadcast-shared-srcs",
srcs: [
- "android/os/HandlerExecutor.java",
"android/util/LocalLog.java",
+ ],
+}
+
+java_library {
+ name: "modules-utils-statemachine",
+ srcs: [
"com/android/internal/util/IState.java",
- "com/android/internal/util/Preconditions.java",
"com/android/internal/util/State.java",
"com/android/internal/util/StateMachine.java",
],
+ libs: [
+ "framework-annotations-lib",
+ "unsupportedappusage",
+ ],
+ sdk_version: "module_current",
+ min_sdk_version: "29",
+
+ visibility: ["//visibility:public"],
+ apex_available: [
+ "//apex_available:anyapex",
+ "//apex_available:platform",
+ ],
}
filegroup {
@@ -365,10 +381,7 @@
"android/util/Rational.java",
"com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/HexDump.java",
- "com/android/internal/util/IState.java",
"com/android/internal/util/MessageUtils.java",
- "com/android/internal/util/State.java",
- "com/android/internal/util/StateMachine.java",
"com/android/internal/util/WakeupMessage.java",
],
visibility: [
diff --git a/core/java/android/annotation/SuppressLint.java b/core/java/android/annotation/SuppressLint.java
new file mode 100644
index 0000000..2d3456b
--- /dev/null
+++ b/core/java/android/annotation/SuppressLint.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+package android.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Indicates that Lint should ignore the specified warnings for the annotated element. */
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+@Retention(RetentionPolicy.CLASS)
+public @interface SuppressLint {
+ /**
+ * The set of warnings (identified by the lint issue id) that should be
+ * ignored by lint. It is not an error to specify an unrecognized name.
+ */
+ String[] value();
+}
diff --git a/core/java/android/annotation/TargetApi.java b/core/java/android/annotation/TargetApi.java
new file mode 100644
index 0000000..975318e
--- /dev/null
+++ b/core/java/android/annotation/TargetApi.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+package android.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Indicates that Lint should treat this type as targeting a given API level, no matter what the
+ project target is. */
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
+@Retention(RetentionPolicy.CLASS)
+public @interface TargetApi {
+ /**
+ * This sets the target api level for the type..
+ */
+ int value();
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 41170a4..749e8f5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6299,7 +6299,13 @@
final File cacheDir = context.getCacheDir();
if (cacheDir != null) {
// Provide a usable directory for temporary files
- System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+ String tmpdir = cacheDir.getAbsolutePath();
+ System.setProperty("java.io.tmpdir", tmpdir);
+ try {
+ android.system.Os.setenv("TMPDIR", tmpdir, true);
+ } catch (ErrnoException ex) {
+ Log.w(TAG, "Unable to initialize $TMPDIR", ex);
+ }
} else {
Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property "
+ "due to missing cache directory");
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 71b28fb..6c435b9 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -54,7 +54,7 @@
* @see android.support.v4.app.JobIntentService
*
* @deprecated IntentService is subject to all the
- * <a href="/preview/features/background.html">background execution limits</a>
+ * <a href="{@docRoot}about/versions/oreo/background.html">background execution limits</a>
* imposed with Android 8.0 (API level 26). Consider using {@link androidx.work.WorkManager}
* or {@link androidx.core.app.JobIntentService}, which uses jobs
* instead of services when running on Android 8.0 or higher.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d487025..313dd3e 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2819,6 +2819,10 @@
} else if (profile == BluetoothProfile.VOLUME_CONTROL) {
BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
return true;
+ } else if (profile == BluetoothProfile.CSIP_SET_COORDINATOR) {
+ BluetoothCsipSetCoordinator csipSetCoordinator =
+ new BluetoothCsipSetCoordinator(context, listener);
+ return true;
} else {
return false;
}
@@ -2908,6 +2912,11 @@
BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
vcs.close();
break;
+ case BluetoothProfile.CSIP_SET_COORDINATOR:
+ BluetoothCsipSetCoordinator csipSetCoordinator =
+ (BluetoothCsipSetCoordinator) proxy;
+ csipSetCoordinator.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
new file mode 100644
index 0000000..cb542e5
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.
+ */
+
+package android.bluetooth;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.CloseGuard;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides the public APIs to control the Bluetooth CSIP set coordinator.
+ *
+ * <p>BluetoothCsipSetCoordinator is a proxy object for controlling the Bluetooth VC
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothCsipSetCoordinator proxy object.
+ *
+ */
+public final class BluetoothCsipSetCoordinator implements BluetoothProfile, AutoCloseable {
+ private static final String TAG = "BluetoothCsipSetCoordinator";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+
+ private CloseGuard mCloseGuard;
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public interface ClientLockCallback {
+ /**
+ * @hide
+ */
+ @SystemApi void onGroupLockSet(int groupId, int opStatus, boolean isLocked);
+ }
+
+ private static class BluetoothCsipSetCoordinatorLockCallbackDelegate
+ extends IBluetoothCsipSetCoordinatorLockCallback.Stub {
+ private final ClientLockCallback mCallback;
+ private final Executor mExecutor;
+
+ BluetoothCsipSetCoordinatorLockCallbackDelegate(
+ Executor executor, ClientLockCallback callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onGroupLockSet(int groupId, int opStatus, boolean isLocked) {
+ mExecutor.execute(() -> mCallback.onGroupLockSet(groupId, opStatus, isLocked));
+ }
+ };
+
+ /**
+ * Intent used to broadcast the change in connection state of the CSIS
+ * Client.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED";
+
+ /**
+ * Intent used to expose broadcast receiving device.
+ *
+ * <p>This intent will have 2 extras:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Broadcast receiver device. </li>
+ * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li>
+ * <li> {@link #EXTRA_CSIS_GROUP_SIZE} - Group size. </li>
+ * <li> {@link #EXTRA_CSIS_GROUP_TYPE_UUID} - Group type UUID. </li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CSIS_DEVICE_AVAILABLE =
+ "android.bluetooth.action.CSIS_DEVICE_AVAILABLE";
+
+ /**
+ * Used as an extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
+ * Contains the group id.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CSIS_GROUP_ID = "android.bluetooth.extra.CSIS_GROUP_ID";
+
+ /**
+ * Group size as int extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CSIS_GROUP_SIZE = "android.bluetooth.extra.CSIS_GROUP_SIZE";
+
+ /**
+ * Group type uuid extra field in {@link #ACTION_CSIS_DEVICE_AVAILABLE} intent.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CSIS_GROUP_TYPE_UUID =
+ "android.bluetooth.extra.CSIS_GROUP_TYPE_UUID";
+
+ /**
+ * Intent used to broadcast information about identified set member
+ * ready to connect.
+ *
+ * <p>This intent will have one extra:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+ * be null if no device is active. </li>
+ * <li> {@link #EXTRA_CSIS_GROUP_ID} - Group identifier. </li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE =
+ "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE";
+
+ /**
+ * This represents an invalid group ID.
+ *
+ * @hide
+ */
+ public static final int GROUP_ID_INVALID = IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVALID;
+
+ /**
+ * Indicating that group was locked with success.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_SUCCESS = 0;
+
+ /**
+ * Indicating that group locked failed due to invalid group ID.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_INVALID_GROUP = 1;
+
+ /**
+ * Indicating that group locked failed due to empty group.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_GROUP_EMPTY = 2;
+
+ /**
+ * Indicating that group locked failed due to group members being disconnected.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_GROUP_NOT_CONNECTED = 3;
+
+ /**
+ * Indicating that group locked failed due to group member being already locked.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_LOCKED_BY_OTHER = 4;
+
+ /**
+ * Indicating that group locked failed due to other reason.
+ *
+ * @hide
+ */
+ public static final int GROUP_LOCK_FAILED_OTHER_REASON = 5;
+
+ /**
+ * Indicating that group member in locked state was lost.
+ *
+ * @hide
+ */
+ public static final int LOCKED_GROUP_MEMBER_LOST = 6;
+
+ private BluetoothAdapter mAdapter;
+ private final BluetoothProfileConnector<IBluetoothCsipSetCoordinator> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.CSIP_SET_COORDINATOR, TAG,
+ IBluetoothCsipSetCoordinator.class.getName()) {
+ @Override
+ public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) {
+ return IBluetoothCsipSetCoordinator.Stub.asInterface(
+ Binder.allowBlocking(service));
+ }
+ };
+
+ /**
+ * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local
+ * Bluetooth CSIS service.
+ */
+ /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener) {
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mProfileConnector.connect(context, listener);
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("close");
+ }
+
+ /**
+ * @hide
+ */
+ protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+
+ /**
+ * @hide
+ */
+ public void close() {
+ mProfileConnector.disconnect();
+ }
+
+ private IBluetoothCsipSetCoordinator getService() {
+ return mProfileConnector.getService();
+ }
+
+ /**
+ * Lock the set.
+ * @param groupId group ID to lock,
+ * @param executor callback executor,
+ * @param cb callback to report lock and unlock events - stays valid until the app unlocks
+ * using the returned lock identifier or the lock timeouts on the remote side,
+ * as per CSIS specification,
+ * @return unique lock identifier used for unlocking or null if lock has failed.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public
+ @Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor,
+ @Nullable ClientLockCallback cb) {
+ if (VDBG) {
+ log("groupLockSet()");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ IBluetoothCsipSetCoordinatorLockCallback delegate = null;
+ if ((executor != null) && (cb != null)) {
+ delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
+ }
+ return service.groupLock(groupId, delegate).getUuid();
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return null;
+ }
+ }
+
+ /**
+ * Unlock the set.
+ * @param lockUuid unique lock identifier
+ * @return true if unlocked, false on error
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean groupUnlock(@NonNull UUID lockUuid) {
+ if (VDBG) {
+ log("groupLockSet()");
+ }
+ if (lockUuid == null) {
+ return false;
+ }
+
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ service.groupUnlock(new ParcelUuid(lockUuid));
+ return true;
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+
+ /**
+ * Get device's groups.
+ * @param device the active device
+ * @return Map of groups ids and related UUIDs
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) {
+ if (VDBG) {
+ log("getGroupUuidMapByDevice()");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ return service.getGroupUuidMapByDevice(device);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return new HashMap<>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new HashMap<>();
+ }
+ }
+
+ /**
+ * Get group id for the given UUID
+ * @param uuid
+ * @return list of group IDs
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) {
+ if (VDBG) {
+ log("getAllGroupIds()");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ return service.getAllGroupIds(uuid);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return new ArrayList<Integer>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<Integer>();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ if (VDBG) {
+ log("getConnectedDevices()");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return service.getConnectedDevices();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public
+ @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+ @NonNull int[] states) {
+ if (VDBG) {
+ log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return service.getDevicesMatchingConnectionStates(states);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public
+ @BluetoothProfile.BtProfileState int getConnectionState(
+ @Nullable BluetoothDevice device) {
+ if (VDBG) {
+ log("getState(" + device + ")");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.getConnectionState(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * Set connection policy of the profile
+ *
+ * <p> The device should already be paired.
+ * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+ * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setConnectionPolicy(
+ @Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) {
+ if (DBG) {
+ log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ return false;
+ }
+ return service.setConnectionPolicy(device, connectionPolicy);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+
+ /**
+ * Get the connection policy of the profile.
+ *
+ * <p> The connection policy can be any of:
+ * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+ * {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Bluetooth device
+ * @return connection policy of the device
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ if (VDBG) {
+ log("getConnectionPolicy(" + device + ")");
+ }
+ final IBluetoothCsipSetCoordinator service = getService();
+ try {
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ return service.getConnectionPolicy(device);
+ }
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+ }
+
+ private boolean isEnabled() {
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
+ }
+
+ private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 38fb90d..1054b4c 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -106,7 +106,7 @@
* <p>Sent when a remote device is found during discovery.
* <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
* #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or
- * {@link #EXTRA_RSSI} if they are available.
+ * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} and
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive.
*/
@@ -257,6 +257,15 @@
public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
/**
+ * Used as an bool extra field in {@link #ACTION_FOUND} intents.
+ * It contains the information if device is discovered as member of a coordinated set or not.
+ * Pairing with device that belongs to a set would trigger pairing with the rest of set members.
+ * See Bluetooth CSIP specification for more details.
+ */
+ public static final String EXTRA_IS_COORDINATED_SET_MEMBER =
+ "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER";
+
+ /**
* Used as a Parcelable {@link BluetoothClass} extra field in {@link
* #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents.
*/
@@ -1326,7 +1335,7 @@
* @throws IllegalArgumentException if an invalid transport was specified
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean createBond(int transport) {
return createBondInternal(transport, null, null);
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 632572d..18aa23a 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -1018,8 +1018,8 @@
* - binder is dead or Bluetooth is disabled or other error
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- @UnsupportedAppUsage
public boolean startScoUsingVirtualVoiceCall() {
if (DBG) log("startScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
@@ -1048,8 +1048,8 @@
* - binder is dead or Bluetooth is disabled or other error
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- @UnsupportedAppUsage
public boolean stopScoUsingVirtualVoiceCall() {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
@@ -1227,7 +1227,8 @@
* @return true if in-band ringing is enabled, false if in-band ringing is disabled
* @hide
*/
- @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean isInbandRingingEnabled() {
if (DBG) {
log("isInbandRingingEnabled()");
diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS
index 2239100..fbee577 100644
--- a/core/java/android/bluetooth/OWNERS
+++ b/core/java/android/bluetooth/OWNERS
@@ -1,5 +1,6 @@
# Bug component: 27441
-zachoverflow@google.com
-siyuanh@google.com
rahulsabnis@google.com
+sattiraju@google.com
+siyuanh@google.com
+zachoverflow@google.com
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
index 0ba92cc..504a7bd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.content.IntentFilter;
import android.os.Parcel;
-import android.os.Parcelable;
import android.util.Pair;
import com.android.internal.util.DataClass;
@@ -168,19 +167,6 @@
+ '}';
}
- public static final Parcelable.Creator<ParsedIntentInfo> CREATOR =
- new Parcelable.Creator<ParsedIntentInfo>() {
- @Override
- public ParsedIntentInfo createFromParcel(Parcel source) {
- return new ParsedIntentInfo(source);
- }
-
- @Override
- public ParsedIntentInfo[] newArray(int size) {
- return new ParsedIntentInfo[size];
- }
- };
-
public boolean isHasDefault() {
return hasDefault;
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 0a76a9c..9e78a6b 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1440,14 +1440,14 @@
* Assuming that the bottom edge of the device faces the
* user and that the screen is face-up, tilting the top edge
* of the device toward the ground creates a positive pitch
- * angle. The range of values is -π to π.</li>
+ * angle. The range of values is -π/2 to π/2.</li>
* <li>values[2]: <i>Roll</i>, angle of rotation about the y axis. This
* value represents the angle between a plane perpendicular
* to the device's screen and a plane perpendicular to the
* ground. Assuming that the bottom edge of the device faces
* the user and that the screen is face-up, tilting the left
* edge of the device toward the ground creates a positive
- * roll angle. The range of values is -π/2 to π/2.</li>
+ * roll angle. The range of values is -π to π.</li>
* </ul>
* <p>
* Applying these three rotations in the azimuth, pitch, roll order
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 68917a8..08f75df 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -47,7 +47,6 @@
import android.util.BackupUtils;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.net.module.util.NetworkIdentityUtils;
@@ -151,24 +150,6 @@
}
}
- private static boolean sForceAllNetworkTypes = false;
-
- /**
- * Results in matching against all mobile network types.
- *
- * <p>See {@link #matchesMobile} and {@link matchesMobileWildcard}.
- */
- @VisibleForTesting
- public static void forceAllNetworkTypes() {
- sForceAllNetworkTypes = true;
- }
-
- /** Resets the affect of {@link #forceAllNetworkTypes}. */
- @VisibleForTesting
- public static void resetForceAllNetworkTypes() {
- sForceAllNetworkTypes = false;
- }
-
/**
* Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
* the given IMSI.
@@ -611,7 +592,7 @@
// Only metered mobile network would be matched regardless of metered filter.
// This is used to exclude non-metered APNs, e.g. IMS. See ag/908650.
// TODO: Respect metered filter and remove mMetered condition.
- return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
+ return (ident.mType == TYPE_MOBILE && ident.mMetered)
&& !ArrayUtils.isEmpty(mMatchSubscriberIds)
&& ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
&& matchesCollapsedRatType(ident);
@@ -726,7 +707,7 @@
if (ident.mType == TYPE_WIMAX) {
return true;
} else {
- return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
+ return (ident.mType == TYPE_MOBILE && ident.mMetered)
&& matchesCollapsedRatType(ident);
}
}
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index 5a25cfc..ae8d010 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -23,6 +23,9 @@
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 @@
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 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 @@
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 @@
} 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 6da02f5..7ce8d72 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -260,6 +260,9 @@
/**
* 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 @@
/**
* 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;
@@ -428,7 +431,7 @@
*
* @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));
}
@@ -1045,7 +1048,7 @@
*/
char getChar(String key, char defaultValue) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
@@ -1448,7 +1451,7 @@
@Nullable
short[] getShortArray(@Nullable String key) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return null;
}
@@ -1471,7 +1474,7 @@
@Nullable
char[] getCharArray(@Nullable String key) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return null;
}
@@ -1540,7 +1543,7 @@
@Nullable
float[] getFloatArray(@Nullable String key) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return null;
}
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index 4d160da..d83d94a 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -40,4 +40,9 @@
* @see SystemConfigManager#getSystemPermissionUids
*/
int[] getSystemPermissionUids(String permissionName);
+
+ /**
+ * @see SystemConfigManager#getEnabledComponentOverrides
+ */
+ List<String> getEnabledComponentOverrides(String packageName);
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index a870c04..c575c80e 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -69,3 +69,6 @@
# 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/OutcomeReceiver.java b/core/java/android/os/OutcomeReceiver.java
new file mode 100644
index 0000000..01b2764
--- /dev/null
+++ b/core/java/android/os/OutcomeReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback interface intended for use when an asynchronous operation may result in a failure.
+ *
+ * This interface may be used in cases where an asynchronous API may complete either with a value
+ * or with a {@link Throwable} that indicates an error.
+ * @param <R> The type of the result that's being sent.
+ * @param <E> The type of the {@link Throwable} that contains more information about the error.
+ */
+public interface OutcomeReceiver<R, E extends Throwable> {
+ /**
+ * Called when the asynchronous operation succeeds and delivers a result value.
+ * @param result The value delivered by the asynchronous operation.
+ */
+ void onResult(@NonNull R result);
+
+ /**
+ * Called when the asynchronous operation fails. The mode of failure is indicated by the
+ * {@link Throwable} passed as an argument to this method.
+ * @param error A subclass of {@link Throwable} with more details about the error that occurred.
+ */
+ default void onError(@NonNull E error) {}
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 00db972..358aa29 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;
@@ -261,6 +263,10 @@
private static final int VAL_SIZE = 26;
private static final int VAL_SIZEF = 27;
private static final int VAL_DOUBLEARRAY = 28;
+ private static final int VAL_CHAR = 29;
+ private static final int VAL_SHORTARRAY = 30;
+ private static final int VAL_CHARARRAY = 31;
+ private static final int VAL_FLOATARRAY = 32;
// The initial int32 in a Binder call's reply Parcel header:
// Keep these in sync with libbinder's binder/Status.h.
@@ -282,6 +288,8 @@
@CriticalNative
private static native void nativeMarkSensitive(long nativePtr);
+ @FastNative
+ private static native void nativeMarkForBinder(long nativePtr, IBinder binder);
@CriticalNative
private static native int nativeDataSize(long nativePtr);
@CriticalNative
@@ -444,6 +452,21 @@
}
/**
+ * Retrieve a new Parcel object from the pool for use with a specific binder.
+ *
+ * Associate this parcel with a binder object. This marks the parcel as being prepared for a
+ * transaction on this specific binder object. Based on this, the format of the wire binder
+ * protocol may change. For future compatibility, it is recommended to use this for all
+ * Parcels.
+ */
+ @NonNull
+ public static Parcel obtain(@NonNull IBinder binder) {
+ Parcel parcel = Parcel.obtain();
+ parcel.markForBinder(binder);
+ return parcel;
+ }
+
+ /**
* Put a Parcel object back into the pool. You must not touch
* the object after this call.
*/
@@ -498,6 +521,16 @@
/**
* Parcel data should be zero'd before realloc'd or deleted.
+ *
+ * Note: currently this feature requires multiple things to work in concert:
+ * - markSensitive must be called on every relative Parcel
+ * - FLAG_CLEAR_BUF must be passed into the kernel
+ * This requires having code which does the right thing in every method and in every backend
+ * of AIDL. Rather than exposing this API, it should be replaced with a single API on
+ * IBinder objects which can be called once, and the information should be fed into the
+ * Parcel using markForBinder APIs. In terms of code size and number of API calls, this is
+ * much more extensible.
+ *
* @hide
*/
public final void markSensitive() {
@@ -505,9 +538,16 @@
}
/**
+ * @hide
+ */
+ private void markForBinder(@NonNull IBinder binder) {
+ nativeMarkForBinder(mNativePtr, binder);
+ }
+
+ /**
* Returns the total amount of data contained in the parcel.
*/
- public final int dataSize() {
+ public int dataSize() {
return nativeDataSize(mNativePtr);
}
@@ -1281,6 +1321,46 @@
}
}
+ /** @hide */
+ public void writeShortArray(@Nullable short[] val) {
+ if (val != null) {
+ int n = val.length;
+ writeInt(n);
+ for (int i = 0; i < n; i++) {
+ writeInt(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /** @hide */
+ @Nullable
+ public short[] createShortArray() {
+ int n = readInt();
+ if (n >= 0 && n <= (dataAvail() >> 2)) {
+ short[] val = new short[n];
+ for (int i = 0; i < n; i++) {
+ val[i] = (short) readInt();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public void readShortArray(@NonNull short[] val) {
+ int n = readInt();
+ if (n == val.length) {
+ for (int i = 0; i < n; i++) {
+ val[i] = (short) readInt();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
public final void writeCharArray(@Nullable char[] val) {
if (val != null) {
int N = val.length;
@@ -1946,6 +2026,14 @@
return VAL_SIZE;
} else if (v instanceof double[]) {
return VAL_DOUBLEARRAY;
+ } else if (v instanceof Character) {
+ return VAL_CHAR;
+ } else if (v instanceof short[]) {
+ return VAL_SHORTARRAY;
+ } else if (v instanceof char[]) {
+ return VAL_CHARARRAY;
+ } else if (v instanceof float[]) {
+ return VAL_FLOATARRAY;
} else {
Class<?> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
@@ -2048,6 +2136,18 @@
case VAL_DOUBLEARRAY:
writeDoubleArray((double[]) v);
break;
+ case VAL_CHAR:
+ writeInt((Character) v);
+ break;
+ case VAL_SHORTARRAY:
+ writeShortArray((short[]) v);
+ break;
+ case VAL_CHARARRAY:
+ writeCharArray((char[]) v);
+ break;
+ case VAL_FLOATARRAY:
+ writeFloatArray((float[]) v);
+ break;
case VAL_OBJECTARRAY:
writeArray((Object[]) v);
break;
@@ -2728,7 +2828,20 @@
*/
public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
int N = readInt();
- readListInternal(outVal, N, loader);
+ readListInternal(outVal, N, loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readList(List, ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item. If the item to be deserialized is not an instance
+ * of that class or any of its children class
+ * a {@link BadParcelableException} will be thrown.
+ */
+ public <T> void readList(@NonNull List<? super T> outVal,
+ @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ int n = readInt();
+ readListInternal(outVal, n, loader, clazz);
}
/**
@@ -2927,7 +3040,7 @@
return null;
}
ArrayList l = new ArrayList(N);
- readListInternal(l, N, loader);
+ readListInternal(l, N, loader, /* clazz */ null);
return l;
}
@@ -3327,20 +3440,29 @@
*/
@Nullable
public final Object readValue(@Nullable ClassLoader loader) {
+ return readValue(loader, /* clazz */ null);
+ }
+
+
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @Nullable
+ private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
int type = readInt();
- final Object object;
+ final T object;
if (isLengthPrefixed(type)) {
int length = readInt();
int start = dataPosition();
- object = readValue(type, loader);
+ object = readValue(type, loader, clazz);
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.");
}
} else {
- object = readValue(type, loader);
+ object = readValue(type, loader, clazz);
}
return object;
}
@@ -3377,7 +3499,7 @@
setDataPosition(MathUtils.addOrThrow(dataPosition(), length));
return new LazyValue(this, start, length, type, loader);
} else {
- return readValue(type, loader);
+ return readValue(type, loader, /* clazz */ null);
}
}
@@ -3386,12 +3508,19 @@
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;
@@ -3400,46 +3529,41 @@
@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) {
- int restore = mSource.dataPosition();
- try {
- mSource.setDataPosition(mPosition);
- out.writeInt(mSource.readInt()); // Type
- out.writeInt(mSource.readInt()); // Length
- out.appendFrom(mSource, mSource.dataPosition(), mLength);
- } finally {
- mSource.setDataPosition(restore);
- }
+ 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 + "}";
}
@@ -3458,155 +3582,224 @@
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;
}
}
/**
* Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
* type first.
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
*/
+ @SuppressWarnings("unchecked")
@Nullable
- private Object readValue(int type, @Nullable ClassLoader loader) {
+ private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ final Object object;
switch (type) {
- case VAL_NULL:
- return null;
+ case VAL_NULL:
+ object = null;
+ break;
- case VAL_STRING:
- return readString();
+ case VAL_STRING:
+ object = readString();
+ break;
- case VAL_INTEGER:
- return readInt();
+ case VAL_INTEGER:
+ object = readInt();
+ break;
- case VAL_MAP:
- return readHashMap(loader);
+ case VAL_MAP:
+ object = readHashMap(loader);
+ break;
- case VAL_PARCELABLE:
- return readParcelable(loader);
+ case VAL_PARCELABLE:
+ object = readParcelableInternal(loader, clazz);
+ break;
- case VAL_SHORT:
- return (short) readInt();
+ case VAL_SHORT:
+ object = (short) readInt();
+ break;
- case VAL_LONG:
- return readLong();
+ case VAL_LONG:
+ object = readLong();
+ break;
- case VAL_FLOAT:
- return readFloat();
+ case VAL_FLOAT:
+ object = readFloat();
+ break;
- case VAL_DOUBLE:
- return readDouble();
+ case VAL_DOUBLE:
+ object = readDouble();
+ break;
- case VAL_BOOLEAN:
- return readInt() == 1;
+ case VAL_BOOLEAN:
+ object = readInt() == 1;
+ break;
- case VAL_CHARSEQUENCE:
- return readCharSequence();
+ case VAL_CHARSEQUENCE:
+ object = readCharSequence();
+ break;
- case VAL_LIST:
- return readArrayList(loader);
+ case VAL_LIST:
+ object = readArrayList(loader);
+ break;
- case VAL_BOOLEANARRAY:
- return createBooleanArray();
+ case VAL_BOOLEANARRAY:
+ object = createBooleanArray();
+ break;
- case VAL_BYTEARRAY:
- return createByteArray();
+ case VAL_BYTEARRAY:
+ object = createByteArray();
+ break;
- case VAL_STRINGARRAY:
- return readStringArray();
+ case VAL_STRINGARRAY:
+ object = readStringArray();
+ break;
- case VAL_CHARSEQUENCEARRAY:
- return readCharSequenceArray();
+ case VAL_CHARSEQUENCEARRAY:
+ object = readCharSequenceArray();
+ break;
- case VAL_IBINDER:
- return readStrongBinder();
+ case VAL_IBINDER:
+ object = readStrongBinder();
+ break;
- case VAL_OBJECTARRAY:
- return readArray(loader);
+ case VAL_OBJECTARRAY:
+ object = readArray(loader);
+ break;
- case VAL_INTARRAY:
- return createIntArray();
+ case VAL_INTARRAY:
+ object = createIntArray();
+ break;
- case VAL_LONGARRAY:
- return createLongArray();
+ case VAL_LONGARRAY:
+ object = createLongArray();
+ break;
- case VAL_BYTE:
- return readByte();
+ case VAL_BYTE:
+ object = readByte();
+ break;
- case VAL_SERIALIZABLE:
- return readSerializable(loader);
+ case VAL_SERIALIZABLE:
+ object = readSerializable(loader);
+ break;
- case VAL_PARCELABLEARRAY:
- return readParcelableArray(loader);
+ case VAL_PARCELABLEARRAY:
+ object = readParcelableArray(loader);
+ break;
- case VAL_SPARSEARRAY:
- return readSparseArray(loader);
+ case VAL_SPARSEARRAY:
+ object = readSparseArray(loader);
+ break;
- case VAL_SPARSEBOOLEANARRAY:
- return readSparseBooleanArray();
+ case VAL_SPARSEBOOLEANARRAY:
+ object = readSparseBooleanArray();
+ break;
- case VAL_BUNDLE:
- return readBundle(loader); // loading will be deferred
+ case VAL_BUNDLE:
+ object = readBundle(loader); // loading will be deferred
+ break;
- case VAL_PERSISTABLEBUNDLE:
- return readPersistableBundle(loader);
+ case VAL_PERSISTABLEBUNDLE:
+ object = readPersistableBundle(loader);
+ break;
- case VAL_SIZE:
- return readSize();
+ case VAL_SIZE:
+ object = readSize();
+ break;
- case VAL_SIZEF:
- return readSizeF();
+ case VAL_SIZEF:
+ object = readSizeF();
+ break;
- case VAL_DOUBLEARRAY:
- return createDoubleArray();
+ case VAL_DOUBLEARRAY:
+ object = createDoubleArray();
+ break;
- default:
- int off = dataPosition() - 4;
- throw new RuntimeException(
- "Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off);
+ case VAL_CHAR:
+ object = (char) readInt();
+ break;
+
+ case VAL_SHORTARRAY:
+ object = createShortArray();
+ break;
+
+ case VAL_CHARARRAY:
+ object = createCharArray();
+ break;
+
+ case VAL_FLOATARRAY:
+ object = createFloatArray();
+ break;
+
+ default:
+ int off = dataPosition() - 4;
+ throw new RuntimeException(
+ "Parcel " + this + ": Unmarshalling unknown type code " + type
+ + " at offset " + off);
}
+ if (clazz != null && !clazz.isInstance(object)) {
+ throw new BadParcelableException("Unparcelled object " + object
+ + " is not an instance of required class " + clazz.getName()
+ + " provided in the parameter");
+ }
+ return (T) object;
}
private boolean isLengthPrefixed(int type) {
+ // In general, we want custom types and containers of custom types to be length-prefixed,
+ // this allows clients (eg. Bundle) to skip their content during deserialization. The
+ // exception to this is Bundle, since Bundle is already length-prefixed and already copies
+ // the correspondent section of the parcel internally.
switch (type) {
+ case VAL_MAP:
case VAL_PARCELABLE:
- case VAL_PARCELABLEARRAY:
case VAL_LIST:
case VAL_SPARSEARRAY:
- case VAL_BUNDLE:
+ case VAL_PARCELABLEARRAY:
+ case VAL_OBJECTARRAY:
case VAL_SERIALIZABLE:
return true;
default:
@@ -3625,17 +3818,42 @@
* @throws BadParcelableException Throws BadParcelableException if there
* was an error trying to instantiate the Parcelable.
*/
- @SuppressWarnings("unchecked")
@Nullable
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
- Parcelable.Creator<?> creator = readParcelableCreator(loader);
+ return readParcelableInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readParcelable(ClassLoader)} but accepts {@code clazz} parameter as the type
+ * required for each item. If the item to be deserialized is not an instance of that class or
+ * any of its children classes a {@link BadParcelableException} will be thrown.
+ */
+ @Nullable
+ public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
+ @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readParcelableInternal(loader, clazz);
+ }
+
+ /**
+ *
+ * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ if (clazz != null && !Parcelable.class.isAssignableFrom(clazz)) {
+ throw new BadParcelableException("About to unparcel a parcelable object "
+ + " but class required " + clazz.getName() + " is not Parcelable");
+ }
+ Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
if (creator == null) {
return null;
}
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
- Parcelable.ClassLoaderCreator<?> classLoaderCreator =
- (Parcelable.ClassLoaderCreator<?>) creator;
- return (T) classLoaderCreator.createFromParcel(this, loader);
+ Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+ (Parcelable.ClassLoaderCreator<?>) creator;
+ return (T) classLoaderCreator.createFromParcel(this, loader);
}
return (T) creator.createFromParcel(this);
}
@@ -3669,6 +3887,28 @@
*/
@Nullable
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
+ return readParcelableCreatorInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readParcelableCreator(ClassLoader)} but accepts {@code clazz} parameter
+ * as the required type. If the item to be deserialized is not an instance of that class
+ * or any of its children classes a {@link BadParcelableException} will be thrown.
+ */
+ @Nullable
+ public <T> Parcelable.Creator<T> readParcelableCreator(
+ @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readParcelableCreatorInternal(loader, clazz);
+ }
+
+ /**
+ * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private <T> Parcelable.Creator<T> readParcelableCreatorInternal(
+ @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
String name = readString();
if (name == null) {
return null;
@@ -3684,7 +3924,15 @@
creator = map.get(name);
}
if (creator != null) {
- return creator;
+ if (clazz != null) {
+ Class<?> parcelableClass = creator.getClass().getEnclosingClass();
+ if (!clazz.isAssignableFrom(parcelableClass)) {
+ throw new BadParcelableException("Parcelable creator " + name + " is not "
+ + "a subclass of required class " + clazz.getName()
+ + " provided in the parameter");
+ }
+ }
+ return (Parcelable.Creator<T>) creator;
}
try {
@@ -3700,6 +3948,14 @@
throw new BadParcelableException("Parcelable protocol requires subclassing "
+ "from Parcelable on class " + name);
}
+ if (clazz != null) {
+ if (!clazz.isAssignableFrom(parcelableClass)) {
+ throw new BadParcelableException("Parcelable creator " + name + " is not "
+ + "a subclass of required class " + clazz.getName()
+ + " provided in the parameter");
+ }
+ }
+
Field f = parcelableClass.getField("CREATOR");
if ((f.getModifiers() & Modifier.STATIC) == 0) {
throw new BadParcelableException("Parcelable protocol requires "
@@ -3737,7 +3993,7 @@
map.put(name, creator);
}
- return creator;
+ return (Parcelable.Creator<T>) creator;
}
/**
@@ -3983,13 +4239,21 @@
return result;
}
- private void readListInternal(@NonNull List outVal, int N,
+ private void readListInternal(@NonNull List outVal, int n,
@Nullable ClassLoader loader) {
- while (N > 0) {
- Object value = readValue(loader);
+ readListInternal(outVal, n, loader, null);
+ }
+
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ private <T> void readListInternal(@NonNull List<? super T> outVal, int n,
+ @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ while (n > 0) {
+ T value = readValue(loader, clazz);
//Log.d(TAG, "Unmarshalling value=" + value);
outVal.add(value);
- N--;
+ n--;
}
}
@@ -4068,6 +4332,10 @@
case VAL_SIZE: return "VAL_SIZE";
case VAL_SIZEF: return "VAL_SIZEF";
case VAL_DOUBLEARRAY: return "VAL_DOUBLEARRAY";
+ case VAL_CHAR: return "VAL_CHAR";
+ case VAL_SHORTARRAY: return "VAL_SHORTARRAY";
+ case VAL_CHARARRAY: return "VAL_CHARARRAY";
+ case VAL_FLOATARRAY: return "VAL_FLOATARRAY";
case VAL_OBJECTARRAY: return "VAL_OBJECTARRAY";
case VAL_SERIALIZABLE: return "VAL_SERIALIZABLE";
default: return "UNKNOWN(" + type + ")";
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index a537c98..a396211 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.NonNull;
import android.annotation.IntDef;
import android.annotation.SystemApi;
@@ -202,7 +203,7 @@
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
- public void writeToParcel(Parcel dest, @WriteFlags int flags);
+ public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags);
/**
* Interface that must be implemented and provided as a public CREATOR
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 06a2c87..4e8418b 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -29,7 +29,14 @@
import java.util.Map;
-/** @hide */
+/**
+ * Manage binder services as registered with the binder context manager. These services must be
+ * declared statically on an Android device (SELinux access_vector service_manager, w/ service
+ * names in service_contexts files), and they do not follow the activity lifecycle. When
+ * building applications, android.app.Service should be preferred.
+ *
+ * @hide
+ **/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class ServiceManager {
private static final String TAG = "ServiceManager";
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 755c35f..3739040 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -98,6 +98,10 @@
return mServiceManager.updatableViaApex(name);
}
+ public ConnectionInfo getConnectionInfo(String name) throws RemoteException {
+ return mServiceManager.getConnectionInfo(name);
+ }
+
public void registerClientCallback(String name, IBinder service, IClientCallback cb)
throws RemoteException {
throw new RemoteException();
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 9bfa8ad..a6316df 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -17,6 +17,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -129,4 +130,21 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Get enabled component for a specific package
+ *
+ * @param packageName The target package.
+ * @return The enabled component
+ * {@hide}
+ */
+ @SystemApi
+ @NonNull
+ public List<String> getEnabledComponentOverrides(@NonNull String packageName) {
+ try {
+ return mInterface.getEnabledComponentOverrides(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index bf0b655..1f11197 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -97,8 +97,11 @@
* ["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/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index 121fd33..fc17002 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -109,7 +109,7 @@
CellSignalStrength.getNumSignalStrengthLevels()));
mTxTimeMs = Arrays.copyOfRange(
txTimeMs, 0,
- Math.min(txTimeMs.length, ModemActivityInfo.TX_POWER_LEVELS));
+ Math.min(txTimeMs.length, ModemActivityInfo.getNumTxPowerLevels()));
mMonitoredRailChargeConsumedMaMs = monitoredRailChargeConsumedMaMs;
}
diff --git a/core/java/android/permission/OWNERS b/core/java/android/permission/OWNERS
index 19a3a8b..b5466b6 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/text/Layout.java b/core/java/android/text/Layout.java
index f0f0867..505f400 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -2351,7 +2351,10 @@
final int ellipsisStringLen = ellipsisString.length();
// Use the ellipsis string only if there are that at least as many characters to replace.
final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen;
- for (int i = 0; i < ellipsisCount; i++) {
+ final int min = Math.max(0, start - ellipsisStart - lineStart);
+ final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart);
+
+ for (int i = min; i < max; i++) {
final char c;
if (useEllipsisString && i < ellipsisStringLen) {
c = ellipsisString.charAt(i);
@@ -2360,9 +2363,7 @@
}
final int a = i + ellipsisStart + lineStart;
- if (start <= a && a < end) {
- dest[destoff + a - start] = c;
- }
+ dest[destoff + a - start] = c;
}
}
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4cf0a36..418d92c 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -645,7 +645,7 @@
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/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 844b156..c7609a6 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -310,7 +310,7 @@
String extension = null;
// If we couldn't do anything with the hint, move toward the content disposition
- if (filename == null && contentDisposition != null) {
+ if (contentDisposition != null) {
filename = parseContentDisposition(contentDisposition);
if (filename != null) {
int index = filename.lastIndexOf('/') + 1;
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index ea39f6d..fcaeeff 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -396,8 +396,9 @@
// 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/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index dd2940f..e8db609 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -66,7 +66,6 @@
import android.telephony.CellSignalStrength;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
-import android.telephony.ModemActivityInfo.TransmitPower;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -7205,7 +7204,7 @@
public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
if (mModemControllerActivity == null) {
mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
- ModemActivityInfo.TX_POWER_LEVELS);
+ ModemActivityInfo.getNumTxPowerLevels());
}
return mModemControllerActivity;
}
@@ -8687,7 +8686,7 @@
if (in.readInt() != 0) {
mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
- ModemActivityInfo.TX_POWER_LEVELS, in);
+ ModemActivityInfo.getNumTxPowerLevels(), in);
} else {
mModemControllerActivity = null;
}
@@ -9953,7 +9952,7 @@
mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_BT_TX_LEVELS);
mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
- ModemActivityInfo.TX_POWER_LEVELS);
+ ModemActivityInfo.getNumTxPowerLevels());
mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase);
mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
mOnBatteryTimeBase);
@@ -11131,26 +11130,7 @@
}
}
- private ModemActivityInfo mLastModemActivityInfo =
- new ModemActivityInfo(0, 0, 0, new int[0], 0);
-
- private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) {
- if (activityInfo == null) {
- return null;
- }
- int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
- for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
- txTimeMs[i] = activityInfo.getTransmitPowerInfo().get(i).getTimeInMillis()
- - mLastModemActivityInfo.getTransmitPowerInfo().get(i).getTimeInMillis();
- }
- ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(),
- activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(),
- activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(),
- txTimeMs,
- activityInfo.getReceiveTimeMillis() - mLastModemActivityInfo.getReceiveTimeMillis());
- mLastModemActivityInfo = activityInfo;
- return deltaInfo;
- }
+ private ModemActivityInfo mLastModemActivityInfo = null;
/**
* Distribute Cell radio energy info and network traffic to apps.
@@ -11159,7 +11139,9 @@
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
- ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo);
+ ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo
+ : mLastModemActivityInfo.getDelta(activityInfo);
+ mLastModemActivityInfo = activityInfo;
// Add modem tx power to history.
addModemTxPowerToHistory(deltaInfo);
@@ -11191,10 +11173,9 @@
mModemActivity.getSleepTimeCounter().addCountLocked(
deltaInfo.getSleepTimeMillis());
mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis());
- for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+ for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
mModemActivity.getTxTimeCounters()[lvl]
- .addCountLocked(deltaInfo.getTransmitPowerInfo()
- .get(lvl).getTimeInMillis());
+ .addCountLocked(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl));
}
// POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -11208,11 +11189,11 @@
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
+ deltaInfo.getReceiveTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
- List<TransmitPower> txPowerInfo = deltaInfo.getTransmitPowerInfo();
- for (int i = 0; i < Math.min(txPowerInfo.size(),
+ for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(),
CellSignalStrength.getNumSignalStrengthLevels()); i++) {
- energyUsed += txPowerInfo.get(i).getTimeInMillis() * mPowerProfile
- .getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+ energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i)
+ * mPowerProfile.getAveragePower(
+ PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
}
// We store the power drain as mAms.
@@ -11307,10 +11288,10 @@
}
if (totalTxPackets > 0 && entry.txPackets > 0) {
- for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
- long txMs =
- entry.txPackets * deltaInfo.getTransmitPowerInfo()
- .get(lvl).getTimeInMillis();
+ for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels();
+ lvl++) {
+ long txMs = entry.txPackets
+ * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl);
txMs /= totalTxPackets;
activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
}
@@ -11341,20 +11322,16 @@
if (activityInfo == null) {
return;
}
- List<TransmitPower> txPowerInfo = activityInfo.getTransmitPowerInfo();
- if (txPowerInfo == null || txPowerInfo.size() != ModemActivityInfo.TX_POWER_LEVELS) {
- return;
- }
final long elapsedRealtime = mClocks.elapsedRealtime();
final long uptime = mClocks.uptimeMillis();
int levelMaxTimeSpent = 0;
- for (int i = 1; i < txPowerInfo.size(); i++) {
- if (txPowerInfo.get(i).getTimeInMillis() > txPowerInfo.get(levelMaxTimeSpent)
- .getTimeInMillis()) {
+ for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
+ if (activityInfo.getTransmitDurationMillisAtPowerLevel(i)
+ > activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) {
levelMaxTimeSpent = i;
}
}
- if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+ if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) {
mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
addHistoryRecordLocked(elapsedRealtime, uptime);
}
@@ -12771,7 +12748,7 @@
timeInRxSignalStrengthLevelMs[i]
= getPhoneSignalStrengthTime(i, rawRealTime, which) / 1000;
}
- long[] txTimeMs = new long[Math.min(ModemActivityInfo.TX_POWER_LEVELS,
+ long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(),
counter.getTxTimeCounters().length)];
long totalTxTimeMs = 0;
for (int i = 0; i < txTimeMs.length; i++) {
@@ -14817,7 +14794,7 @@
mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_BT_TX_LEVELS, in);
mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
- ModemActivityInfo.TX_POWER_LEVELS, in);
+ ModemActivityInfo.getNumTxPowerLevels(), in);
mHasWifiReporting = in.readInt() != 0;
mHasBluetoothReporting = in.readInt() != 0;
mHasModemReporting = in.readInt() != 0;
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index ea3b3a7..7766b77 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -10,4 +10,6 @@
per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS
per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS
per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
+per-file *Kernel* = file:/BATTERY_STATS_OWNERS
+per-file *MultiState* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/util/IState.java b/core/java/com/android/internal/util/IState.java
index 07837bf..41b3d5e 100644
--- a/core/java/com/android/internal/util/IState.java
+++ b/core/java/com/android/internal/util/IState.java
@@ -27,12 +27,12 @@
public interface IState {
/**
- * Returned by processMessage to indicate the the message was processed.
+ * Returned by processMessage to indicate the message was processed.
*/
static final boolean HANDLED = true;
/**
- * Returned by processMessage to indicate the the message was NOT processed.
+ * Returned by processMessage to indicate the message was NOT processed.
*/
static final boolean NOT_HANDLED = false;
diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java
index 4613dad..d5c0f60 100644
--- a/core/java/com/android/internal/util/State.java
+++ b/core/java/com/android/internal/util/State.java
@@ -16,6 +16,7 @@
package com.android.internal.util;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Message;
@@ -25,6 +26,7 @@
*
* The class for implementing states in a StateMachine
*/
+@SuppressLint("AndroidFrameworkRequiresPermission")
public class State implements IState {
/**
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 4cff785..cb8d9d1 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -48,7 +48,7 @@
* in Object Oriented programming and are used to perform initialization and
* cleanup of the state respectively. The <code>getName</code> method returns the
* name of the state; the default implementation returns the class name. It may be
- * desirable to have <code>getName</code> return the the state instance name instead,
+ * desirable to have <code>getName</code> return the state instance name instead,
* in particular if a particular state class has multiple instances.</p>
*
* <p>When a state machine is created, <code>addState</code> is used to build the
@@ -433,14 +433,14 @@
/**
* Convenience constant that maybe returned by processMessage
- * to indicate the the message was processed and is not to be
+ * to indicate the message was processed and is not to be
* processed by parent states
*/
public static final boolean HANDLED = true;
/**
* Convenience constant that maybe returned by processMessage
- * to indicate the the message was NOT processed and is to be
+ * to indicate the message was NOT processed and is to be
* processed by parent states
*/
public static final boolean NOT_HANDLED = false;
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 701960e..6fb2904 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -75,3 +75,7 @@
# VINTF
per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS
per-file android_os_VintfRuntimeInfo* = file:platform/system/libvintf:/OWNERS
+
+# Battery
+per-file com_android_internal_os_Kernel* = file:/BATTERY_STATS_OWNERS
+per-file com_android_internal_os_*MultiStateCounter* = file:/BATTERY_STATS_OWNERS
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 241570a..ac32038 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -98,6 +98,15 @@
}
}
+static void android_os_Parcel_markForBinder(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jobject binder)
+{
+ Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+ if (parcel) {
+ parcel->markForBinder(ibinderForJavaObject(env, binder));
+ }
+}
+
static jint android_os_Parcel_dataSize(jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
@@ -766,7 +775,9 @@
static const JNINativeMethod gParcelMethods[] = {
// @CriticalNative
- {"nativeMarkSensitive", "(J)V", (void*)android_os_Parcel_markSensitive},
+ {"nativeMarkSensitive", "(J)V", (void*)android_os_Parcel_markSensitive},
+ // @FastNative
+ {"nativeMarkForBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_markForBinder},
// @CriticalNative
{"nativeDataSize", "(J)I", (void*)android_os_Parcel_dataSize},
// @CriticalNative
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 515c08d..288327e 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1129,14 +1129,14 @@
}
// Relabel directory
-static void relabelDir(const char* path, security_context_t context, fail_fn_t fail_fn) {
+static void relabelDir(const char* path, const char* context, fail_fn_t fail_fn) {
if (setfilecon(path, context) != 0) {
fail_fn(CREATE_ERROR("Failed to setfilecon %s %s", path, strerror(errno)));
}
}
// Relabel all directories under a path non-recursively.
-static void relabelAllDirs(const char* path, security_context_t context, fail_fn_t fail_fn) {
+static void relabelAllDirs(const char* path, const char* context, fail_fn_t fail_fn) {
DIR* dir = opendir(path);
if (dir == nullptr) {
fail_fn(CREATE_ERROR("Failed to opendir %s", path));
@@ -1211,7 +1211,7 @@
snprintf(internalDePath, PATH_MAX, "/data/user_de");
snprintf(externalPrivateMountPath, PATH_MAX, "/mnt/expand");
- security_context_t dataDataContext = nullptr;
+ char* dataDataContext = nullptr;
if (getfilecon(internalDePath, &dataDataContext) < 0) {
fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", internalDePath,
strerror(errno)));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0135e45c..35a3cde 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -196,6 +196,9 @@
android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_DEVICE_AVAILABLE" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE" />
<protected-broadcast
android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 14d3147..ac19121 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/public.xml b/core/res/res/values/public.xml
index 6a4702b..c1c1858 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3046,6 +3046,9 @@
<public name="canPauseRecording" />
<!-- attribute definitions go here -->
<public name="requireDeviceScreenOn" />
+ </public-group>
+
+ <public-group type="attr" first-id="0x01010624">
<public name="memtagMode" />
<public name="nativeHeapZeroInitialized" />
</public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2be5152..aebad6a 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/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 37d059a..f1c66c5 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -265,7 +265,7 @@
<!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
visual voicemail code for T-Mobile: 122 -->
- <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245" />
+ <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611" />
<!-- Vietnam: 1-5 digits (standard system default, not country specific) -->
<shortcode country="vn" pattern="\\d{1,5}" free="5001|9055" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 000e870..2d63351 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -88,6 +88,11 @@
false /* launchActivity */);
@Test
+ public void testTemporaryDirectory() throws Exception {
+ assertEquals(System.getProperty("java.io.tmpdir"), System.getenv("TMPDIR"));
+ }
+
+ @Test
public void testDoubleRelaunch() throws Exception {
final Activity activity = mActivityTestRule.launchActivity(new Intent());
final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index 4cc70ba..9d2cab3 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 @@
@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 @@
// 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/core/tests/hosttests/Android.mk b/core/tests/hosttests/Android.mk
deleted file mode 100644
index f26d401..0000000
--- a/core/tests/hosttests/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/hosttests/test-apps/Android.mk b/core/tests/hosttests/test-apps/Android.mk
deleted file mode 100644
index e25764f..0000000
--- a/core/tests/hosttests/test-apps/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.bp b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.bp
new file mode 100644
index 0000000..d439124
--- /dev/null
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2010 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "DownloadManagerTestApp",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "android-common",
+ "mockwebserver",
+ "junit",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ platform_apis: true,
+
+ // Need to run as system app to get access to Settings. This test won't work for user builds.
+ certificate: "platform",
+}
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
deleted file mode 100644
index d9e6151..0000000
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2010 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver junit
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_PACKAGE_NAME := DownloadManagerTestApp
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-ifneq ($(TARGET_BUILD_VARIANT),user)
-# Need to run as system app to get access to Settings. This test won't work for user builds.
-LOCAL_CERTIFICATE := platform
-endif
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
new file mode 100644
index 0000000..d0645b0
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// The application with a minimal main dex
+android_test_helper_app {
+ name: "MultiDexLegacyAndException",
+
+ static_libs: [
+ "android-support-multidex",
+ "android-support-multidex-instrumentation",
+ "androidx.test.rules",
+ ],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ javacflags: ["-nowarn"],
+
+ main_dex_rules: [":mainDexClassesRules"],
+ dxflags: [
+ // --debug triggers the old --minimal-main-dex behavior
+ "--debug",
+ ],
+ optimize: {
+ // disable optimization to force D8 instead of R8, as R8 doesn't support
+ // --main-dex-rules.
+ enabled: false,
+ },
+
+ min_sdk_version: "16",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
deleted file mode 100644
index 2d8556f..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-## The application with a minimal main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex android-support-multidex-instrumentation androidx.test.rules
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyAndException
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVACFLAGS := -nowarn
-
-mainDexList:= \
- $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-LOCAL_MIN_SDK_VERSION := 8
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
- $(hide) mkdir -p $(dir $@)
- PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
- echo "com/android/multidexlegacyandexception/Test.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
new file mode 100644
index 0000000..c0c8aba
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
@@ -0,0 +1,70 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// The application with a minimal main dex
+android_test_helper_app {
+ name: "MultiDexLegacyTestApp",
+
+ static_libs: ["android-support-multidex"],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ main_dex_rules: [
+ ":mainDexClassesRules",
+ "mainDexClasses.rules",
+ ],
+ dxflags: [
+ // --debug triggers the old --minimal-main-dex behavior
+ "--debug",
+ ],
+ optimize: {
+ // disable optimization to force D8 instead of R8, as R8 doesn't support
+ // --main-dex-rules.
+ enabled: false,
+ },
+
+ min_sdk_version: "16",
+}
+
+android_test_helper_app {
+ name: "MultiDexLegacyTestApp2",
+
+ static_libs: ["android-support-multidex"],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ main_dex_rules: [
+ ":mainDexClassesRules",
+ "mainDexClasses.rules",
+ ],
+ dxflags: [
+ // --release disables the old --minimal-main-dex behavior
+ "--release",
+ ],
+ optimize: {
+ // disable optimization to force D8 instead of R8, as R8 doesn't support
+ // --main-dex-rules.
+ enabled: false,
+ },
+
+ min_sdk_version: "16",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
deleted file mode 100644
index d7af2d9..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ /dev/null
@@ -1,87 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-## The application with a minimal main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-mainDexList:= \
- $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-LOCAL_MIN_SDK_VERSION := 8
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
- $(hide) mkdir -p $(dir $@)
- PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
- echo "com/android/multidexlegacytestapp/Test.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
-
-## The application with a full main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-mainDexList2:= \
- $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList2)
-
-LOCAL_MIN_SDK_VERSION := 8
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList2): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
- $(hide) mkdir -p $(dir $@)
- PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
- echo "com/android/multidexlegacytestapp/Test.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList2)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/mainDexClasses.rules b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/mainDexClasses.rules
new file mode 100644
index 0000000..91e6ddb
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/mainDexClasses.rules
@@ -0,0 +1 @@
+-keep class com.android.multidexlegacytestapp.Test
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
new file mode 100644
index 0000000..fe29416
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// The application with a minimal main dex
+android_test {
+ name: "MultiDexLegacyTestAppTests",
+
+ static_libs: ["android-support-multidex-instrumentation"],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ javacflags: ["-nowarn"],
+
+ min_sdk_version: "16",
+
+ instrumentation_for: "MultiDexLegacyTestApp",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk
deleted file mode 100644
index 236c740..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-## The application with a minimal main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVACFLAGS := -nowarn
-
-LOCAL_MIN_SDK_VERSION := 8
-
-LOCAL_INSTRUMENTATION_FOR := MultiDexLegacyTestApp
-
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
new file mode 100644
index 0000000..c558153
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
@@ -0,0 +1,57 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// The tests with only one dex
+android_test {
+ name: "MultiDexLegacyTestAppTests2",
+
+ static_libs: [
+ "android-support-multidex-instrumentation",
+ "androidx.test.rules",
+ ],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ javacflags: ["-nowarn"],
+
+ min_sdk_version: "16",
+
+ instrumentation_for: "MultiDexLegacyTestApp",
+}
+
+// The tests with a minimal main dex
+android_test {
+ name: "MultiDexLegacyTestAppTests2-multidex",
+
+ static_libs: [
+ "android-support-multidex-instrumentation",
+ "androidx.test.rules",
+ ],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ javacflags: ["-nowarn"],
+
+ min_sdk_version: "16",
+
+ instrumentation_for: "MultiDexLegacyTestApp",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
deleted file mode 100644
index 6f6ccfe..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-## The tests with only one dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation androidx.test.rules
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVACFLAGS := -nowarn
-
-LOCAL_MIN_SDK_VERSION := 8
-
-LOCAL_INSTRUMENTATION_FOR := MultiDexLegacyTestApp
-
-include $(BUILD_PACKAGE)
-
-
-## The tests with a minimal main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation androidx.test.rules
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests2-multidex
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVACFLAGS := -nowarn
-
-LOCAL_MIN_SDK_VERSION := 8
-
-LOCAL_INSTRUMENTATION_FOR := MultiDexLegacyTestApp
-
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.bp
new file mode 100644
index 0000000..15bc4ef
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "MultiDexLegacyTestApp_without_corrupted",
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ static_libs: ["android-support-multidex"],
+}
+
+java_genrule {
+ name: "MultiDexLegacyTestApp_genrule",
+ srcs: [
+ ":MultiDexLegacyTestApp_without_corrupted",
+ ],
+ tools: [
+ "soong_zip",
+ "merge_zips",
+ ],
+ out: ["MultiDexLegacyTestApp_with_corrupted.apk"],
+ cmd: "touch $(genDir)/classes2.dex &&" +
+ " $(location soong_zip) -o $(genDir)/corrupted.zip -j -f $(genDir)/classes2.dex &&" +
+ " $(location merge_zips) $(out) $(location :MultiDexLegacyTestApp_without_corrupted) $(genDir)/corrupted.zip",
+}
+
+android_test_import {
+ name: "MultiDexLegacyTestApp_corrupted",
+ apk: ":MultiDexLegacyTestApp_genrule",
+ default_dev_cert: true,
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk
deleted file mode 100644
index 33a46ea..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 18
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp_corrupted
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
-
-corrupted_classes2_dex := $(dir $(built_dex))/classes2.dex
-
-$(corrupted_classes2_dex): $(built_dex)
- $(hide) touch $@
-
-$(LOCAL_BUILT_MODULE): $(corrupted_classes2_dex)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.bp
new file mode 100644
index 0000000..de0657f
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "MultiDexLegacyTestServices",
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ static_libs: ["android-support-multidex"],
+
+ main_dex_rules: [":mainDexClassesRules"],
+ dxflags: [
+ // --debug triggers the old --minimal-main-dex behavior
+ "--debug",
+ ],
+ optimize: {
+ // disable optimization to force D8 instead of R8, as R8 doesn't support
+ // --main-dex-rules.
+ enabled: false,
+ },
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
deleted file mode 100644
index efc0688..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestServices
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-mainDexList:= \
- $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
- $(hide) mkdir -p $(dir $@)
- PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
index 56f10fe..b62b25c 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
@@ -24,7 +24,7 @@
android_test {
name: "MultiDexLegacyTestServicesTests",
srcs: ["src/**/*.java"],
- sdk_version: "9",
+ sdk_version: "16",
dex_preopt: {
enabled: false,
},
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
new file mode 100644
index 0000000..75c753c
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "MultiDexLegacyTestServicesTests2",
+
+ srcs: ["src/**/*.java"],
+
+ libs: ["android-support-multidex"],
+ static_libs: ["androidx.test.rules"],
+
+ sdk_version: "16",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
deleted file mode 100644
index 3920fd6..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_JAVA_LIBRARIES := android-support-multidex
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.bp
new file mode 100644
index 0000000..23c62dc
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "MultiDexLegacyVersionedTestApp_v1",
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ static_libs: ["android-support-multidex"],
+
+ main_dex_rules: [
+ ":mainDexClassesRules",
+ "mainDexClasses.rules",
+ ],
+ dxflags: [
+ // --debug triggers the old --minimal-main-dex behavior
+ "--debug",
+ ],
+ optimize: {
+ // disable optimization to force D8 instead of R8, as R8 doesn't support
+ // --main-dex-rules.
+ enabled: false,
+ },
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
deleted file mode 100644
index 2323ad9..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v1
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-mainDexList:= \
- $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
- $(hide) mkdir -p $(dir $@)
- PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
- echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/mainDexClasses.rules b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/mainDexClasses.rules
new file mode 100644
index 0000000..1cdf3af
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/mainDexClasses.rules
@@ -0,0 +1 @@
+-keep class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.bp
new file mode 100644
index 0000000..6cd3df7
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "MultiDexLegacyVersionedTestApp_v2",
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ static_libs: ["android-support-multidex"],
+
+ main_dex_rules: [
+ ":mainDexClassesRules",
+ "mainDexClasses.rules",
+ ],
+ dxflags: [
+ // --debug triggers the old --minimal-main-dex behavior
+ "--debug",
+ ],
+ optimize: {
+ // disable optimization to force D8 instead of R8, as R8 doesn't support
+ // --main-dex-rules.
+ enabled: false,
+ },
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
deleted file mode 100644
index 79a5906..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-mainDexList:= \
- $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
- $(hide) mkdir -p $(dir $@)
- PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
- echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/mainDexClasses.rules b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/mainDexClasses.rules
new file mode 100644
index 0000000..1cdf3af
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/mainDexClasses.rules
@@ -0,0 +1 @@
+-keep class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.bp
new file mode 100644
index 0000000..34dba40
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2014 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.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "MultiDexLegacyVersionedTestApp_v3",
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "16",
+
+ static_libs: ["android-support-multidex"],
+
+ main_dex_rules: [
+ ":mainDexClassesRules",
+ "mainDexClasses.rules",
+ ],
+ dxflags: [
+ // --debug triggers the old --minimal-main-dex behavior
+ "--debug",
+ ],
+ optimize: {
+ // disable optimization to force D8 instead of R8, as R8 doesn't support
+ // --main-dex-rules.
+ enabled: false,
+ },
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
deleted file mode 100644
index 521bad0..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v3
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-mainDexList:= \
- $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
- $(hide) mkdir -p $(dir $@)
- PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
- echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/mainDexClasses.rules b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/mainDexClasses.rules
new file mode 100644
index 0000000..1cdf3af
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/mainDexClasses.rules
@@ -0,0 +1 @@
+-keep class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index edf473e..b85cb9c 100644
--- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -542,83 +542,83 @@
public void testStateMachineEnterExitTransitionToTest() throws Exception {
//if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
- StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest =
- new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest");
- smEnterExitTranstionToTest.start();
- if (smEnterExitTranstionToTest.isDbg()) {
+ StateMachineEnterExitTransitionToTest smEnterExitTransitionToTest =
+ new StateMachineEnterExitTransitionToTest("smEnterExitTransitionToTest");
+ smEnterExitTransitionToTest.start();
+ if (smEnterExitTransitionToTest.isDbg()) {
tlog("testStateMachineEnterExitTransitionToTest E");
}
- synchronized (smEnterExitTranstionToTest) {
- smEnterExitTranstionToTest.sendMessage(TEST_CMD_1);
+ synchronized (smEnterExitTransitionToTest) {
+ smEnterExitTransitionToTest.sendMessage(TEST_CMD_1);
try {
// wait for the messages to be handled
- smEnterExitTranstionToTest.wait();
+ smEnterExitTransitionToTest.wait();
} catch (InterruptedException e) {
tloge("testStateMachineEnterExitTransitionToTest: exception while waiting "
+ e.getMessage());
}
}
- dumpLogRecs(smEnterExitTranstionToTest);
+ dumpLogRecs(smEnterExitTransitionToTest);
- assertEquals(9, smEnterExitTranstionToTest.getLogRecCount());
+ assertEquals(9, smEnterExitTransitionToTest.getLogRecCount());
LogRec lr;
- lr = smEnterExitTranstionToTest.getLogRec(0);
+ lr = smEnterExitTransitionToTest.getLogRec(0);
assertEquals(ENTER, lr.getInfo());
- assertEquals(smEnterExitTranstionToTest.mS1, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS1, lr.getState());
- lr = smEnterExitTranstionToTest.getLogRec(1);
+ lr = smEnterExitTransitionToTest.getLogRec(1);
assertEquals(EXIT, lr.getInfo());
- assertEquals(smEnterExitTranstionToTest.mS1, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS1, lr.getState());
- lr = smEnterExitTranstionToTest.getLogRec(2);
+ lr = smEnterExitTransitionToTest.getLogRec(2);
assertEquals(ENTER, lr.getInfo());
- assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS2, lr.getState());
- lr = smEnterExitTranstionToTest.getLogRec(3);
+ lr = smEnterExitTransitionToTest.getLogRec(3);
assertEquals(TEST_CMD_1, lr.getWhat());
- assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
- assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState());
- assertEquals(smEnterExitTranstionToTest.mS3, lr.getDestState());
+ assertEquals(smEnterExitTransitionToTest.mS2, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS2, lr.getOriginalState());
+ assertEquals(smEnterExitTransitionToTest.mS3, lr.getDestState());
- lr = smEnterExitTranstionToTest.getLogRec(4);
+ lr = smEnterExitTransitionToTest.getLogRec(4);
assertEquals(TEST_CMD_1, lr.getWhat());
- assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
- assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState());
- assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+ assertEquals(smEnterExitTransitionToTest.mS2, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS2, lr.getOriginalState());
+ assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState());
assertEquals(EXIT, lr.getInfo());
- lr = smEnterExitTranstionToTest.getLogRec(5);
+ lr = smEnterExitTransitionToTest.getLogRec(5);
assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(ENTER, lr.getInfo());
- assertEquals(smEnterExitTranstionToTest.mS3, lr.getState());
- assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState());
- assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+ assertEquals(smEnterExitTransitionToTest.mS3, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS3, lr.getOriginalState());
+ assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState());
- lr = smEnterExitTranstionToTest.getLogRec(6);
+ lr = smEnterExitTransitionToTest.getLogRec(6);
assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(EXIT, lr.getInfo());
- assertEquals(smEnterExitTranstionToTest.mS3, lr.getState());
- assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState());
- assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+ assertEquals(smEnterExitTransitionToTest.mS3, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS3, lr.getOriginalState());
+ assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState());
- lr = smEnterExitTranstionToTest.getLogRec(7);
+ lr = smEnterExitTransitionToTest.getLogRec(7);
assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(ENTER, lr.getInfo());
- assertEquals(smEnterExitTranstionToTest.mS4, lr.getState());
- assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState());
- assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+ assertEquals(smEnterExitTransitionToTest.mS4, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS4, lr.getOriginalState());
+ assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState());
- lr = smEnterExitTranstionToTest.getLogRec(8);
+ lr = smEnterExitTransitionToTest.getLogRec(8);
assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(EXIT, lr.getInfo());
- assertEquals(smEnterExitTranstionToTest.mS4, lr.getState());
- assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState());
+ assertEquals(smEnterExitTransitionToTest.mS4, lr.getState());
+ assertEquals(smEnterExitTransitionToTest.mS4, lr.getOriginalState());
- if (smEnterExitTranstionToTest.isDbg()) {
+ if (smEnterExitTransitionToTest.isDbg()) {
tlog("testStateMachineEnterExitTransitionToTest X");
}
}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 8f175bb..1e68585 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -160,7 +160,7 @@
* not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
*
* <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
- * from the verifier. The content can be defined in the way appropriate for the credential, byt
+ * from the verifier. The content can be defined in the way appropriate for the credential, but
* there are three requirements that must be met to work with this API:
* <ul>
* <li>The content must be a CBOR-encoded structure.</li>
@@ -205,9 +205,9 @@
* must appear somewhere in {@code sessionTranscript} and ditto for the 32 bytes for the Y
* coordinate.
*
- * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1}
- * structure as defined in RFC 8152. For the payload nil shall be used and the
- * detached payload is the ReaderAuthenticationBytes CBOR described below.
+ * <p>If {@code readerSignature} is not {@code null} it must be the bytes of a
+ * {@code COSE_Sign1} structure as defined in RFC 8152. For the payload nil shall be used and
+ * the detached payload is the ReaderAuthenticationBytes CBOR described below.
* <pre>
* ReaderAuthentication = [
* "ReaderAuthentication",
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index e08b99d..d897e94f 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -32,6 +32,8 @@
using namespace android::uirenderer::renderthread;
+static constexpr bool sEnableExtraCropInset = true;
+
namespace android {
namespace uirenderer {
@@ -71,6 +73,20 @@
ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
return CopyResult::SourceEmpty;
}
+
+ if (sEnableExtraCropInset &&
+ (cropRect.right - cropRect.left != bitmap->width() ||
+ cropRect.bottom - cropRect.top != bitmap->height())) {
+ /*
+ * When we need use filtering, we should also make border shrink here like gui.
+ * But we could not check format for YUV or RGB here... Just use 1 pix.
+ */
+ cropRect.left += 0.5f;
+ cropRect.top += 0.5f;
+ cropRect.right -= 0.5f;
+ cropRect.bottom -= 0.5f;
+ }
+
UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
AHardwareBuffer_Desc description;
AHardwareBuffer_describe(sourceBuffer.get(), &description);
diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp
index ec115b4..86d4742 100644
--- a/libs/hwui/jni/PaintFilter.cpp
+++ b/libs/hwui/jni/PaintFilter.cpp
@@ -74,7 +74,7 @@
result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods,
NELEM(paintflags_methods));
- return 0;
+ return result;
}
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9657b25e..c7c503d 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2178,12 +2178,6 @@
if (size == null || size.getWidth() * size.getHeight() <= 0) {
continue;
}
- if (size.getWidth() > SIZE_RANGE.getUpper()
- || size.getHeight() > SIZE_RANGE.getUpper()) {
- size = new Size(
- Math.min(size.getWidth(), SIZE_RANGE.getUpper()),
- Math.min(size.getHeight(), SIZE_RANGE.getUpper()));
- }
Range<Long> range = Utils.parseLongRange(map.get(key), null);
if (range == null || range.getLower() < 0 || range.getUpper() < 0) {
continue;
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 559a61d..0f9e89a 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -900,7 +900,7 @@
private @NonNull List<Bitmap> getFramesAtIndexInternal(
int frameIndex, int numFrames, @Nullable BitmapParams params) {
if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) {
- throw new IllegalStateException("Does not contail video or image sequences");
+ throw new IllegalStateException("Does not contain video or image sequences");
}
int frameCount = Integer.parseInt(
extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT));
@@ -1018,7 +1018,7 @@
private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) {
if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
- throw new IllegalStateException("Does not contail still images");
+ throw new IllegalStateException("Does not contain still images");
}
String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 0d53ab1..2691983 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-
#include <stdio.h>
+#include <unordered_set>
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioEffects-JNI"
@@ -58,21 +58,14 @@
struct effect_callback_cookie {
jclass audioEffect_class; // AudioEffect class
jobject audioEffect_ref; // AudioEffect object instance
- };
-
-// ----------------------------------------------------------------------------
-class AudioEffectJniStorage {
- public:
- effect_callback_cookie mCallbackData;
-
- AudioEffectJniStorage() {
- }
-
- ~AudioEffectJniStorage() {
- }
-
+ bool busy;
+ Condition cond;
};
+// ----------------------------------------------------------------------------
+struct AudioEffectJniStorage {
+ effect_callback_cookie mCallbackData{};
+};
jint AudioEffectJni::translateNativeErrorToJava(int code) {
switch(code) {
@@ -101,6 +94,7 @@
}
static Mutex sLock;
+static std::unordered_set<effect_callback_cookie*> sAudioEffectCallBackCookies;
// ----------------------------------------------------------------------------
static void effectCallback(int event, void* user, void *info) {
@@ -121,7 +115,13 @@
ALOGW("effectCallback error user %p, env %p", user, env);
return;
}
-
+ {
+ Mutex::Autolock l(sLock);
+ if (sAudioEffectCallBackCookies.count(callbackInfo) == 0) {
+ return;
+ }
+ callbackInfo->busy = true;
+ }
ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
callbackInfo,
callbackInfo->audioEffect_ref,
@@ -188,6 +188,11 @@
env->ExceptionDescribe();
env->ExceptionClear();
}
+ {
+ Mutex::Autolock l(sLock);
+ callbackInfo->busy = false;
+ callbackInfo->cond.broadcast();
+ }
}
// ----------------------------------------------------------------------------
@@ -396,6 +401,10 @@
setAudioEffect(env, thiz, lpAudioEffect);
}
+ {
+ Mutex::Autolock l(sLock);
+ sAudioEffectCallBackCookies.insert(&lpJniStorage->mCallbackData);
+ }
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
return (jint) AUDIOEFFECT_SUCCESS;
@@ -427,6 +436,7 @@
// ----------------------------------------------------------------------------
+#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0);
if (lpAudioEffect == 0) {
@@ -442,7 +452,17 @@
env->SetLongField(thiz, fields.fidJniData, 0);
if (lpJniStorage) {
- ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
+ Mutex::Autolock l(sLock);
+ effect_callback_cookie *lpCookie = &lpJniStorage->mCallbackData;
+ ALOGV("deleting lpJniStorage: %p\n", lpJniStorage);
+ sAudioEffectCallBackCookies.erase(lpCookie);
+ while (lpCookie->busy) {
+ if (lpCookie->cond.waitRelative(sLock,
+ milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
+ NO_ERROR) {
+ break;
+ }
+ }
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
delete lpJniStorage;
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 4c5970a..609fafe 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -15,6 +15,7 @@
*/
#include <stdio.h>
+#include <unordered_set>
//#define LOG_NDEBUG 0
#define LOG_TAG "visualizers-JNI"
@@ -63,6 +64,12 @@
jclass visualizer_class; // Visualizer class
jobject visualizer_ref; // Visualizer object instance
+ // 'busy_count' and 'cond' together with 'sLock' are used to serialize
+ // concurrent access to the callback cookie from 'setup'/'release'
+ // and the callback.
+ int busy_count;
+ Condition cond;
+
// Lazily allocated arrays used to hold callback data provided to java
// applications. These arrays are allocated during the first callback and
// reallocated when the size of the callback data changes. Allocating on
@@ -70,14 +77,12 @@
// reference to the provided data (they need to make a copy if they want to
// hold onto outside of the callback scope), but it avoids GC thrash caused
// by constantly allocating and releasing arrays to hold callback data.
+ // 'callback_data_lock' must never be held at the same time with 'sLock'.
Mutex callback_data_lock;
jbyteArray waveform_data;
jbyteArray fft_data;
- visualizer_callback_cookie() {
- waveform_data = NULL;
- fft_data = NULL;
- }
+ // Assumes use of default initialization by the client.
~visualizer_callback_cookie() {
cleanupBuffers();
@@ -102,15 +107,8 @@
};
// ----------------------------------------------------------------------------
-class VisualizerJniStorage {
- public:
- visualizer_callback_cookie mCallbackData;
-
- VisualizerJniStorage() {
- }
-
- ~VisualizerJniStorage() {
- }
+struct VisualizerJniStorage {
+ visualizer_callback_cookie mCallbackData{};
};
@@ -136,6 +134,7 @@
}
static Mutex sLock;
+static std::unordered_set<visualizer_callback_cookie*> sVisualizerCallBackCookies;
// ----------------------------------------------------------------------------
static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
@@ -173,11 +172,19 @@
return;
}
+ {
+ Mutex::Autolock l(sLock);
+ if (sVisualizerCallBackCookies.count(callbackInfo) == 0) {
+ return;
+ }
+ callbackInfo->busy_count++;
+ }
ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
callbackInfo,
callbackInfo->visualizer_ref,
callbackInfo->visualizer_class);
+ {
AutoMutex lock(&callbackInfo->callback_data_lock);
if (waveformSize != 0 && waveform != NULL) {
@@ -219,11 +226,17 @@
jArray);
}
}
+ } // callback_data_lock scope
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
+ {
+ Mutex::Autolock l(sLock);
+ callbackInfo->busy_count--;
+ callbackInfo->cond.broadcast();
+ }
}
// ----------------------------------------------------------------------------
@@ -332,16 +345,41 @@
void *info) {
if ((event == AudioEffect::EVENT_ERROR) &&
(*((status_t*)info) == DEAD_OBJECT)) {
- VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user;
- visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
+ visualizer_callback_cookie* callbackInfo =
+ (visualizer_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (!user || !env) {
+ ALOGW("effectCallback error user %p, env %p", user, env);
+ return;
+ }
+ {
+ Mutex::Autolock l(sLock);
+ if (sVisualizerCallBackCookies.count(callbackInfo) == 0) {
+ return;
+ }
+ callbackInfo->busy_count++;
+ }
+ ALOGV("effectCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
+ callbackInfo,
+ callbackInfo->visualizer_ref,
+ callbackInfo->visualizer_class);
+
env->CallStaticVoidMethod(
callbackInfo->visualizer_class,
fields.midPostNativeEvent,
callbackInfo->visualizer_ref,
NATIVE_EVENT_SERVER_DIED,
0, NULL);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ {
+ Mutex::Autolock l(sLock);
+ callbackInfo->busy_count--;
+ callbackInfo->cond.broadcast();
+ }
}
}
@@ -389,7 +427,7 @@
}
lpVisualizer->set(0,
android_media_visualizer_effect_callback,
- lpJniStorage,
+ &lpJniStorage->mCallbackData,
(audio_session_t) sessionId);
lStatus = translateError(lpVisualizer->initCheck());
@@ -410,6 +448,10 @@
setVisualizer(env, thiz, lpVisualizer);
+ {
+ Mutex::Autolock l(sLock);
+ sVisualizerCallBackCookies.insert(&lpJniStorage->mCallbackData);
+ }
env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
return VISUALIZER_SUCCESS;
@@ -432,13 +474,15 @@
}
// ----------------------------------------------------------------------------
+#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
- { //limit scope so that lpVisualizer is deleted before JNI storage data.
+ {
sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
if (lpVisualizer == 0) {
return;
}
lpVisualizer->release();
+ // Visualizer can still can be held by AudioEffect::EffectClient
}
// delete the JNI data
VisualizerJniStorage* lpJniStorage =
@@ -449,9 +493,22 @@
env->SetLongField(thiz, fields.fidJniData, 0);
if (lpJniStorage) {
+ {
+ Mutex::Autolock l(sLock);
+ visualizer_callback_cookie *lpCookie = &lpJniStorage->mCallbackData;
ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
+ sVisualizerCallBackCookies.erase(lpCookie);
+ while (lpCookie->busy_count > 0) {
+ if (lpCookie->cond.waitRelative(sLock,
+ milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
+ NO_ERROR) {
+ break;
+ }
+ }
+ ALOG_ASSERT(lpCookie->busy_count == 0, "Unbalanced busy_count inc/dec");
env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
+ } // sLock scope
delete lpJniStorage;
}
}
@@ -707,4 +764,3 @@
{
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
-
diff --git a/media/tests/EffectsTest/AndroidManifest.xml b/media/tests/EffectsTest/AndroidManifest.xml
index 9b59891..ad0c10e 100644
--- a/media/tests/EffectsTest/AndroidManifest.xml
+++ b/media/tests/EffectsTest/AndroidManifest.xml
@@ -13,6 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!--
+Make sure to enable access to the mic in settings and run:
+adb shell am compat enable ALLOW_TEST_API_ACCESS com.android.effectstest
+-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.effectstest">
diff --git a/media/tests/EffectsTest/res/layout/bassboosttest.xml b/media/tests/EffectsTest/res/layout/bassboosttest.xml
index ac912c8..5f9132c 100644
--- a/media/tests/EffectsTest/res/layout/bassboosttest.xml
+++ b/media/tests/EffectsTest/res/layout/bassboosttest.xml
@@ -187,6 +187,11 @@
android:layout_height="wrap_content"
android:scaleType="fitXY"/>
+ <Button android:id="@+id/hammer_on_release_bug"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/hammer_on_release_bug_name">
+ </Button>
+
</LinearLayout>
</ScrollView>
diff --git a/media/tests/EffectsTest/res/layout/visualizertest.xml b/media/tests/EffectsTest/res/layout/visualizertest.xml
index 18d7a36..85dabbc 100644
--- a/media/tests/EffectsTest/res/layout/visualizertest.xml
+++ b/media/tests/EffectsTest/res/layout/visualizertest.xml
@@ -175,6 +175,11 @@
</LinearLayout>
+ <Button android:id="@+id/hammer_on_release_bug"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/hammer_on_release_bug_name">
+ </Button>
+
<ImageView
android:src="@android:drawable/divider_horizontal_dark"
android:layout_width="fill_parent"
diff --git a/media/tests/EffectsTest/res/values/strings.xml b/media/tests/EffectsTest/res/values/strings.xml
index 7c12da1..a44c7e9 100644
--- a/media/tests/EffectsTest/res/values/strings.xml
+++ b/media/tests/EffectsTest/res/values/strings.xml
@@ -37,4 +37,6 @@
<string name="send_level_name">Send Level</string>
<!-- Toggles use of a multi-threaded client for an effect [CHAR LIMIT=24] -->
<string name="effect_multithreaded">Multithreaded Use</string>
+ <!-- Runs a stress test for a bug related to simultaneous release of multiple effect instances [CHAR LIMIT=24] -->
+ <string name="hammer_on_release_bug_name">Hammer on release()</string>
</resources>
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
index cce2acc..a207bf1 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
@@ -17,29 +17,24 @@
package com.android.effectstest;
import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.BassBoost;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.View.OnClickListener;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.View.OnClickListener;
import android.widget.Button;
-import android.widget.TextView;
-import android.widget.EditText;
-import android.widget.SeekBar;
-import android.widget.ToggleButton;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.ToggleButton;
-import android.media.audiofx.BassBoost;
-import android.media.audiofx.AudioEffect;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
public class BassBoostTest extends Activity implements OnCheckedChangeListener {
@@ -78,6 +73,9 @@
mReleaseButton = (ToggleButton)findViewById(R.id.bbReleaseButton);
mOnOffButton = (ToggleButton)findViewById(R.id.bassboostOnOff);
+ final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug);
+ hammerReleaseTest.setEnabled(false);
+
getEffect(sSession);
if (mBassBoost != null) {
@@ -93,6 +91,14 @@
mStrength = new BassBoostParam(mBassBoost, 0, 1000, seekBar, textView);
seekBar.setOnSeekBarChangeListener(mStrength);
mStrength.setEnabled(mBassBoost.getStrengthSupported());
+
+ hammerReleaseTest.setEnabled(true);
+ hammerReleaseTest.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ runHammerReleaseTest(hammerReleaseTest);
+ }
+ });
}
}
@@ -273,4 +279,52 @@
}
}
+ // Stress-tests releasing of AudioEffect by doing repeated creation
+ // and subsequent releasing. Also forces emission of callbacks from
+ // the AudioFlinger by setting a control status listener. Since all
+ // effect instances are bound to the same session, the AF will
+ // notify them about the change in their status. This can reveal racy
+ // behavior w.r.t. releasing.
+ class HammerReleaseTest extends Thread {
+ private static final int NUM_EFFECTS = 10;
+ private static final int NUM_ITERATIONS = 100;
+ private final int mSession;
+ private final Runnable mOnComplete;
+
+ HammerReleaseTest(int session, Runnable onComplete) {
+ mSession = session;
+ mOnComplete = onComplete;
+ }
+
+ @Override
+ public void run() {
+ Log.w(TAG, "HammerReleaseTest started");
+ BassBoost[] effects = new BassBoost[NUM_EFFECTS];
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ for (int j = 0; j < NUM_EFFECTS; j++) {
+ effects[j] = new BassBoost(0, mSession);
+ effects[j].setControlStatusListener(mEffectListener);
+ yield();
+ }
+ for (int j = NUM_EFFECTS - 1; j >= 0; j--) {
+ Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]);
+ effects[j].release();
+ effects[j] = null;
+ yield();
+ }
+ }
+ Log.w(TAG, "HammerReleaseTest ended");
+ runOnUiThread(mOnComplete);
+ }
+ }
+
+ private void runHammerReleaseTest(Button controlButton) {
+ controlButton.setEnabled(false);
+ HammerReleaseTest thread = new HammerReleaseTest(sSession,
+ () -> {
+ controlButton.setEnabled(true);
+ });
+ thread.start();
+ }
+
}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
index 2e141c5..dcfe11a 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
@@ -17,6 +17,7 @@
package com.android.effectstest;
import android.app.Activity;
+import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -24,6 +25,8 @@
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
@@ -74,11 +77,22 @@
mCallbackOn = false;
mCallbackButton.setChecked(mCallbackOn);
+ final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug);
+ hammerReleaseTest.setEnabled(false);
+
mMultithreadedButton.setOnCheckedChangeListener(this);
if (getEffect(sSession) != null) {
mReleaseButton.setOnCheckedChangeListener(this);
mOnOffButton.setOnCheckedChangeListener(this);
mCallbackButton.setOnCheckedChangeListener(this);
+
+ hammerReleaseTest.setEnabled(true);
+ hammerReleaseTest.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ runHammerReleaseTest(hammerReleaseTest);
+ }
+ });
}
}
@@ -214,4 +228,50 @@
}
}
+ // Stress-tests releasing of AudioEffect by doing repeated creation
+ // and subsequent releasing. Unlike a similar class in BassBoostTest,
+ // this one doesn't sets a control status listener because Visualizer
+ // doesn't inherit from AudioEffect and doesn't implement this method
+ // by itself.
+ class HammerReleaseTest extends Thread {
+ private static final int NUM_EFFECTS = 10;
+ private static final int NUM_ITERATIONS = 100;
+ private final int mSession;
+ private final Runnable mOnComplete;
+
+ HammerReleaseTest(int session, Runnable onComplete) {
+ mSession = session;
+ mOnComplete = onComplete;
+ }
+
+ @Override
+ public void run() {
+ Log.w(TAG, "HammerReleaseTest started");
+ Visualizer[] effects = new Visualizer[NUM_EFFECTS];
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ for (int j = 0; j < NUM_EFFECTS; j++) {
+ effects[j] = new Visualizer(mSession);
+ yield();
+ }
+ for (int j = NUM_EFFECTS - 1; j >= 0; j--) {
+ Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]);
+ effects[j].release();
+ effects[j] = null;
+ yield();
+ }
+ }
+ Log.w(TAG, "HammerReleaseTest ended");
+ runOnUiThread(mOnComplete);
+ }
+ }
+
+ private void runHammerReleaseTest(Button controlButton) {
+ controlButton.setEnabled(false);
+ HammerReleaseTest thread = new HammerReleaseTest(sSession,
+ () -> {
+ controlButton.setEnabled(true);
+ });
+ thread.start();
+ }
+
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 59d8acb..70baf1d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -18,6 +18,7 @@
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
@@ -125,6 +126,9 @@
addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler());
addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler());
+ addHandler(BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE,
+ new SetMemberAvailableHandler());
+
registerAdapterIntentReceiver();
}
@@ -293,6 +297,8 @@
BluetoothDevice device) {
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
+ final boolean isCoordinatedSetMember =
+ intent.getBooleanExtra(BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER, false);
// TODO Pick up UUID. They should be available for 2.1 devices.
// Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
@@ -307,6 +313,7 @@
}
cachedDevice.setRssi(rssi);
cachedDevice.setJustDiscovered(true);
+ cachedDevice.setIsCoordinatedSetMember(isCoordinatedSetMember);
}
}
@@ -335,6 +342,12 @@
}
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
BluetoothDevice.ERROR);
+
+ if (mDeviceManager.onBondStateChangedIfProcess(device, bondState)) {
+ Log.d(TAG, "Should not update UI for the set member");
+ return;
+ }
+
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
if (cachedDevice == null) {
Log.w(TAG, "Got bonding state changed for " + device +
@@ -348,8 +361,10 @@
cachedDevice.onBondingStateChanged(bondState);
if (bondState == BluetoothDevice.BOND_NONE) {
- /* Check if we need to remove other Hearing Aid devices */
- if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
+ // Check if we need to remove other Coordinated set member devices / Hearing Aid
+ // devices
+ if (cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID
+ || cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
mDeviceManager.onDeviceUnpaired(cachedDevice);
}
int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
@@ -496,4 +511,29 @@
dispatchAudioModeChanged();
}
}
+
+ private class SetMemberAvailableHandler implements Handler {
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ final String action = intent.getAction();
+ if (device == null) {
+ Log.e(TAG, "SetMemberAvailableHandler: device is null");
+ return;
+ }
+
+ if (action == null) {
+ Log.e(TAG, "SetMemberAvailableHandler: action is null");
+ return;
+ }
+
+ final int groupId = intent.getIntExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID,
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+ if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+ Log.e(TAG, "SetMemberAvailableHandler: Invalid group id");
+ return;
+ }
+
+ mDeviceManager.onSetMemberAppear(device, groupId);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4c80b91..78fc139 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -18,6 +18,7 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
@@ -41,7 +42,9 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -66,6 +69,7 @@
private final Object mProfileLock = new Object();
BluetoothDevice mDevice;
private long mHiSyncId;
+ private int mGroupId;
// Need this since there is no method for getting RSSI
short mRssi;
// mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
@@ -80,6 +84,8 @@
boolean mJustDiscovered;
+ boolean mIsCoordinatedSetMember = false;
+
private final Collection<Callback> mCallbacks = new CopyOnWriteArrayList<>();
/**
@@ -98,6 +104,8 @@
private boolean mIsA2dpProfileConnectedFail = false;
private boolean mIsHeadsetProfileConnectedFail = false;
private boolean mIsHearingAidProfileConnectedFail = false;
+ // Group member devices for the coordinated set
+ private Set<CachedBluetoothDevice> mMemberDevices = new HashSet<CachedBluetoothDevice>();
// Group second device for Hearing Aid
private CachedBluetoothDevice mSubDevice;
@@ -131,6 +139,7 @@
mDevice = device;
fillData();
mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
+ mGroupId = BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
}
/**
@@ -297,6 +306,42 @@
return mHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
}
+ /**
+ * Mark the discovered device as member of coordinated set.
+ *
+ * @param isCoordinatedSetMember {@code true}, if the device is a member of a coordinated set.
+ */
+ public void setIsCoordinatedSetMember(boolean isCoordinatedSetMember) {
+ mIsCoordinatedSetMember = isCoordinatedSetMember;
+ }
+
+ /**
+ * Check if the device is a CSIP member device.
+ *
+ * @return {@code true}, if this device supports CSIP, otherwise returns {@code false}.
+ */
+ public boolean isCoordinatedSetMemberDevice() {
+ return mIsCoordinatedSetMember;
+ }
+
+ /**
+ * Get the coordinated set group id.
+ *
+ * @return the group id.
+ */
+ public int getGroupId() {
+ return mGroupId;
+ }
+
+ /**
+ * Set the coordinated set group id.
+ *
+ * @param id the group id from the CSIP.
+ */
+ public void setGroupId(int id) {
+ mGroupId = id;
+ }
+
void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
@@ -1171,4 +1216,52 @@
mSubDevice.mJustDiscovered = tmpJustDiscovered;
fetchActiveDevices();
}
+
+ /**
+ * @return a set of member devices that are in the same coordinated set with this device.
+ */
+ public Set<CachedBluetoothDevice> getMemberDevice() {
+ return mMemberDevices;
+ }
+
+ /**
+ * Store the member devices that are in the same coordinated set.
+ */
+ public void setMemberDevice(CachedBluetoothDevice memberDevice) {
+ mMemberDevices.add(memberDevice);
+ }
+
+ /**
+ * Remove a device from the member device sets.
+ */
+ public void removeMemberDevice(CachedBluetoothDevice memberDevice) {
+ mMemberDevices.remove(memberDevice);
+ }
+
+ /**
+ * In order to show the preference for the whole group, we always set the main device as the
+ * first connected device in the coordinated set, and then switch the content of the main
+ * device and member devices.
+ *
+ * @param prevMainDevice the previous Main device, it will be added into the member device set.
+ * @param newMainDevie the new Main device, it will be removed from the member device set.
+ */
+ public void switchMemberDeviceContent(CachedBluetoothDevice prevMainDevice,
+ CachedBluetoothDevice newMainDevie) {
+ // Backup from main device
+ final BluetoothDevice tmpDevice = mDevice;
+ final short tmpRssi = mRssi;
+ final boolean tmpJustDiscovered = mJustDiscovered;
+ // Set main device from sub device
+ mDevice = newMainDevie.mDevice;
+ mRssi = newMainDevie.mRssi;
+ mJustDiscovered = newMainDevie.mJustDiscovered;
+ setMemberDevice(prevMainDevice);
+ mMemberDevices.remove(newMainDevie);
+ // Set sub device from backup
+ newMainDevie.mDevice = tmpDevice;
+ newMainDevie.mRssi = tmpRssi;
+ newMainDevie.mJustDiscovered = tmpJustDiscovered;
+ fetchActiveDevices();
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index cca9cfa..0256615 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -18,6 +18,7 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
@@ -26,6 +27,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
/**
* CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -41,11 +43,15 @@
final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
@VisibleForTesting
HearingAidDeviceManager mHearingAidDeviceManager;
+ @VisibleForTesting
+ CsipDeviceManager mCsipDeviceManager;
+ BluetoothDevice mOngoingSetMemberPair;
CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager) {
mContext = context;
mBtManager = localBtManager;
mHearingAidDeviceManager = new HearingAidDeviceManager(localBtManager, mCachedDevices);
+ mCsipDeviceManager = new CsipDeviceManager(localBtManager, mCachedDevices);
}
public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
@@ -79,7 +85,16 @@
if (cachedDevice.getDevice().equals(device)) {
return cachedDevice;
}
- // Check sub devices if it exists
+ // Check the member devices for the coordinated set if it exists
+ final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ if (memberDevices != null) {
+ for (CachedBluetoothDevice memberDevice : memberDevices) {
+ if (memberDevice.getDevice().equals(device)) {
+ return memberDevice;
+ }
+ }
+ }
+ // Check sub devices for hearing aid if it exists
CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
if (subDevice != null && subDevice.getDevice().equals(device)) {
return subDevice;
@@ -102,8 +117,10 @@
newDevice = findDevice(device);
if (newDevice == null) {
newDevice = new CachedBluetoothDevice(mContext, profileManager, device);
+ mCsipDeviceManager.initCsipDeviceIfNeeded(newDevice);
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
- if (!mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
+ if (!mCsipDeviceManager.setMemberDeviceIfNeeded(newDevice)
+ && !mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
mCachedDevices.add(newDevice);
mBtManager.getEventManager().dispatchDeviceAdded(newDevice);
}
@@ -114,13 +131,23 @@
}
/**
- * Returns device summary of the pair of the hearing aid passed as the parameter.
+ * Returns device summary of the pair of the hearing aid / CSIP passed as the parameter.
*
* @param CachedBluetoothDevice device
- * @return Device summary, or if the pair does not exist or if it is not a hearing aid,
- * then {@code null}.
+ * @return Device summary, or if the pair does not exist or if it is not a hearing aid or
+ * a CSIP set member, then {@code null}.
*/
public synchronized String getSubDeviceSummary(CachedBluetoothDevice device) {
+ final Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
+ if (memberDevices != null) {
+ for (CachedBluetoothDevice memberDevice : memberDevices) {
+ if (!memberDevice.isConnected()) {
+ return null;
+ }
+ }
+
+ return device.getConnectionSummary();
+ }
CachedBluetoothDevice subDevice = device.getSubDevice();
if (subDevice != null && subDevice.isConnected()) {
return subDevice.getConnectionSummary();
@@ -132,12 +159,22 @@
* Search for existing sub device {@link CachedBluetoothDevice}.
*
* @param device the address of the Bluetooth device
- * @return true for found sub device or false.
+ * @return true for found sub / member device or false.
*/
public synchronized boolean isSubDevice(BluetoothDevice device) {
for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
if (!cachedDevice.getDevice().equals(device)) {
- // Check sub devices if it exists
+ // Check the member devices of the coordinated set if it exists
+ Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ if (memberDevices != null) {
+ for (CachedBluetoothDevice memberDevice : memberDevices) {
+ if (memberDevice.getDevice().equals(device)) {
+ return true;
+ }
+ }
+ continue;
+ }
+ // Check sub devices of hearing aid if it exists
CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
if (subDevice != null && subDevice.getDevice().equals(device)) {
return true;
@@ -157,6 +194,14 @@
}
/**
+ * Updates the Csip devices; specifically the GroupId's. This routine is called when the
+ * CSIS is connected and the GroupId's are now available.
+ */
+ public synchronized void updateCsipDevices() {
+ mCsipDeviceManager.updateCsipDevices();
+ }
+
+ /**
* Attempts to get the name of a remote device, otherwise returns the address.
*
* @param device The remote device.
@@ -185,6 +230,16 @@
private void clearNonBondedSubDevices() {
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+ final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ if (memberDevices != null) {
+ for (CachedBluetoothDevice memberDevice : memberDevices) {
+ // Member device exists and it is not bonded
+ if (memberDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
+ cachedDevice.removeMemberDevice(memberDevice);
+ }
+ }
+ return;
+ }
CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
if (subDevice != null
&& subDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
@@ -201,6 +256,13 @@
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
cachedDevice.setJustDiscovered(false);
+ final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ if (memberDevices != null) {
+ for (CachedBluetoothDevice memberDevice : memberDevices) {
+ memberDevice.setJustDiscovered(false);
+ }
+ return;
+ }
final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
if (subDevice != null) {
subDevice.setJustDiscovered(false);
@@ -214,10 +276,19 @@
if (bluetoothState == BluetoothAdapter.STATE_TURNING_OFF) {
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
- CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
- if (subDevice != null) {
- if (subDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
- cachedDevice.setSubDevice(null);
+ final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ if (memberDevices != null) {
+ for (CachedBluetoothDevice memberDevice : memberDevices) {
+ if (memberDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
+ cachedDevice.removeMemberDevice(memberDevice);
+ }
+ }
+ } else {
+ CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+ if (subDevice != null) {
+ if (subDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
+ cachedDevice.setSubDevice(null);
+ }
}
}
if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
@@ -229,13 +300,32 @@
}
public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice
- cachedDevice, int state) {
- return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
+ cachedDevice, int state, int profileId) {
+ if (profileId == BluetoothProfile.HEARING_AID) {
+ return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
state);
+ }
+ if (profileId == BluetoothProfile.CSIP_SET_COORDINATOR) {
+ return mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
+ state);
+ }
+ return false;
}
public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
- CachedBluetoothDevice mainDevice = mHearingAidDeviceManager.findMainDevice(device);
+ CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(device);
+ final Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
+ if (memberDevices != null) {
+ // Main device is unpaired, to unpair the member device
+ for (CachedBluetoothDevice memberDevice : memberDevices) {
+ memberDevice.unpair();
+ device.removeMemberDevice(memberDevice);
+ }
+ } else if (mainDevice != null) {
+ // the member device unpaired, to unpair main device
+ mainDevice.unpair();
+ }
+ mainDevice = mHearingAidDeviceManager.findMainDevice(device);
CachedBluetoothDevice subDevice = device.getSubDevice();
if (subDevice != null) {
// Main device is unpaired, to unpair sub device
@@ -248,6 +338,74 @@
}
}
+ /**
+ * Called when we found a set member of a group. The function will check the {@code groupId} if
+ * it exists and if there is a ongoing pair, the device would be ignored.
+ *
+ * @param device The found device
+ * @param groupId The group id of the found device
+ */
+ public synchronized void onSetMemberAppear(BluetoothDevice device, int groupId) {
+ Log.d(TAG, "onSetMemberAppear, groupId: " + groupId + " device: " + device.toString());
+
+ if (mOngoingSetMemberPair != null) {
+ Log.d(TAG, "Ongoing set memberPairing in process, drop it!");
+ return;
+ }
+
+ if (mCsipDeviceManager.onSetMemberAppear(device, groupId)) {
+ mOngoingSetMemberPair = device;
+ }
+ }
+
+ /**
+ * Called when the bond state change. If the bond state change is related with the
+ * ongoing set member pair, the cachedBluetoothDevice will be created but the UI
+ * would not be updated. For the other case, return {@code false} to go through the normal
+ * flow.
+ *
+ * @param device The device
+ * @param bondState The new bond state
+ *
+ * @return {@code true}, if the bond state change for the device is handled inside this
+ * function, and would not like to update the UI. If not, return {@code false}.
+ */
+ public synchronized boolean onBondStateChangedIfProcess(BluetoothDevice device, int bondState) {
+ if (mOngoingSetMemberPair == null || !mOngoingSetMemberPair.equals(device)) {
+ return false;
+ }
+
+ if (bondState == BluetoothDevice.BOND_BONDING) {
+ return true;
+ }
+
+ mOngoingSetMemberPair = null;
+ if (bondState != BluetoothDevice.BOND_NONE) {
+ if (findDevice(device) == null) {
+ final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+ CachedBluetoothDevice newDevice =
+ new CachedBluetoothDevice(mContext, profileManager, device);
+ mCachedDevices.add(newDevice);
+ findDevice(device).connect();
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if the device is the one which is initial paired locally by CSIP. The setting
+ * would depned on it to accept the pairing request automatically
+ *
+ * @param device The device
+ *
+ * @return {@code true}, if the device is ongoing pair by CSIP. Otherwise, return
+ * {@code false}.
+ */
+ public boolean isOngoingPairByCsip(BluetoothDevice device) {
+ return !(mOngoingSetMemberPair == null) && mOngoingSetMemberPair.equals(device);
+ }
+
private void log(String msg) {
if (DEBUG) {
Log.d(TAG, msg);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
new file mode 100644
index 0000000..347e14b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * CsipDeviceManager manages the set of remote CSIP Bluetooth devices.
+ */
+public class CsipDeviceManager {
+ private static final String TAG = "CsipDeviceManager";
+ private static final boolean DEBUG = BluetoothUtils.D;
+
+ private final LocalBluetoothManager mBtManager;
+ private final List<CachedBluetoothDevice> mCachedDevices;
+
+ CsipDeviceManager(LocalBluetoothManager localBtManager,
+ List<CachedBluetoothDevice> cachedDevices) {
+ mBtManager = localBtManager;
+ mCachedDevices = cachedDevices;
+ };
+
+ void initCsipDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+ // Current it only supports the base uuid for CSIP and group this set in UI.
+ final int groupId = getBaseGroupId(newDevice.getDevice());
+ if (isValidGroupId(groupId)) {
+ log("initCsipDeviceIfNeeded: " + newDevice + " (group: " + groupId + ")");
+ // Once groupId is valid, assign groupId
+ newDevice.setGroupId(groupId);
+ }
+ }
+
+ private int getBaseGroupId(BluetoothDevice device) {
+ final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+ final CsipSetCoordinatorProfile profileProxy = profileManager
+ .getCsipSetCoordinatorProfile();
+ if (profileProxy != null) {
+ final Map<Integer, ParcelUuid> groupIdMap = profileProxy
+ .getGroupUuidMapByDevice(device);
+ if (groupIdMap == null) {
+ return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+ }
+
+ for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
+ if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+ return entry.getKey();
+ }
+ }
+ }
+ return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+ }
+
+ boolean setMemberDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+ final int groupId = newDevice.getGroupId();
+ if (isValidGroupId(groupId)) {
+ final CachedBluetoothDevice CsipDevice = getCachedDevice(groupId);
+ log("setMemberDeviceIfNeeded, main: " + CsipDevice + ", member: " + newDevice);
+ // Just add one of the coordinated set from a pair in the list that is shown in the UI.
+ // Once there is other devices with the same groupId, to add new device as member
+ // devices.
+ if (CsipDevice != null) {
+ CsipDevice.setMemberDevice(newDevice);
+ newDevice.setName(CsipDevice.getName());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isValidGroupId(int groupId) {
+ return groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+ }
+
+ private CachedBluetoothDevice getCachedDevice(int groupId) {
+ for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+ CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+ if (cachedDevice.getGroupId() == groupId) {
+ return cachedDevice;
+ }
+ }
+ return null;
+ }
+
+ // To collect all set member devices and call #onGroupIdChanged to group device by GroupId
+ void updateCsipDevices() {
+ final Set<Integer> newGroupIdSet = new HashSet<Integer>();
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ // Do nothing if GroupId has been assigned
+ if (!isValidGroupId(cachedDevice.getGroupId())) {
+ final int newGroupId = getBaseGroupId(cachedDevice.getDevice());
+ // Do nothing if there is no GroupId on Bluetooth device
+ if (isValidGroupId(newGroupId)) {
+ cachedDevice.setGroupId(newGroupId);
+ newGroupIdSet.add(newGroupId);
+ }
+ }
+ }
+ for (int groupId : newGroupIdSet) {
+ onGroupIdChanged(groupId);
+ }
+ }
+
+ // Group devices by groupId
+ @VisibleForTesting
+ void onGroupIdChanged(int groupId) {
+ int firstMatchedIndex = -1;
+ CachedBluetoothDevice mainDevice = null;
+
+ for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+ final CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+ if (cachedDevice.getGroupId() != groupId) {
+ continue;
+ }
+
+ if (firstMatchedIndex == -1) {
+ // Found the first one
+ firstMatchedIndex = i;
+ mainDevice = cachedDevice;
+ continue;
+ }
+
+ log("onGroupIdChanged: removed from UI device =" + cachedDevice
+ + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex);
+
+ mainDevice.setMemberDevice(cachedDevice);
+ mCachedDevices.remove(i);
+ mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
+ break;
+ }
+ }
+
+ // @return {@code true}, the event is processed inside the method. It is for updating
+ // le audio device on group relationship when receiving connected or disconnected.
+ // @return {@code false}, it is not le audio device or to process it same as other profiles
+ boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice cachedDevice,
+ int state) {
+ log("onProfileConnectionStateChangedIfProcessed: " + cachedDevice + ", state: " + state);
+ switch (state) {
+ case BluetoothProfile.STATE_CONNECTED:
+ onGroupIdChanged(cachedDevice.getGroupId());
+ CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice);
+ if (mainDevice != null) {
+ if (mainDevice.isConnected()) {
+ // When main device exists and in connected state, receiving member device
+ // connection. To refresh main device UI
+ mainDevice.refresh();
+ return true;
+ } else {
+ // When both LE Audio devices are disconnected, receiving member device
+ // connection. To switch content and dispatch to notify UI change
+ mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
+ mainDevice.switchMemberDeviceContent(mainDevice, cachedDevice);
+ mainDevice.refresh();
+ // It is necessary to do remove and add for updating the mapping on
+ // preference and device
+ mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
+ return true;
+ }
+ }
+ break;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ mainDevice = findMainDevice(cachedDevice);
+ if (mainDevice != null) {
+ // When main device exists, receiving sub device disconnection
+ // To update main device UI
+ mainDevice.refresh();
+ return true;
+ }
+ final Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
+ if (memberSet == null) {
+ break;
+ }
+
+ for (CachedBluetoothDevice device: memberSet) {
+ if (device.isConnected()) {
+ // Main device is disconnected and sub device is connected
+ // To copy data from sub device to main device
+ mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
+ cachedDevice.switchMemberDeviceContent(device, cachedDevice);
+ cachedDevice.refresh();
+ // It is necessary to do remove and add for updating the mapping on
+ // preference and device
+ mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice);
+ return true;
+ }
+ }
+ break;
+ default:
+ // Do not handle this state.
+ }
+ return false;
+ }
+
+ CachedBluetoothDevice findMainDevice(CachedBluetoothDevice device) {
+ if (device == null || mCachedDevices == null) {
+ return null;
+ }
+
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ if (isValidGroupId(cachedDevice.getGroupId())) {
+ Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
+ if (memberSet == null) {
+ continue;
+ }
+
+ for (CachedBluetoothDevice memberDevice: memberSet) {
+ if (memberDevice != null && memberDevice.equals(device)) {
+ return cachedDevice;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Called when we found a set member of a group. The function will check bond state, and
+ * the {@code groupId} if it exists, and then create the bond.
+ *
+ * @param device The found device
+ * @param groupId The group id of the found device
+ *
+ * @return {@code true}, if the we create bond with the device. Otherwise, return
+ * {@code false}.
+ */
+ public boolean onSetMemberAppear(BluetoothDevice device, int groupId) {
+ if (device.getBondState() != BluetoothDevice.BOND_NONE) {
+ return false;
+ }
+
+ if (getCachedDevice(groupId) != null) {
+ device.createBond(BluetoothDevice.TRANSPORT_LE);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void log(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, msg);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
new file mode 100644
index 0000000..6da249c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CSIP Set Coordinator handles Bluetooth CSIP Set Coordinator role profile.
+ */
+public class CsipSetCoordinatorProfile implements LocalBluetoothProfile {
+ private static final String TAG = "CsipSetCoordinatorProfile";
+ private static final boolean VDBG = true;
+
+ private Context mContext;
+
+ private BluetoothCsipSetCoordinator mService;
+ private boolean mIsProfileReady;
+
+ private final CachedBluetoothDeviceManager mDeviceManager;
+
+ static final String NAME = "CSIP Set Coordinator";
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 1;
+
+ // These callbacks run on the main thread.
+ private final class CoordinatedSetServiceListener implements BluetoothProfile.ServiceListener {
+ @RequiresApi(32)
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (VDBG) {
+ Log.d(TAG, "Bluetooth service connected");
+ }
+ mService = (BluetoothCsipSetCoordinator) proxy;
+ // We just bound to the service, so refresh the UI for any connected CSIP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ if (VDBG) {
+ Log.d(TAG, "CsipSetCoordinatorProfile found new device: " + nextDevice);
+ }
+ device = mDeviceManager.addDevice(nextDevice);
+ }
+ device.onProfileStateChanged(
+ CsipSetCoordinatorProfile.this, BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+
+ mDeviceManager.updateCsipDevices();
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady = true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (VDBG) {
+ Log.d(TAG, "Bluetooth service disconnected");
+ }
+ mProfileManager.callServiceDisconnectedListeners();
+ mIsProfileReady = false;
+ }
+ }
+
+ CsipSetCoordinatorProfile(Context context, CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mContext = context;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+
+ BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
+ new CoordinatedSetServiceListener(), BluetoothProfile.CSIP_SET_COORDINATOR);
+ }
+
+ /**
+ * Get CSIP devices matching connection states{
+ *
+ * @code BluetoothProfile.STATE_CONNECTED,
+ * @code BluetoothProfile.STATE_CONNECTING,
+ * @code BluetoothProfile.STATE_DISCONNECTING}
+ *
+ * @return Matching device list
+ */
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ /**
+ * Gets the connection status of the device.
+ *
+ * @code BluetoothProfile.STATE_CONNECTED,
+ * @code BluetoothProfile.STATE_CONNECTING,
+ * @code BluetoothProfile.STATE_DISCONNECTING}
+ *
+ * @return Connection status, {@code BluetoothProfile.STATE_DISCONNECTED} if unknown.
+ */
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ @Override
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ @Override
+ public int getProfileId() {
+ return BluetoothProfile.CSIP_SET_COORDINATOR;
+ }
+
+ @Override
+ public boolean accessProfileEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(BluetoothDevice device) {
+ if (mService == null || device == null) {
+ return false;
+ }
+ return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
+ }
+
+ @Override
+ public int getConnectionPolicy(BluetoothDevice device) {
+ if (mService == null || device == null) {
+ return CONNECTION_POLICY_FORBIDDEN;
+ }
+ return mService.getConnectionPolicy(device);
+ }
+
+ @Override
+ public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+ boolean isEnabled = false;
+ if (mService == null || device == null) {
+ return false;
+ }
+ if (enabled) {
+ if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+ }
+ } else {
+ isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+ }
+
+ return isEnabled;
+ }
+
+ @Override
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ @Override
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.summary_empty;
+ }
+
+ @Override
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ return BluetoothUtils.getConnectionStateSummary(state);
+ }
+
+ @Override
+ public int getDrawableResource(BluetoothClass btClass) {
+ return 0;
+ }
+
+ /**
+ * Get the device's groups and correspondsing uuids map.
+ * @param device the bluetooth device
+ * @return Map of groups ids and related UUIDs
+ */
+ public Map<Integer, ParcelUuid> getGroupUuidMapByDevice(BluetoothDevice device) {
+ if (mService == null || device == null) {
+ return null;
+ }
+ return mService.getGroupUuidMapByDevice(device);
+ }
+
+ /**
+ * Return the profile name as a string.
+ */
+ public String toString() {
+ return NAME;
+ }
+
+ @RequiresApi(32)
+ protected void finalize() {
+ if (VDBG) {
+ Log.d(TAG, "finalize()");
+ }
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
+ BluetoothProfile.CSIP_SET_COORDINATOR, mService);
+ mService = null;
+ } catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up CSIP Set Coordinator proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index c4cb6a1..24113c5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -19,6 +19,7 @@
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
@@ -100,6 +101,7 @@
private PbapClientProfile mPbapClientProfile;
private PbapServerProfile mPbapProfile;
private HearingAidProfile mHearingAidProfile;
+ private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
private SapProfile mSapProfile;
private VolumeControlProfile mVolumeControlProfile;
@@ -230,7 +232,16 @@
// Note: no event handler for VCP, only for being connectable.
mProfileNameMap.put(VolumeControlProfile.NAME, mVolumeControlProfile);
}
-
+ if (mCsipSetCoordinatorProfile == null
+ && supportedList.contains(BluetoothProfile.CSIP_SET_COORDINATOR)) {
+ if (DEBUG) {
+ Log.d(TAG, "Adding local CSIP set coordinator profile");
+ }
+ mCsipSetCoordinatorProfile =
+ new CsipSetCoordinatorProfile(mContext, mDeviceManager, this);
+ addProfile(mCsipSetCoordinatorProfile, mCsipSetCoordinatorProfile.NAME,
+ BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED);
+ }
mEventManager.registerProfileIntentReceiver();
}
@@ -307,11 +318,35 @@
}
}
}
+
+ if (getCsipSetCoordinatorProfile() != null
+ && mProfile instanceof CsipSetCoordinatorProfile
+ && newState == BluetoothProfile.STATE_CONNECTED) {
+ // Check if the GroupID has being initialized
+ if (cachedDevice.getGroupId() == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+ final Map<Integer, ParcelUuid> groupIdMap = getCsipSetCoordinatorProfile()
+ .getGroupUuidMapByDevice(cachedDevice.getDevice());
+ if (groupIdMap != null) {
+ for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
+ if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+ cachedDevice.setGroupId(entry.getKey());
+ break;
+ }
+ }
+ }
+ }
+ }
+
cachedDevice.onProfileStateChanged(mProfile, newState);
// Dispatch profile changed after device update
- if (!(cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
- && mDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
- newState))) {
+ boolean needDispatchProfileConnectionState = true;
+ if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
+ || cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+ needDispatchProfileConnectionState = !mDeviceManager
+ .onProfileConnectionStateChangedIfProcessed(cachedDevice, newState,
+ mProfile.getProfileId());
+ }
+ if (needDispatchProfileConnectionState) {
cachedDevice.refresh();
mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
mProfile.getProfileId());
@@ -462,6 +497,10 @@
return mHidDeviceProfile;
}
+ public CsipSetCoordinatorProfile getCsipSetCoordinatorProfile() {
+ return mCsipSetCoordinatorProfile;
+ }
+
/**
* Fill in a list of LocalBluetoothProfile objects that are supported by
* the local device and the remote device.
@@ -582,6 +621,12 @@
removedProfiles.remove(mVolumeControlProfile);
}
+ if (mCsipSetCoordinatorProfile != null
+ && ArrayUtils.contains(uuids, BluetoothUuid.COORDINATED_SET)) {
+ profiles.add(mCsipSetCoordinatorProfile);
+ removedProfiles.remove(mCsipSetCoordinatorProfile);
+ }
+
if (DEBUG) {
Log.d(TAG,"New Profiles" + profiles.toString());
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index fd5b053..4f8fa2f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -206,7 +206,7 @@
mContext.sendBroadcast(mIntent);
verify(mDeviceManager).onProfileConnectionStateChangedIfProcessed(mCachedBluetoothDevice,
- BluetoothProfile.STATE_CONNECTED);
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
}
/**
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 10600e3..0e9a51d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -513,7 +513,7 @@
<provider android:name=".HeapDumpProvider"
android:authorities="com.android.shell.heapdump"
android:grantUriPermissions="true"
- android:exported="true" />
+ android:exported="false" />
<activity
android:name=".BugreportWarningActivity"
diff --git a/services/OWNERS b/services/OWNERS
index b7128a3..a083319 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -4,3 +4,4 @@
per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com
per-file java/com/android/server/* = toddke@google.com,patb@google.com
+per-file tests/servicestests/src/com/android/server/systemconfig/* = patb@google.com
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 00cc753..bdeb4d5 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -175,8 +175,6 @@
private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
private boolean mBinding;
private boolean mUnbinding;
- private int mWaitForEnableRetry;
- private int mWaitForDisableRetry;
private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
@@ -933,14 +931,15 @@
if (mState == BluetoothAdapter.STATE_ON
|| mState == BluetoothAdapter.STATE_BLE_ON
|| mState == BluetoothAdapter.STATE_TURNING_ON
- || mState == BluetoothAdapter.STATE_TURNING_OFF) {
- Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+ || mState == BluetoothAdapter.STATE_TURNING_OFF
+ || mState == BluetoothAdapter.STATE_BLE_TURNING_ON) {
+ Log.d(TAG, "enableBLE(): Bluetooth is already enabled or is turning on");
return true;
}
synchronized (mReceiver) {
// waive WRITE_SECURE_SETTINGS permission check
- sendEnableMsg(false,
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST, packageName);
+ sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
+ packageName, true);
}
return true;
}
@@ -1734,6 +1733,8 @@
private class BluetoothHandler extends Handler {
boolean mGetNameAddressOnly = false;
+ private int mWaitForEnableRetry;
+ private int mWaitForDisableRetry;
BluetoothHandler(Looper looper) {
super(looper);
@@ -1781,11 +1782,12 @@
case MESSAGE_ENABLE:
int quietEnable = msg.arg1;
+ int isBle = msg.arg2;
if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED)
|| mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
// We are handling enable or disable right now, wait for it.
mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE,
- quietEnable, 0), ENABLE_DISABLE_DELAY_MS);
+ quietEnable, isBle), ENABLE_DISABLE_DELAY_MS);
break;
}
@@ -1800,13 +1802,28 @@
try {
mBluetoothLock.readLock().lock();
if (mBluetooth != null) {
+ boolean isHandled = true;
int state = mBluetooth.getState();
- if (state == BluetoothAdapter.STATE_BLE_ON) {
- Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
- mBluetooth.onLeServiceUp();
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
- break;
+ switch (state) {
+ case BluetoothAdapter.STATE_BLE_ON:
+ if (isBle == 1) {
+ Slog.i(TAG, "Already at BLE_ON State");
+ } else {
+ Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
+ mBluetooth.onLeServiceUp();
+ persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+ }
+ break;
+ case BluetoothAdapter.STATE_BLE_TURNING_ON:
+ case BluetoothAdapter.STATE_TURNING_ON:
+ case BluetoothAdapter.STATE_ON:
+ Slog.i(TAG, "MESSAGE_ENABLE: already enabled");
+ break;
+ default:
+ isHandled = false;
+ break;
}
+ if (isHandled) break;
}
} catch (RemoteException e) {
Slog.e(TAG, "", e);
@@ -2559,7 +2576,12 @@
}
private void sendEnableMsg(boolean quietMode, int reason, String packageName) {
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));
+ sendEnableMsg(quietMode, reason, packageName, false);
+ }
+
+ private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0,
+ isBle ? 1 : 0));
addActiveLog(reason, packageName, true);
mLastEnabledTime = SystemClock.elapsedRealtime();
}
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index a9f3a1b..462ed5c 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -82,6 +82,8 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b1ffaeb..6d85273 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -734,11 +734,8 @@
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
- if (!r.mAllowWhileInUsePermissionInFgs) {
- r.mAllowWhileInUsePermissionInFgs =
- shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
- callingUid, service, r, allowBackgroundActivityStarts);
- }
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, r,
+ allowBackgroundActivityStarts);
return cmp;
}
@@ -1411,14 +1408,6 @@
+ String.format("0x%08X", manifestType)
+ " in service element of manifest file");
}
- // If the foreground service is not started from TOP process, do not allow it to
- // have while-in-use location/camera/microphone access.
- if (!r.mAllowWhileInUsePermissionInFgs) {
- Slog.w(TAG,
- "Foreground service started from background can not have "
- + "location/camera/microphone access: service "
- + r.shortInstanceName);
- }
}
boolean alreadyStartedOp = false;
boolean stopProcStatsOp = false;
@@ -1466,6 +1455,57 @@
ignoreForeground = true;
}
+ if (!ignoreForeground) {
+ if (r.mStartForegroundCount == 0) {
+ /*
+ If the service was started with startService(), not
+ startForegroundService(), and if startForeground() isn't called within
+ mFgsStartForegroundTimeoutMs, then we check the state of the app
+ (who owns the service, which is the app that called startForeground())
+ again. If the app is in the foreground, or in any other cases where
+ FGS-starts are allowed, then we still allow the FGS to be started.
+ Otherwise, startForeground() would fail.
+
+ If the service was started with startForegroundService(), then the service
+ must call startForeground() within a timeout anyway, so we don't need this
+ check.
+ */
+ if (!r.fgRequired) {
+ final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime;
+ if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
+ resetFgsRestrictionLocked(r);
+ setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid,
+ r.appInfo.uid, r, false);
+ EventLog.writeEvent(0x534e4554, "183147114",
+ r.appInfo.uid,
+ "call setFgsRestrictionLocked again due to "
+ + "startForegroundTimeout");
+ }
+ }
+ } else if (r.mStartForegroundCount >= 1) {
+ // The second or later time startForeground() is called after service is
+ // started. Check for app state again.
+ final long delayMs = SystemClock.elapsedRealtime() -
+ r.mLastSetFgsRestrictionTime;
+ if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
+ resetFgsRestrictionLocked(r);
+ setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid,
+ r.appInfo.uid, r, false);
+ EventLog.writeEvent(0x534e4554, "183147114", r.appInfo.uid,
+ "call setFgsRestrictionLocked for "
+ + (r.mStartForegroundCount + 1) + "th startForeground");
+ }
+ }
+ // If the foreground service is not started from TOP process, do not allow it to
+ // have while-in-use location/camera/microphone access.
+ if (!r.mAllowWhileInUsePermissionInFgs) {
+ Slog.w(TAG,
+ "Foreground service started from background can not have "
+ + "location/camera/microphone access: service "
+ + r.shortInstanceName);
+ }
+ }
+
// Apps under strict background restrictions simply don't get to have foreground
// services, so now that we've enforced the startForegroundService() contract
// we only do the machinery of making the service foreground when the app
@@ -1501,6 +1541,7 @@
active.mNumActive++;
}
r.isForeground = true;
+ r.mStartForegroundCount++;
if (!stopProcStatsOp) {
ServiceState stracker = r.getTracker();
if (stracker != null) {
@@ -1559,6 +1600,7 @@
decActiveForegroundAppLocked(smap, r);
}
r.isForeground = false;
+ resetFgsRestrictionLocked(r);
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -2118,12 +2160,7 @@
}
}
- if (!s.mAllowWhileInUsePermissionInFgs) {
- s.mAllowWhileInUsePermissionInFgs =
- shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
- callingPid, callingUid,
- service, s, false);
- }
+ setFgsRestrictionLocked(callingPackage, callingPid, callingUid, s, false);
if (s.app != null) {
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
@@ -3419,7 +3456,7 @@
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
- r.mAllowWhileInUsePermissionInFgs = false;
+ resetFgsRestrictionLocked(r);
// Clear start entries.
r.clearDeliveredStartsLocked();
@@ -4900,7 +4937,7 @@
* @return true if allow, false otherwise.
*/
private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
- int callingPid, int callingUid, Intent intent, ServiceRecord r,
+ int callingPid, int callingUid, ServiceRecord r,
boolean allowBackgroundActivityStarts) {
// Is the background FGS start restriction turned on?
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
@@ -4986,6 +5023,28 @@
boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
String callingPackage) {
return shouldAllowWhileInUsePermissionInFgsLocked(
- callingPackage, callingPid, callingUid, null, null, false);
+ callingPackage, callingPid, callingUid, null, false);
+ }
+
+ /**
+ * In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground
+ * service or not. while-in-use permissions in FGS started from background might be restricted.
+ * @param callingPackage caller app's package name.
+ * @param callingUid caller app's uid.
+ * @param r the service to start.
+ * @return true if allow, false otherwise.
+ */
+ private void setFgsRestrictionLocked(String callingPackage,
+ int callingPid, int callingUid, ServiceRecord r,
+ boolean allowBackgroundActivityStarts) {
+ r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime();
+ if (!r.mAllowWhileInUsePermissionInFgs) {
+ r.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked(
+ callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts);
+ }
+ }
+
+ private void resetFgsRestrictionLocked(ServiceRecord r) {
+ r.mAllowWhileInUsePermissionInFgs = false;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 7be843f..00d8208e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -88,6 +88,7 @@
static final String KEY_PROCESS_START_ASYNC = "process_start_async";
static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
+ static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout";
static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
@@ -121,6 +122,7 @@
private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
+ private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000;
private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000;
// Flag stored in the DeviceConfig API.
@@ -273,6 +275,12 @@
// this long.
public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION;
+ /**
+ * When service started from background, before the timeout it can be promoted to FGS by calling
+ * Service.startForeground().
+ */
+ volatile long mFgsStartForegroundTimeoutMs = DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS;
+
// Indicates whether the activity starts logging is enabled.
// Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -421,6 +429,9 @@
case KEY_MIN_ASSOC_LOG_DURATION:
updateMinAssocLogDuration();
break;
+ case KEY_FGS_START_FOREGROUND_TIMEOUT:
+ updateFgsStartForegroundTimeout();
+ break;
default:
break;
}
@@ -697,6 +708,13 @@
/* defaultValue */ DEFAULT_MIN_ASSOC_LOG_DURATION);
}
+ private void updateFgsStartForegroundTimeout() {
+ mFgsStartForegroundTimeoutMs = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_FGS_START_FOREGROUND_TIMEOUT,
+ DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS);
+ }
+
void dump(PrintWriter pw) {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
+ Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":");
@@ -769,6 +787,8 @@
pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.toArray()));
pw.print(" "); pw.print(KEY_MIN_ASSOC_LOG_DURATION); pw.print("=");
pw.println(MIN_ASSOC_LOG_DURATION);
+ pw.print(" "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("=");
+ pw.println(mFgsStartForegroundTimeoutMs);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index af89907..50f3520 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -94,7 +94,6 @@
import com.android.server.compat.PlatformCompat;
import java.io.BufferedReader;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -787,8 +786,7 @@
return -1;
}
- File file = new File(filename);
- file.delete();
+ // Writes an error message to stderr on failure
ParcelFileDescriptor fd = openFileForSystem(filename, "w");
if (fd == null) {
return -1;
@@ -942,16 +940,16 @@
String logNameTimeString = LOG_NAME_TIME_FORMATTER.format(localDateTime);
heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof";
}
- pw.println("File: " + heapFile);
- pw.flush();
- File file = new File(heapFile);
- file.delete();
+ // Writes an error message to stderr on failure
ParcelFileDescriptor fd = openFileForSystem(heapFile, "w");
if (fd == null) {
return -1;
}
+ pw.println("File: " + heapFile);
+ pw.flush();
+
final CountDownLatch latch = new CountDownLatch(1);
final RemoteCallback finishCallback = new RemoteCallback(new OnResultListener() {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 39f79ca..ef47b1e 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -22,6 +22,7 @@
import android.net.wifi.WifiManager;
import android.os.BatteryStats;
import android.os.Bundle;
+import android.os.OutcomeReceiver;
import android.os.Parcelable;
import android.os.Process;
import android.os.ServiceManager;
@@ -40,6 +41,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
+import java.util.concurrent.ExecutionException;
import libcore.util.EmptyArray;
import java.util.concurrent.CompletableFuture;
@@ -405,7 +407,7 @@
// We will request data from external processes asynchronously, and wait on a timeout.
SynchronousResultReceiver wifiReceiver = null;
SynchronousResultReceiver bluetoothReceiver = null;
- SynchronousResultReceiver modemReceiver = null;
+ CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null);
boolean railUpdated = false;
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
@@ -460,8 +462,22 @@
}
if (mTelephony != null) {
- modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
+ CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
+ mTelephony.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<ModemActivityInfo,
+ TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ temp.complete(result);
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ temp.complete(null);
+ }
+ });
+ modemFuture = temp;
}
if (!railUpdated) {
synchronized (mStats) {
@@ -472,7 +488,17 @@
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ ModemActivityInfo modemInfo = null;
+ try {
+ modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException e) {
+ Slog.w(TAG, "timeout or interrupt reading modem stats: " + e);
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "exception reading modem stats: " + e.getCause());
+ }
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
synchronized (mStats) {
mStats.addHistoryEventLocked(
@@ -519,11 +545,7 @@
}
if (modemInfo != null) {
- if (modemInfo.isValid()) {
- mStats.updateMobileRadioState(modemInfo);
- } else {
- Slog.w(TAG, "modem info is invalid: " + modemInfo);
- }
+ mStats.updateMobileRadioState(modemInfo);
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 580ceca..34ba3e0 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1208,7 +1208,7 @@
public void noteModemControllerActivity(ModemActivityInfo info) {
enforceCallingPermission();
- if (info == null || !info.isValid()) {
+ if (info == null) {
Slog.e(TAG, "invalid modem data given: " + info);
return;
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 1b65dba..0e62828 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -142,6 +142,10 @@
// allow while-in-use permissions in foreground service or not.
// while-in-use permissions in FGS started from background might be restricted.
boolean mAllowWhileInUsePermissionInFgs;
+ // The number of times Service.startForeground() is called;
+ int mStartForegroundCount;
+ // Last time mAllowWhileInUsePermissionInFgs is set.
+ long mLastSetFgsRestrictionTime;
// the most recent package that start/bind this service.
String mRecentCallingPackage;
@@ -406,6 +410,8 @@
}
pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
pw.println(mAllowWhileInUsePermissionInFgs);
+ pw.print(prefix); pw.print("startForegroundCount=");
+ pw.println(mStartForegroundCount);
pw.print(prefix); pw.print("recentCallingPackage=");
pw.println(mRecentCallingPackage);
if (delayed) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index c24973d..8926e20 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 @@
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 @@
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 cca8be8..b049d01 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 @@
// 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();
+ }
+ }
}
}
@@ -1122,7 +1128,7 @@
@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 +1159,7 @@
@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 +3359,8 @@
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/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5babfeb..f83d059 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -123,6 +123,7 @@
import android.app.ITransientNotification;
import android.app.ITransientNotificationCallback;
import android.app.IUriGrantsManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -479,6 +480,8 @@
final ArrayMap<NotificationRecord, ArrayList<CancelNotificationRunnable>> mDelayedCancelations =
new ArrayMap<>();
+ private KeyguardManager mKeyguardManager;
+
// The last key in this list owns the hardware.
ArrayList<String> mLights = new ArrayList<>();
@@ -1726,6 +1729,11 @@
}
@VisibleForTesting
+ void setKeyguardManager(KeyguardManager keyguardManager) {
+ mKeyguardManager = keyguardManager;
+ }
+
+ @VisibleForTesting
ShortcutHelper getShortcutHelper() {
return mShortcutHelper;
}
@@ -2330,6 +2338,7 @@
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ mKeyguardManager = getContext().getSystemService(KeyguardManager.class);
mZenModeHelper.onSystemReady();
mRoleObserver = new RoleObserver(getContext().getSystemService(RoleManager.class),
mPackageManager, getContext().getMainExecutor());
@@ -6902,7 +6911,6 @@
boolean beep = false;
boolean blink = false;
- final Notification notification = record.getSbn().getNotification();
final String key = record.getKey();
// Should this notification make noise, vibe, or use the LED?
@@ -6924,7 +6932,7 @@
if (!record.isUpdate
&& record.getImportance() > IMPORTANCE_MIN
&& !suppressedByDnd) {
- sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+ sendAccessibilityEvent(record);
sentAccessibilityEvent = true;
}
@@ -6946,7 +6954,7 @@
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (!sentAccessibilityEvent) {
- sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+ sendAccessibilityEvent(record);
sentAccessibilityEvent = true;
}
if (DBG) Slog.v(TAG, "Interrupting!");
@@ -7663,17 +7671,30 @@
return (x < low) ? low : ((x > high) ? high : x);
}
- void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
+ void sendAccessibilityEvent(NotificationRecord record) {
if (!mAccessibilityManager.isEnabled()) {
return;
}
- AccessibilityEvent event =
+ final Notification notification = record.getNotification();
+ final CharSequence packageName = record.getSbn().getPackageName();
+ final AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setPackageName(packageName);
event.setClassName(Notification.class.getName());
- event.setParcelableData(notification);
- CharSequence tickerText = notification.tickerText;
+ final int visibilityOverride = record.getPackageVisibilityOverride();
+ final int notifVisibility = visibilityOverride == NotificationManager.VISIBILITY_NO_OVERRIDE
+ ? notification.visibility : visibilityOverride;
+ final int userId = record.getUser().getIdentifier();
+ final boolean needPublic = userId >= 0 && mKeyguardManager.isDeviceLocked(userId);
+ if (needPublic && notifVisibility != Notification.VISIBILITY_PUBLIC) {
+ // Emit the public version if we're on the lockscreen and this notification isn't
+ // publicly visible.
+ event.setParcelableData(notification.publicVersion);
+ } else {
+ event.setParcelableData(notification);
+ }
+ final CharSequence tickerText = notification.tickerText;
if (!TextUtils.isEmpty(tickerText)) {
event.getText().add(tickerText);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1173df6..4767823 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -14289,9 +14289,15 @@
return new ParceledListSlice<IntentFilter>(result) {
@Override
protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
- // IntentFilter has final Parcelable methods, so redirect to the subclass
- ((ParsedIntentInfo) parcelable).writeIntentInfoToParcel(dest,
- callFlags);
+ parcelable.writeToParcel(dest, callFlags);
+ }
+
+ @Override
+ protected void writeParcelableCreator(IntentFilter parcelable, Parcel dest) {
+ // All Parcel#writeParcelableCreator does is serialize the class name to
+ // access via reflection to grab its CREATOR. This does that manually, pointing
+ // to the parent IntentFilter so that all of the subclass fields are ignored.
+ dest.writeString(IntentFilter.class.getName());
}
};
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 836e615..ae940d0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -608,6 +608,8 @@
private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
+ private boolean mLockNowPending = false;
+
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5;
@@ -4941,6 +4943,7 @@
mKeyguardDelegate.doKeyguardTimeout(options);
}
mLockScreenTimerActive = false;
+ mLockNowPending = false;
options = null;
}
}
@@ -4950,7 +4953,7 @@
}
}
- ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout();
+ final ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout();
@Override
public void lockNow(Bundle options) {
@@ -4962,6 +4965,9 @@
mScreenLockTimeout.setLockOptions(options);
}
mHandler.post(mScreenLockTimeout);
+ synchronized (mScreenLockTimeout) {
+ mLockNowPending = true;
+ }
}
// TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display.
@@ -4977,6 +4983,10 @@
private void updateLockScreenTimeout() {
synchronized (mScreenLockTimeout) {
+ if (mLockNowPending) {
+ Log.w(TAG, "lockNow pending, ignore updating lockscreen timeout");
+ return;
+ }
final boolean enable = !mAllowLockscreenWhenOnDisplays.isEmpty()
&& mDefaultDisplayPolicy.isAwake()
&& mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId);
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 392792d..b75bce8 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -662,6 +662,12 @@
@Override
public String getDefaultSmsPackage(int userId) {
+ userId = handleIncomingUser(userId, false, "getDefaultSmsPackage");
+ if (!mUserManagerInternal.exists(userId)) {
+ Slog.e(LOG_TAG, "user " + userId + " does not exist");
+ return null;
+ }
+
long identity = Binder.clearCallingIdentity();
try {
return CollectionUtils.firstOrNull(
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 0390015..71b3e61 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -102,6 +102,7 @@
import android.os.IStoraged;
import android.os.IThermalEventListener;
import android.os.IThermalService;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -172,6 +173,7 @@
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
+import java.util.concurrent.ExecutionException;
import libcore.io.IoUtils;
import org.json.JSONArray;
@@ -1731,22 +1733,47 @@
int pullModemActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
long token = Binder.clearCallingIdentity();
try {
- SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ CompletableFuture<ModemActivityInfo> modemFuture = new CompletableFuture<>();
+ mTelephony.requestModemActivityInfo(Runnable::run,
+ new OutcomeReceiver<ModemActivityInfo,
+ TelephonyManager.ModemActivityInfoException>() {
+ @Override
+ public void onResult(ModemActivityInfo result) {
+ modemFuture.complete(result);
+ }
+
+ @Override
+ public void onError(TelephonyManager.ModemActivityInfoException e) {
+ Slog.w(TAG, "error reading modem stats:" + e);
+ modemFuture.complete(null);
+ }
+ });
+
+ ModemActivityInfo modemInfo;
+ try {
+ modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS);
+ } catch (TimeoutException | InterruptedException e) {
+ Slog.w(TAG, "timeout or interrupt reading modem stats: " + e);
+ return StatsManager.PULL_SKIP;
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "exception reading modem stats: " + e.getCause());
+ return StatsManager.PULL_SKIP;
+ }
+
if (modemInfo == null) {
return StatsManager.PULL_SKIP;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
- .writeLong(modemInfo.getTimestamp())
+ .writeLong(modemInfo.getTimestampMillis())
.writeLong(modemInfo.getSleepTimeMillis())
.writeLong(modemInfo.getIdleTimeMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis())
- .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis())
+ .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(0))
+ .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(1))
+ .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(2))
+ .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(3))
+ .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(4))
.writeLong(modemInfo.getReceiveTimeMillis())
.build();
pulledData.add(e);
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 277163a6..630317a 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1265,12 +1265,14 @@
if (inputId != null) {
if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
if (previousCableConnectionStatus != connection.getInputStateLocked()) {
- mListener.onStateChanged(inputId, connection.getInputStateLocked());
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ connection.getInputStateLocked(), 0, inputId).sendToTarget();
}
} else {
if ((previousConfigsLength == 0)
!= (connection.getConfigsLengthLocked() == 0)) {
- mListener.onStateChanged(inputId, connection.getInputStateLocked());
+ mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+ connection.getInputStateLocked(), 0, inputId).sendToTarget();
}
}
}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 382398a..e0cc8e1 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -352,7 +352,7 @@
}
private void handleSafeModeStatusChanged() {
- logDbg("VcnGatewayConnection safe mode status changed");
+ logVdbg("VcnGatewayConnection safe mode status changed");
boolean hasSafeModeGatewayConnection = false;
// If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode
@@ -368,7 +368,7 @@
hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE;
if (oldStatus != mCurrentStatus) {
mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection);
- logDbg(
+ logInfo(
"Safe mode "
+ (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE ? "entered" : "exited"));
}
@@ -539,6 +539,16 @@
Slog.d(TAG, getLogPrefix() + msg, tr);
}
+ private void logInfo(String msg) {
+ Slog.i(TAG, getLogPrefix() + msg);
+ LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg);
+ }
+
+ private void logInfo(String msg, Throwable tr) {
+ Slog.i(TAG, getLogPrefix() + msg, tr);
+ LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg + tr);
+ }
+
private void logErr(String msg) {
Slog.e(TAG, getLogPrefix() + msg);
LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg);
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 450257f..7dec4e7 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1677,10 +1677,8 @@
mFailedAttempts = 0;
cancelSafeModeAlarm();
- if (mIsInSafeMode) {
- mIsInSafeMode = false;
- mGatewayStatusCallback.onSafeModeStatusChanged();
- }
+ mIsInSafeMode = false;
+ mGatewayStatusCallback.onSafeModeStatusChanged();
}
protected void applyTransform(
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index a2768c6..3a9b2dc 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -21,6 +21,7 @@
import android.Manifest;
import android.content.Context;
import android.os.ISystemConfig;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -84,6 +85,21 @@
}
return ArrayUtils.convertToIntArray(uids);
}
+
+ @Override
+ public List<String> getEnabledComponentOverrides(String packageName) {
+ ArrayMap<String, Boolean> systemComponents = SystemConfig.getInstance()
+ .getComponentsEnabledStates(packageName);
+ List<String> enabledComponent = new ArrayList<>();
+ if (systemComponents != null) {
+ for (Map.Entry<String, Boolean> entry : systemComponents.entrySet()) {
+ if (Boolean.TRUE.equals(entry.getValue())) {
+ enabledComponent.add(entry.getKey());
+ }
+ }
+ }
+ return enabledComponent;
+ }
};
public SystemConfigService(Context context) {
diff --git a/services/net/java/android/net/ConnectivityModuleConnector.java b/services/net/java/android/net/ConnectivityModuleConnector.java
index 62f2c35..c6b15c1 100644
--- a/services/net/java/android/net/ConnectivityModuleConnector.java
+++ b/services/net/java/android/net/ConnectivityModuleConnector.java
@@ -278,7 +278,10 @@
// This code path is only run by the system server: only the system server binds
// to the NetworkStack as a service. Other processes get the NetworkStack from
// the ServiceManager.
- maybeCrashWithTerribleFailure("Lost network stack", mPackageName);
+ maybeCrashWithTerribleFailure(
+ "Lost network stack. This is not the root cause of any issue, it is a side "
+ + "effect of a crash that happened earlier. Earlier logs should point to the "
+ + "actual issue.", mPackageName);
}
}
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 3e5cbea..c45d084 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 @@
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 ae7f422..ae99dab 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 @@
}
private IntegerSubject assertMessageValidity(String message) {
- return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
+ return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message), false));
}
/**
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ad15a99..11ea4a4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -46,6 +46,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
@@ -103,6 +104,8 @@
NotificationUsageStats mUsageStats;
@Mock
IAccessibilityManager mAccessibilityService;
+ @Mock
+ KeyguardManager mKeyguardManager;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -147,6 +150,7 @@
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
long serviceReturnValue = IntPair.of(
AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
@@ -168,6 +172,7 @@
mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
mService.setUsageStats(mUsageStats);
mService.setAccessibilityManager(accessibilityManager);
+ mService.setKeyguardManager(mKeyguardManager);
mService.mScreenOn = false;
mService.mInCallStateOffHook = false;
mService.mNotificationPulseEnabled = true;
@@ -484,6 +489,94 @@
}
@Test
+ public void testLockedPrivateA11yRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+ }
+
+ @Test
+ public void testLockedOverridePrivateA11yRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE);
+ r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+ }
+
+ @Test
+ public void testLockedPublicA11yNoRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification(), event.getParcelableData());
+ }
+
+ @Test
+ public void testUnlockedPrivateA11yNoRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification(), event.getParcelableData());
+ }
+
+ @Test
public void testBeepInsistently() throws Exception {
NotificationRecord r = getInsistentBeepyNotification();
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 5e3d26a..705b491 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -81,7 +81,8 @@
* <pre>
* {@code
* <service android:name="your.package.YourInCallServiceImplementation"
- * android:permission="android.permission.BIND_INCALL_SERVICE">
+ * android:permission="android.permission.BIND_INCALL_SERVICE"
+ * android:exported="true">
* <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
* <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
* android:value="true" />
@@ -91,6 +92,10 @@
* </service>
* }
* </pre>
+ *
+ * <em>Note: You should NOT mark your {@link InCallService} with the attribute
+ * {@code android:exported="false"}; doing so can result in a failure to bind to your implementation
+ * during calls.</em>
* <p>
* In addition to implementing the {@link InCallService} API, you must also declare an activity in
* your manifest which handles the {@link Intent#ACTION_DIAL} intent. The example below illustrates
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index debb119..0bf8ce6 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -16,8 +16,12 @@
package android.telephony;
+import android.annotation.DurationMillisLong;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -25,46 +29,50 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
+import java.util.Objects;
/**
- * Reports modem activity information.
+ * Contains information about the modem's activity. May be useful for power stats reporting.
* @hide
*/
+@SystemApi
+@TestApi
public final class ModemActivityInfo implements Parcelable {
+ private static final int TX_POWER_LEVELS = 5;
+
/**
- * Tx(transmit) power level. see power index below
- * <ul>
- * <li> index 0 = tx_power < 0dBm. </li>
- * <li> index 1 = 0dBm < tx_power < 5dBm. </li>
- * <li> index 2 = 5dBm < tx_power < 15dBm. </li>
- * <li> index 3 = 15dBm < tx_power < 20dBm. </li>
- * <li> index 4 = tx_power > 20dBm. </li>
- * </ul>
- */
- public static final int TX_POWER_LEVELS = 5;
- /**
- * Tx(transmit) power level 0: tx_power < 0dBm
+ * Corresponds to transmit power of less than 0dBm.
*/
public static final int TX_POWER_LEVEL_0 = 0;
+
/**
- * Tx(transmit) power level 1: 0dBm < tx_power < 5dBm
+ * Corresponds to transmit power between 0dBm and 5dBm.
*/
public static final int TX_POWER_LEVEL_1 = 1;
+
/**
- * Tx(transmit) power level 2: 5dBm < tx_power < 15dBm
+ * Corresponds to transmit power between 5dBm and 15dBm.
*/
public static final int TX_POWER_LEVEL_2 = 2;
+
/**
- * Tx(transmit) power level 3: 15dBm < tx_power < 20dBm.
+ * Corresponds to transmit power between 15dBm and 20dBm.
*/
public static final int TX_POWER_LEVEL_3 = 3;
+
/**
- * Tx(transmit) power level 4: tx_power > 20dBm
+ * Corresponds to transmit power above 20dBm.
*/
public static final int TX_POWER_LEVEL_4 = 4;
+ /**
+ * The number of transmit power levels. Fixed by HAL definition.
+ */
+ public static int getNumTxPowerLevels() {
+ return TX_POWER_LEVELS;
+ }
+
/** @hide */
@IntDef(prefix = {"TX_POWER_LEVEL_"}, value = {
TX_POWER_LEVEL_0,
@@ -82,34 +90,39 @@
new Range<>(5, 15),
new Range<>(15, 20),
new Range<>(20, Integer.MAX_VALUE)
-
};
private long mTimestamp;
private int mSleepTimeMs;
private int mIdleTimeMs;
- private List<TransmitPower> mTransmitPowerInfo = new ArrayList<>(TX_POWER_LEVELS);
+ private int[] mTxTimeMs;
private int mRxTimeMs;
+ /**
+ * @hide
+ */
+ @TestApi
public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
@NonNull int[] txTimeMs, int rxTimeMs) {
+ Objects.requireNonNull(txTimeMs);
+ if (txTimeMs.length != TX_POWER_LEVELS) {
+ throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS");
+ }
mTimestamp = timestamp;
mSleepTimeMs = sleepTimeMs;
mIdleTimeMs = idleTimeMs;
- populateTransmitPowerRange(txTimeMs);
+ mTxTimeMs = txTimeMs;
mRxTimeMs = rxTimeMs;
}
- /** helper API to populate tx power range for each bucket **/
- private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
- int i = 0;
- for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
- mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
- }
- // Make sure that mTransmitPowerInfo is fully initialized.
- for ( ; i < TX_POWER_LEVELS; i++) {
- mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0));
- }
+ /**
+ * Provided for convenience in manipulation since the API exposes long values but internal
+ * representations are ints.
+ * @hide
+ */
+ public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
+ @NonNull int[] txTimeMs, long rxTimeMs) {
+ this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs);
}
@Override
@@ -118,7 +131,7 @@
+ " mTimestamp=" + mTimestamp
+ " mSleepTimeMs=" + mSleepTimeMs
+ " mIdleTimeMs=" + mIdleTimeMs
- + " mTransmitPowerInfo[]=" + mTransmitPowerInfo.toString()
+ + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
+ " mRxTimeMs=" + mRxTimeMs
+ "}";
}
@@ -129,14 +142,12 @@
public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
new Parcelable.Creator<ModemActivityInfo>() {
- public ModemActivityInfo createFromParcel(Parcel in) {
+ public ModemActivityInfo createFromParcel(@NonNull Parcel in) {
long timestamp = in.readLong();
int sleepTimeMs = in.readInt();
int idleTimeMs = in.readInt();
int[] txTimeMs = new int[TX_POWER_LEVELS];
- for (int i = 0; i < TX_POWER_LEVELS; i++) {
- txTimeMs[i] = in.readInt();
- }
+ in.readIntArray(txTimeMs);
int rxTimeMs = in.readInt();
return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
txTimeMs, rxTimeMs);
@@ -147,21 +158,25 @@
}
};
- public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mTimestamp);
dest.writeInt(mSleepTimeMs);
dest.writeInt(mIdleTimeMs);
- for (int i = 0; i < TX_POWER_LEVELS; i++) {
- dest.writeInt(mTransmitPowerInfo.get(i).getTimeInMillis());
- }
+ dest.writeIntArray(mTxTimeMs);
dest.writeInt(mRxTimeMs);
}
/**
- * @return milliseconds since boot, including mTimeInMillis spent in sleep.
- * @see SystemClock#elapsedRealtime()
+ * Gets the timestamp at which this modem activity info was recorded.
+ *
+ * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this
+ * {@link ModemActivityInfo} was recorded.
*/
- public long getTimestamp() {
+ public @ElapsedRealtimeLong long getTimestampMillis() {
return mTimestamp;
}
@@ -171,35 +186,48 @@
}
/**
- * @return an arrayList of {@link TransmitPower} with each element representing the total time where
- * transmitter is awake time (in ms) for a given power range (in dbm).
+ * Gets the amount of time the modem spent transmitting at a certain power level.
*
- * @see #TX_POWER_LEVELS
+ * @param powerLevel The power level to query.
+ * @return The amount of time, in milliseconds, that the modem spent transmitting at the
+ * given power level.
*/
- @NonNull
- public List<TransmitPower> getTransmitPowerInfo() {
- return mTransmitPowerInfo;
+ public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+ @TxPowerLevel int powerLevel) {
+ return mTxTimeMs[powerLevel];
+ }
+
+ /**
+ * Gets the range of transmit powers corresponding to a certain power level.
+ *
+ * @param powerLevel The power level to query
+ * @return A {@link Range} object representing the range of intensities (in dBm) to which this
+ * power level corresponds.
+ */
+ public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) {
+ return TX_POWER_RANGES[powerLevel];
}
/** @hide */
public void setTransmitTimeMillis(int[] txTimeMs) {
- populateTransmitPowerRange(txTimeMs);
- }
-
- /** @hide */
- @NonNull
- public int[] getTransmitTimeMillis() {
- int[] transmitTimeMillis = new int[TX_POWER_LEVELS];
- for (int i = 0; i < transmitTimeMillis.length; i++) {
- transmitTimeMillis[i] = mTransmitPowerInfo.get(i).getTimeInMillis();
- }
- return transmitTimeMillis;
+ mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
}
/**
- * @return total mTimeInMillis (in ms) when modem is in a low power or sleep state.
+ * @return The raw array of transmit power durations
+ * @hide
*/
- public int getSleepTimeMillis() {
+ @NonNull
+ public int[] getTransmitTimeMillis() {
+ return mTxTimeMs;
+ }
+
+ /**
+ * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state.
+ *
+ * @return Time in milliseconds.
+ */
+ public @DurationMillisLong long getSleepTimeMillis() {
return mSleepTimeMs;
}
@@ -209,10 +237,44 @@
}
/**
- * @return total mTimeInMillis (in ms) when modem is awake but neither the transmitter nor receiver are
- * active.
+ * Provided for convenience, since the API surface needs to return longs but internal
+ * representations are ints.
+ * @hide
*/
- public int getIdleTimeMillis() {
+ public void setSleepTimeMillis(long sleepTimeMillis) {
+ mSleepTimeMs = (int) sleepTimeMillis;
+ }
+
+ /**
+ * Computes the difference between this instance of {@link ModemActivityInfo} and another
+ * instance.
+ *
+ * This method should be used to compute the amount of activity that has happened between two
+ * samples of modem activity taken at separate times. The sample passed in as an argument to
+ * this method should be the one that's taken later in time (and therefore has more activity).
+ * @param other The other instance of {@link ModemActivityInfo} to diff against.
+ * @return An instance of {@link ModemActivityInfo} representing the difference in modem
+ * activity.
+ */
+ public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) {
+ int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+ for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+ txTimeMs[i] = other.mTxTimeMs[i] - mTxTimeMs[i];
+ }
+ return new ModemActivityInfo(other.getTimestampMillis(),
+ other.getSleepTimeMillis() - getSleepTimeMillis(),
+ other.getIdleTimeMillis() - getIdleTimeMillis(),
+ txTimeMs,
+ other.getReceiveTimeMillis() - getReceiveTimeMillis());
+ }
+
+ /**
+ * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting
+ * nor receiving.
+ *
+ * @return Time in milliseconds.
+ */
+ public @DurationMillisLong long getIdleTimeMillis() {
return mIdleTimeMs;
}
@@ -222,9 +284,20 @@
}
/**
- * @return rx(receive) mTimeInMillis in ms.
+ * Provided for convenience, since the API surface needs to return longs but internal
+ * representations are ints.
+ * @hide
*/
- public int getReceiveTimeMillis() {
+ public void setIdleTimeMillis(long idleTimeMillis) {
+ mIdleTimeMs = (int) idleTimeMillis;
+ }
+
+ /**
+ * Gets the amount of time (in milliseconds) when the modem is awake and receiving data.
+ *
+ * @return Time in milliseconds.
+ */
+ public @DurationMillisLong long getReceiveTimeMillis() {
return mRxTimeMs;
}
@@ -234,71 +307,56 @@
}
/**
+ * Provided for convenience, since the API surface needs to return longs but internal
+ * representations are ints.
+ * @hide
+ */
+ public void setReceiveTimeMillis(long receiveTimeMillis) {
+ mRxTimeMs = (int) receiveTimeMillis;
+ }
+
+ /**
* Indicates if the modem has reported valid {@link ModemActivityInfo}.
*
* @return {@code true} if this {@link ModemActivityInfo} record is valid,
* {@code false} otherwise.
+ * @hide
*/
+ @TestApi
public boolean isValid() {
- for (TransmitPower powerInfo : getTransmitPowerInfo()) {
- if(powerInfo.getTimeInMillis() < 0) {
- return false;
- }
- }
+ boolean isTxPowerValid = Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0);
- return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
+ return isTxPowerValid && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
&& (getReceiveTimeMillis() >= 0) && !isEmpty());
}
- private boolean isEmpty() {
- for (TransmitPower txVal : getTransmitPowerInfo()) {
- if(txVal.getTimeInMillis() != 0) {
- return false;
- }
- }
+ /** @hide */
+ @TestApi
+ public boolean isEmpty() {
+ boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0
+ || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
- return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
+ return isTxPowerEmpty && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
&& (getReceiveTimeMillis() == 0));
}
- /**
- * Transmit power Information, including the power range in dbm and the total time (in ms) where
- * the transmitter is active/awake for this power range.
- * e.g, range: 0dbm(lower) ~ 5dbm(upper)
- * time: 5ms
- */
- public class TransmitPower {
- private int mTimeInMillis;
- private Range<Integer> mPowerRangeInDbm;
- /** @hide */
- public TransmitPower(@NonNull Range<Integer> range, int time) {
- this.mTimeInMillis = time;
- this.mPowerRangeInDbm = range;
- }
- /**
- * @return the total time in ms where the transmitter is active/wake for this power range
- * {@link #getPowerRangeInDbm()}.
- */
- public int getTimeInMillis() {
- return mTimeInMillis;
- }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ModemActivityInfo that = (ModemActivityInfo) o;
+ return mTimestamp == that.mTimestamp
+ && mSleepTimeMs == that.mSleepTimeMs
+ && mIdleTimeMs == that.mIdleTimeMs
+ && mRxTimeMs == that.mRxTimeMs
+ && Arrays.equals(mTxTimeMs, that.mTxTimeMs);
+ }
- /**
- * @return the power range in dbm. e.g, range: 0dbm(lower) ~ 5dbm(upper)
- */
- @NonNull
- public Range<Integer> getPowerRangeInDbm() {
- return mPowerRangeInDbm;
- }
-
- @Override
- public String toString() {
- return "TransmitPower{"
- + " mTimeInMillis=" + mTimeInMillis
- + " mPowerRangeInDbm={" + mPowerRangeInDbm.getLower()
- + "," + mPowerRangeInDbm.getUpper()
- + "}}";
- }
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mRxTimeMs);
+ result = 31 * result + Arrays.hashCode(mTxTimeMs);
+ return result;
}
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 6da61b7..1677c61 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -444,7 +444,9 @@
mArfcnRsrpBoost = s.mArfcnRsrpBoost;
synchronized (mNetworkRegistrationInfos) {
mNetworkRegistrationInfos.clear();
- mNetworkRegistrationInfos.addAll(s.getNetworkRegistrationInfoList());
+ for (NetworkRegistrationInfo nri : s.getNetworkRegistrationInfoList()) {
+ mNetworkRegistrationInfos.add(new NetworkRegistrationInfo(nri));
+ }
}
mNrFrequencyRange = s.mNrFrequencyRange;
mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw;
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index fe7e5976..41e24dd 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -26,8 +26,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -101,9 +103,11 @@
}
mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos);
- // Sort the collection with RAN ascending order, make the ordering not matter for equals
+ // Sort the collection with RAN and then SignalMeasurementType ascending order, make the
+ // ordering not matter for equals
mSignalThresholdInfos.sort(
- Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType));
+ Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType)
+ .thenComparing(SignalThresholdInfo::getSignalMeasurementType));
return this;
}
@@ -144,7 +148,7 @@
* @return the SignalStrengthUpdateRequest object
*
* @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the
- * radio access network type in the collection is not unique
+ * signal measurement type for the same RAN in the collection is not unique
*/
public @NonNull SignalStrengthUpdateRequest build() {
return new SignalStrengthUpdateRequest(mSignalThresholdInfos,
@@ -258,14 +262,23 @@
}
/**
- * Throw IAE when the RAN in the collection is not unique.
+ * Throw IAE if SignalThresholdInfo collection is null or empty,
+ * or the SignalMeasurementType for the same RAN in the collection is not unique.
*/
private static void validate(Collection<SignalThresholdInfo> infos) {
- Set<Integer> uniqueRan = new HashSet<>(infos.size());
+ if (infos == null || infos.isEmpty()) {
+ throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty");
+ }
+
+ // Map from RAN to set of SignalMeasurementTypes
+ Map<Integer, Set<Integer>> ranToTypes = new HashMap<>(infos.size());
for (SignalThresholdInfo info : infos) {
final int ran = info.getRadioAccessNetworkType();
- if (!uniqueRan.add(ran)) {
- throw new IllegalArgumentException("RAN: " + ran + " is not unique");
+ final int type = info.getSignalMeasurementType();
+ ranToTypes.putIfAbsent(ran, new HashSet<>());
+ if (!ranToTypes.get(ran).add(type)) {
+ throw new IllegalArgumentException(
+ "SignalMeasurementType " + type + " for RAN " + ran + " is not unique");
}
}
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4189784..f55dc8b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -57,7 +57,9 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -84,6 +86,7 @@
import android.telephony.VisualVoicemailService.VisualVoicemailTask;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnSetting.MvnoType;
+import android.telephony.data.SlicingConfig;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.telephony.gba.UaSecurityProtocolIdentifier;
@@ -176,6 +179,9 @@
*/
public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
+ /** @hide */
+ public static final String EXCEPTION_RESULT_KEY = "exception";
+
/**
* The process name of the Phone app as well as many other apps that use this process name, such
* as settings and vendor components.
@@ -10855,26 +10861,149 @@
return null;
}
-
/**
- * Requests the modem activity info. The recipient will place the result
- * in `result`.
- * @param result The object on which the recipient will send the resulting
- * {@link android.telephony.ModemActivityInfo} object with key of
- * {@link #MODEM_ACTIVITY_RESULT_KEY}.
+ * Exception that may be supplied to the callback provided in {@link #requestModemActivityInfo}.
* @hide
*/
- public void requestModemActivityInfo(@NonNull ResultReceiver result) {
+ @SystemApi
+ public static class ModemActivityInfoException extends Exception {
+ /** Indicates that an unknown error occurred */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Indicates that the modem or phone processes are not available (such as when the device
+ * is in airplane mode).
+ */
+ public static final int ERROR_PHONE_NOT_AVAILABLE = 1;
+
+ /**
+ * Indicates that the modem supplied an invalid instance of {@link ModemActivityInfo}
+ */
+ public static final int ERROR_INVALID_INFO_RECEIVED = 2;
+
+ /**
+ * Indicates that the modem encountered an internal failure when processing the request
+ * for activity info.
+ */
+ public static final int ERROR_MODEM_RESPONSE_ERROR = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ERROR_"},
+ value = {
+ ERROR_UNKNOWN,
+ ERROR_PHONE_NOT_AVAILABLE,
+ ERROR_INVALID_INFO_RECEIVED,
+ ERROR_MODEM_RESPONSE_ERROR,
+ })
+ public @interface ModemActivityInfoError {}
+
+ private final int mErrorCode;
+
+ /** @hide */
+ public ModemActivityInfoException(@ModemActivityInfoError int errorCode) {
+ mErrorCode = errorCode;
+ }
+
+ public @ModemActivityInfoError int getErrorCode() {
+ return mErrorCode;
+ }
+
+ @Override
+ public String toString() {
+ switch (mErrorCode) {
+ case ERROR_UNKNOWN: return "ERROR_UNKNOWN";
+ case ERROR_PHONE_NOT_AVAILABLE: return "ERROR_PHONE_NOT_AVAILABLE";
+ case ERROR_INVALID_INFO_RECEIVED: return "ERROR_INVALID_INFO_RECEIVED";
+ case ERROR_MODEM_RESPONSE_ERROR: return "ERROR_MODEM_RESPONSE_ERROR";
+ default: return "UNDEFINED";
+ }
+ }
+ }
+
+ /**
+ * Requests the current modem activity info.
+ *
+ * The provided instance of {@link ModemActivityInfo} represents the cumulative activity since
+ * the last restart of the phone process.
+ *
+ * @param callback A callback object to which the result will be delivered. If there was an
+ * error processing the request, {@link OutcomeReceiver#onError} will be called
+ * with more details about the error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ // Pass no handler into the receiver, since we're going to be trampolining the call to the
+ // listener onto the provided executor.
+ ResultReceiver wrapperResultReceiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle data) {
+ if (data == null) {
+ Log.w(TAG, "requestModemActivityInfo: received null bundle");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ data.setDefusable(true);
+ if (data.containsKey(EXCEPTION_RESULT_KEY)) {
+ int receivedErrorCode = data.getInt(EXCEPTION_RESULT_KEY);
+ sendErrorToListener(receivedErrorCode);
+ return;
+ }
+
+ if (!data.containsKey(MODEM_ACTIVITY_RESULT_KEY)) {
+ Log.w(TAG, "requestModemActivityInfo: Bundle did not contain expected key");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ Parcelable receivedResult = data.getParcelable(MODEM_ACTIVITY_RESULT_KEY);
+ if (!(receivedResult instanceof ModemActivityInfo)) {
+ Log.w(TAG, "requestModemActivityInfo: Bundle contained something that wasn't "
+ + "a ModemActivityInfo.");
+ sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+ return;
+ }
+ ModemActivityInfo modemActivityInfo = (ModemActivityInfo) receivedResult;
+ if (!modemActivityInfo.isValid()) {
+ Log.w(TAG, "requestModemActivityInfo: Received an invalid ModemActivityInfo");
+ sendErrorToListener(ModemActivityInfoException.ERROR_INVALID_INFO_RECEIVED);
+ return;
+ }
+ Log.d(TAG, "requestModemActivityInfo: Sending result to app: " + modemActivityInfo);
+ sendResultToListener(modemActivityInfo);
+ }
+
+ private void sendResultToListener(ModemActivityInfo info) {
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onResult(info)));
+ }
+
+ private void sendErrorToListener(int code) {
+ ModemActivityInfoException e = new ModemActivityInfoException(code);
+ Binder.withCleanCallingIdentity(() ->
+ executor.execute(() ->
+ callback.onError(e)));
+ }
+ };
+
try {
ITelephony service = getITelephony();
if (service != null) {
- service.requestModemActivityInfo(result);
+ service.requestModemActivityInfo(wrapperResultReceiver);
return;
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
}
- result.send(0, null);
+ executor.execute(() -> callback.onError(
+ new ModemActivityInfoException(
+ ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE)));
}
/**
@@ -15093,4 +15222,96 @@
return PhoneCapability.DEFAULT_SSSS_CAPABILITY;
}
}
+
+ /**
+ * Exception that may be supplied to the callback in {@link #getNetworkSlicingConfiguration} if
+ * something goes awry.
+ */
+ public static class SlicingException extends Exception {
+ /**
+ * Getting the current slicing configuration successfully. Used internally only.
+ * @hide
+ */
+ public static final int SUCCESS = 0;
+
+ /**
+ * The system timed out waiting for a response from the Radio.
+ */
+ public static final int ERROR_TIMEOUT = 1;
+
+ /**
+ * The modem returned a failure.
+ */
+ public static final int ERROR_MODEM_ERROR = 2;
+
+ /** @hide */
+ @IntDef(prefix = {"ERROR_"}, value = {
+ ERROR_TIMEOUT,
+ ERROR_MODEM_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SlicingError {}
+
+ private final int mErrorCode;
+
+ public SlicingException(@SlicingError int errorCode) {
+ mErrorCode = errorCode;
+ }
+
+ /**
+ * Fetches the error code associated with this exception.
+ * @return An error code.
+ */
+ public @SlicingError int getErrorCode() {
+ return mErrorCode;
+ }
+ }
+
+ /** @hide */
+ public static final String KEY_SLICING_CONFIG_HANDLE = "slicing_config_handle";
+
+ /**
+ * Request to get the current slicing configuration including URSP rules and
+ * NSSAIs (configured, allowed and rejected).
+ *
+ * This method can be invoked if one of the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * </ul>
+ *
+ * @param executor the executor on which callback will be invoked.
+ * @param callback a callback to receive the current slicing configuration.
+ */
+ @SuppressAutoDoc // No support for carrier privileges (b/72967236).
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void getNetworkSlicingConfiguration(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<SlicingConfig, SlicingException> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ telephony.getSlicingConfig(new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle result) {
+ if (resultCode != SlicingException.SUCCESS) {
+ executor.execute(() -> callback.onError(
+ new SlicingException(resultCode)));
+ return;
+ }
+ SlicingConfig slicingConfig =
+ result.getParcelable(KEY_SLICING_CONFIG_HANDLE);
+ executor.execute(() -> callback.onResult(slicingConfig));
+ }
+ });
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/telephony/java/android/telephony/data/NetworkSliceInfo.java b/telephony/java/android/telephony/data/NetworkSliceInfo.java
index 1d90095..232a930 100644
--- a/telephony/java/android/telephony/data/NetworkSliceInfo.java
+++ b/telephony/java/android/telephony/data/NetworkSliceInfo.java
@@ -19,8 +19,6 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,10 +33,7 @@
* SliceServiceType defines the type of service provided by the slice, and SliceDifferentiator is
* used to differentiate between multiple slices of the same type. If the devices is not on HPLMN,
* the mappedHplmn versions of these 2 fields indicate the corresponding values in HPLMN.
- *
- * @hide
*/
-@SystemApi
public final class NetworkSliceInfo implements Parcelable {
/**
* When set on a Slice Differentiator, this value indicates that there is no corresponding
@@ -68,14 +63,14 @@
/**
* The min acceptable value for a Slice Differentiator
+ * @hide
*/
- @SuppressLint("MinMaxConstant")
public static final int MIN_SLICE_DIFFERENTIATOR = -1;
/**
* The max acceptable value for a Slice Differentiator
+ * @hide
*/
- @SuppressLint("MinMaxConstant")
public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE;
/** @hide */
@@ -88,6 +83,62 @@
@Retention(RetentionPolicy.SOURCE)
public @interface SliceServiceType {}
+ /**
+ * The slice status is unknown. This can happen during IWLAN->cellular handover when the
+ * NetworkSliceInfo is received over IWLAN.
+ */
+ public static final int SLICE_STATUS_UNKNOWN = 0;
+
+ /**
+ * The slice is configured but not allowed or rejected yet.
+ */
+ public static final int SLICE_STATUS_CONFIGURED = 1;
+
+ /**
+ * The slice is allowed to be used.
+ */
+ public static final int SLICE_STATUS_ALLOWED = 2;
+
+ /**
+ * The slice is rejected because not available in PLMN.
+ */
+ public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN = 3;
+
+ /**
+ * The slice is rejected because not available in registered area.
+ */
+ public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA = 4;
+
+ /**
+ * The slice is configured by home operator(HPLMN) in default and is used if configured/allowed
+ * slices are not available for the serving PLMN.
+ */
+ public static final int SLICE_STATUS_DEFAULT_CONFIGURED = 5;
+
+ /**
+ * The min acceptable value for a slice status.
+ * @hide
+ */
+ public static final int MIN_SLICE_STATUS = SLICE_STATUS_UNKNOWN;
+
+ /**
+ * The max acceptable value for a slice status.
+ * @hide
+ */
+ public static final int MAX_SLICE_STATUS = SLICE_STATUS_DEFAULT_CONFIGURED;
+
+ /** @hide */
+ @IntDef(prefix = { "SLICE_STATUS_" }, value = {
+ SLICE_STATUS_UNKNOWN,
+ SLICE_STATUS_CONFIGURED,
+ SLICE_STATUS_ALLOWED,
+ SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN,
+ SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA,
+ SLICE_STATUS_DEFAULT_CONFIGURED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SliceStatus {}
+
@SliceServiceType
private final int mSliceServiceType;
@@ -97,14 +148,18 @@
private final int mMappedHplmnSliceServiceType;
@IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
private final int mMappedHplmnSliceDifferentiator;
+ @SliceStatus
+ @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS)
+ private final int mStatus;
private NetworkSliceInfo(@SliceServiceType int sliceServiceType,
int sliceDifferentiator, int mappedHplmnSliceServiceType,
- int mappedHplmnSliceDifferentiator) {
+ int mappedHplmnSliceDifferentiator, int status) {
mSliceServiceType = sliceServiceType;
mSliceDifferentiator = sliceDifferentiator;
mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+ mStatus = status;
}
/**
@@ -157,11 +212,21 @@
return mMappedHplmnSliceDifferentiator;
}
+ /**
+ * Field to indicate the current status of the slice.
+ * @return the current status for this slice info.
+ */
+ @SliceStatus
+ public int getStatus() {
+ return mStatus;
+ }
+
private NetworkSliceInfo(@NonNull Parcel in) {
mSliceServiceType = in.readInt();
mSliceDifferentiator = in.readInt();
mMappedHplmnSliceServiceType = in.readInt();
mMappedHplmnSliceDifferentiator = in.readInt();
+ mStatus = in.readInt();
}
@Override
@@ -175,6 +240,7 @@
dest.writeInt(mSliceDifferentiator);
dest.writeInt(mMappedHplmnSliceServiceType);
dest.writeInt(mMappedHplmnSliceDifferentiator);
+ dest.writeInt(mStatus);
}
public static final @android.annotation.NonNull Parcelable.Creator<NetworkSliceInfo> CREATOR =
@@ -200,6 +266,7 @@
+ ", mMappedHplmnSliceServiceType="
+ sliceServiceTypeToString(mMappedHplmnSliceServiceType)
+ ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator
+ + ", mStatus=" + sliceStatusToString(mStatus)
+ '}';
}
@@ -218,6 +285,25 @@
}
}
+ private static String sliceStatusToString(@SliceStatus int sliceStatus) {
+ switch(sliceStatus) {
+ case SLICE_STATUS_UNKNOWN:
+ return "UNKNOWN";
+ case SLICE_STATUS_CONFIGURED:
+ return "CONFIGURED";
+ case SLICE_STATUS_ALLOWED:
+ return "ALLOWED";
+ case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN:
+ return "REJECTED_NOT_AVAILABLE_IN_PLMN";
+ case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA:
+ return "REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA";
+ case SLICE_STATUS_DEFAULT_CONFIGURED:
+ return "DEFAULT_CONFIGURED";
+ default:
+ return Integer.toString(sliceStatus);
+ }
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -226,13 +312,14 @@
return mSliceServiceType == sliceInfo.mSliceServiceType
&& mSliceDifferentiator == sliceInfo.mSliceDifferentiator
&& mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType
- && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator;
+ && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator
+ && mStatus == sliceInfo.mStatus;
}
@Override
public int hashCode() {
return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType,
- mMappedHplmnSliceDifferentiator);
+ mMappedHplmnSliceDifferentiator, mStatus);
}
/**
@@ -257,6 +344,9 @@
private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE;
@IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+ @SliceStatus
+ @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS)
+ private int mStatus = SLICE_STATUS_UNKNOWN;
/**
* Default constructor for Builder.
@@ -281,8 +371,7 @@
* A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
* corresponding Slice.
*
- * @throws IllegalArgumentException if the parameter is not between
- * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+ * @throws IllegalArgumentException if the parameter is not in the expected range.
*
* @return The same instance of the builder.
*/
@@ -316,8 +405,7 @@
* A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
* corresponding Slice of the HPLMN.
*
- * @throws IllegalArgumentException if the parameter is not between
- * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+ * @throws IllegalArgumentException if the parameter is not in the expected range.
*
* @return The same instance of the builder.
*/
@@ -334,6 +422,22 @@
}
/**
+ * Set the slice status.
+ *
+ * @throws IllegalArgumentException if the status is invalid.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setStatus(@SliceStatus int status) {
+ if (status < MIN_SLICE_STATUS || status > MAX_SLICE_STATUS) {
+ throw new IllegalArgumentException("The slice status is not valid");
+ }
+ this.mStatus = status;
+ return this;
+ }
+
+ /**
* Build the {@link NetworkSliceInfo}.
*
* @return the {@link NetworkSliceInfo} object.
@@ -341,7 +445,8 @@
@NonNull
public NetworkSliceInfo build() {
return new NetworkSliceInfo(this.mSliceServiceType, this.mSliceDifferentiator,
- this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator);
+ this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator,
+ this.mStatus);
}
}
}
diff --git a/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl b/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl
new file mode 100644
index 0000000..563a00e
--- /dev/null
+++ b/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.
+ */
+
+package android.telephony.data;
+
+parcelable RouteSelectionDescriptor;
diff --git a/telephony/java/android/telephony/data/RouteSelectionDescriptor.java b/telephony/java/android/telephony/data/RouteSelectionDescriptor.java
new file mode 100644
index 0000000..c2457f2
--- /dev/null
+++ b/telephony/java/android/telephony/data/RouteSelectionDescriptor.java
@@ -0,0 +1,263 @@
+/**
+ * Copyright 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.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a single route selection descriptor as defined in
+ * 3GPP TS 24.526.
+ */
+public final class RouteSelectionDescriptor implements Parcelable {
+ /**
+ * The min acceptable value for the precedence of a route selection descriptor.
+ * @hide
+ */
+ public static final int MIN_ROUTE_PRECEDENCE = 0;
+
+ /**
+ * The max acceptable value for the precedence of a route selection descriptor.
+ * @hide
+ */
+ public static final int MAX_ROUTE_PRECEDENCE = 255;
+
+ /**
+ * The route selection descriptor is for the session with IPV4 type.
+ */
+ public static final int SESSION_TYPE_IPV4 = 0;
+
+ /**
+ * The route selection descriptor is for the session with IPV6 type.
+ */
+ public static final int SESSION_TYPE_IPV6 = 1;
+
+ /**
+ * The route selection descriptor is for the session with both IP and IPV6 types.
+ */
+ public static final int SESSION_TYPE_IPV4V6 = 2;
+
+ /** @hide */
+ @IntDef(prefix = { "SESSION_TYPE_" }, value = {
+ SESSION_TYPE_IPV4,
+ SESSION_TYPE_IPV6,
+ SESSION_TYPE_IPV4V6,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RouteSessionType {}
+
+ /**
+ * The route selection descriptor is using SSC mode 1. The session will provide continual
+ * support when UE's location is updated.
+ */
+ public static final int ROUTE_SSC_MODE_1 = 1;
+
+ /**
+ * The route selection descriptor is using SSC mode 2. The new session for the same network
+ * will be established after releasing the old session when UE's location is updated.
+ */
+ public static final int ROUTE_SSC_MODE_2 = 2;
+
+ /**
+ * The route selection descriptor is using SSC mode 3. The new session for the same network is
+ * allowed to be established before releasing the old session when UE's location is updated.
+ */
+ public static final int ROUTE_SSC_MODE_3 = 3;
+
+ /**
+ * The min acceptable value for the SSC mode of a route selection descriptor.
+ * @hide
+ */
+ public static final int MIN_ROUTE_SSC_MODE = ROUTE_SSC_MODE_1;
+
+ /**
+ * The max acceptable value for the SSC mode of a route selection descriptor.
+ * @hide
+ */
+ public static final int MAX_ROUTE_SSC_MODE = ROUTE_SSC_MODE_3;
+
+ /** @hide */
+ @IntDef(prefix = { "ROUTE_SSC_MODE_" }, value = {
+ ROUTE_SSC_MODE_1,
+ ROUTE_SSC_MODE_2,
+ ROUTE_SSC_MODE_3,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RouteSscMode {}
+
+ @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE)
+ private final int mPrecedence;
+ @RouteSessionType
+ private final int mSessionType;
+ @RouteSscMode
+ @IntRange(from = MIN_ROUTE_SSC_MODE, to = MAX_ROUTE_SSC_MODE)
+ private final int mSscMode;
+ private final List<NetworkSliceInfo> mSliceInfo;
+ private final List<String> mDnn;
+
+ /** @hide */
+ RouteSelectionDescriptor(android.hardware.radio.V1_6.RouteSelectionDescriptor rsd) {
+ this(rsd.precedence, rsd.sessionType.value(), rsd.sscMode.value(), rsd.sliceInfo,
+ rsd.dnn);
+ }
+
+ /** @hide */
+ public RouteSelectionDescriptor(int precedence, int sessionType, int sscMode,
+ List<android.hardware.radio.V1_6.SliceInfo> sliceInfo, List<String> dnn) {
+ mPrecedence = precedence;
+ mSessionType = sessionType;
+ mSscMode = sscMode;
+ mSliceInfo = new ArrayList<NetworkSliceInfo>();
+ for (android.hardware.radio.V1_6.SliceInfo si : sliceInfo) {
+ mSliceInfo.add(sliceInfoBuilder(si));
+ }
+ mDnn = new ArrayList<String>();
+ mDnn.addAll(dnn);
+ }
+
+ private NetworkSliceInfo sliceInfoBuilder(android.hardware.radio.V1_6.SliceInfo si) {
+ NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder()
+ .setSliceServiceType(si.sst)
+ .setMappedHplmnSliceServiceType(si.mappedHplmnSst);
+ if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
+ builder
+ .setSliceDifferentiator(si.sliceDifferentiator)
+ .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD);
+ }
+ return builder.build();
+ }
+
+ private RouteSelectionDescriptor(Parcel p) {
+ mPrecedence = p.readInt();
+ mSessionType = p.readInt();
+ mSscMode = p.readInt();
+ mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR);
+ mDnn = new ArrayList<String>();
+ p.readStringList(mDnn);
+ }
+
+ /**
+ * Precedence value in the range of 0 to 255. Higher value has lower precedence.
+ * @return the precedence value for this route selection descriptor.
+ */
+ @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE)
+ public int getPrecedence() {
+ return mPrecedence;
+ }
+
+ /**
+ * This is used for checking which session type defined in 3GPP TS 23.501 is allowed for the
+ * route in a route selection descriptor.
+ * @return the session type for this route selection descriptor.
+ */
+ @RouteSessionType
+ public int getSessionType() {
+ return mSessionType;
+ }
+
+ /**
+ * SSC mode stands for Session and Service Continuity mode (which specifies the IP continuity
+ * mode) as defined in 3GPP TS 23.501.
+ * @return the SSC mode for this route selection descriptor.
+ */
+ @RouteSscMode
+ public int getSscMode() {
+ return mSscMode;
+ }
+
+ /**
+ * This is the list of all the slices available in the route selection descriptor as indicated
+ * by the network. These are the slices that can be used by the device if this route selection
+ * descriptor is used based the traffic (see 3GPP TS 23.501 for details).
+ * @return the list of all the slices available in the route selection descriptor.
+ */
+ public @NonNull List<NetworkSliceInfo> getSliceInfo() {
+ return mSliceInfo;
+ }
+
+ /**
+ * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. There
+ * can be 0 or more DNNs specified in a route selection descriptor.
+ * @return the list of DNN for this route selection descriptor.
+ */
+ public @NonNull List<String> getDataNetworkName() {
+ return mDnn;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPrecedence);
+ dest.writeInt(mSessionType);
+ dest.writeInt(mSscMode);
+ dest.writeTypedList(mSliceInfo, flags);
+ dest.writeStringList(mDnn);
+ }
+
+ public static final @NonNull Parcelable.Creator<RouteSelectionDescriptor> CREATOR =
+ new Parcelable.Creator<RouteSelectionDescriptor>() {
+ @Override
+ public RouteSelectionDescriptor createFromParcel(Parcel source) {
+ return new RouteSelectionDescriptor(source);
+ }
+
+ @Override
+ public RouteSelectionDescriptor[] newArray(int size) {
+ return new RouteSelectionDescriptor[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RouteSelectionDescriptor that = (RouteSelectionDescriptor) o;
+ return mPrecedence == that.mPrecedence
+ && mSessionType == that.mSessionType
+ && mSscMode == that.mSscMode
+ && mSliceInfo.size() == that.mSliceInfo.size()
+ && mSliceInfo.containsAll(that.mSliceInfo)
+ && mDnn.size() == that.mDnn.size()
+ && mDnn.containsAll(that.mDnn);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPrecedence, mSessionType, mSscMode, mSliceInfo, mDnn);
+ }
+
+ @Override
+ public String toString() {
+ return "{.precedence = " + mPrecedence + ", .sessionType = " + mSessionType
+ + ", .sscMode = " + mSscMode + ", .sliceInfo = " + mSliceInfo
+ + ", .dnn = " + mDnn + "}";
+ }
+}
diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/telephony/java/android/telephony/data/SlicingConfig.aidl
new file mode 100644
index 0000000..ad93d8c
--- /dev/null
+++ b/telephony/java/android/telephony/data/SlicingConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.
+ */
+
+package android.telephony.data;
+
+parcelable SlicingConfig;
diff --git a/telephony/java/android/telephony/data/SlicingConfig.java b/telephony/java/android/telephony/data/SlicingConfig.java
new file mode 100644
index 0000000..990e4d2
--- /dev/null
+++ b/telephony/java/android/telephony/data/SlicingConfig.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 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.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a slicing configuration
+ */
+public final class SlicingConfig implements Parcelable {
+ private final List<UrspRule> mUrspRules;
+ private final List<NetworkSliceInfo> mSliceInfo;
+
+ public SlicingConfig() {
+ mUrspRules = new ArrayList<UrspRule>();
+ mSliceInfo = new ArrayList<NetworkSliceInfo>();
+ }
+
+ /** @hide */
+ public SlicingConfig(android.hardware.radio.V1_6.SlicingConfig sc) {
+ this(sc.urspRules, sc.sliceInfo);
+ }
+
+ /** @hide */
+ public SlicingConfig(List<android.hardware.radio.V1_6.UrspRule> urspRules,
+ List<android.hardware.radio.V1_6.SliceInfo> sliceInfo) {
+ mUrspRules = new ArrayList<UrspRule>();
+ for (android.hardware.radio.V1_6.UrspRule ur : urspRules) {
+ mUrspRules.add(new UrspRule(ur.precedence, ur.trafficDescriptors,
+ ur.routeSelectionDescriptor));
+ }
+ mSliceInfo = new ArrayList<NetworkSliceInfo>();
+ for (android.hardware.radio.V1_6.SliceInfo si : sliceInfo) {
+ mSliceInfo.add(sliceInfoBuilder(si));
+ }
+ }
+
+ private NetworkSliceInfo sliceInfoBuilder(android.hardware.radio.V1_6.SliceInfo si) {
+ NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder()
+ .setSliceServiceType(si.sst)
+ .setMappedHplmnSliceServiceType(si.mappedHplmnSst);
+ if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
+ builder
+ .setSliceDifferentiator(si.sliceDifferentiator)
+ .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD);
+ }
+ return builder.build();
+ }
+
+ /** @hide */
+ public SlicingConfig(Parcel p) {
+ mUrspRules = p.createTypedArrayList(UrspRule.CREATOR);
+ mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR);
+ }
+
+ /**
+ * This list contains the current URSP rules. Empty list represents that no rules are
+ * configured.
+ * @return the current URSP rules for this slicing configuration.
+ */
+ public @NonNull List<UrspRule> getUrspRules() {
+ return mUrspRules;
+ }
+
+ /**
+ * @return the list of all slices for this slicing configuration.
+ */
+ public @NonNull List<NetworkSliceInfo> getSliceInfo() {
+ return mSliceInfo;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mUrspRules, flags);
+ dest.writeTypedList(mSliceInfo, flags);
+ }
+
+ public static final @NonNull Parcelable.Creator<SlicingConfig> CREATOR =
+ new Parcelable.Creator<SlicingConfig>() {
+ @Override
+ public SlicingConfig createFromParcel(Parcel source) {
+ return new SlicingConfig(source);
+ }
+
+ @Override
+ public SlicingConfig[] newArray(int size) {
+ return new SlicingConfig[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SlicingConfig that = (SlicingConfig) o;
+ return mUrspRules.size() == that.mUrspRules.size()
+ && mUrspRules.containsAll(that.mUrspRules)
+ && mSliceInfo.size() == that.mSliceInfo.size()
+ && mSliceInfo.containsAll(that.mSliceInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUrspRules, mSliceInfo);
+ }
+
+ @Override
+ public String toString() {
+ return "{.urspRules = " + mUrspRules + ", .sliceInfo = " + mSliceInfo + "}";
+ }
+}
diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java
index 480379d..d813bc5 100644
--- a/telephony/java/android/telephony/data/TrafficDescriptor.java
+++ b/telephony/java/android/telephony/data/TrafficDescriptor.java
@@ -18,20 +18,17 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
- * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for URSP traffic
- * matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an optional DNN, which,
- * if present, must be used for traffic matching; it does not specify the end point to be used for
- * the data call.
- * @hide
+ * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection
+ * Policy(URSP) traffic matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an
+ * optional Data Network Name(DNN), which, if present, must be used for traffic matching; it does
+ * not specify the end point to be used for the data call.
*/
-@SystemApi
public final class TrafficDescriptor implements Parcelable {
private final String mDnn;
private final String mOsAppId;
@@ -45,8 +42,10 @@
* Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2
* @param dnn optional DNN, which must be used for traffic matching, if present
* @param osAppId OsId + osAppId of the traffic descriptor
+ *
+ * @hide
*/
- public TrafficDescriptor(@Nullable String dnn, @Nullable String osAppId) {
+ public TrafficDescriptor(String dnn, String osAppId) {
mDnn = dnn;
mOsAppId = osAppId;
}
@@ -55,12 +54,13 @@
* DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003.
* @return the DNN of this traffic descriptor.
*/
- public @Nullable String getDnn() {
+ public @Nullable String getDataNetworkName() {
return mDnn;
}
/**
- * OsAppId represents the OsId + OsAppId as defined in 3GPP TS 24.526 Section 5.2.
+ * OsAppId is the app id as defined in 3GPP TS 24.526 Section 5.2, and it identifies a traffic
+ * category.
* @return the OS App ID of this traffic descriptor.
*/
public @Nullable String getOsAppId() {
@@ -108,4 +108,65 @@
public int hashCode() {
return Objects.hash(mDnn, mOsAppId);
}
+
+ /**
+ * Provides a convenient way to set the fields of a {@link TrafficDescriptor} when creating a
+ * new instance.
+ *
+ * <p>The example below shows how you might create a new {@code TrafficDescriptor}:
+ *
+ * <pre><code>
+ *
+ * TrafficDescriptor response = new TrafficDescriptor.Builder()
+ * .setDnn("")
+ * .build();
+ * </code></pre>
+ */
+ public static final class Builder {
+ private String mDnn = null;
+ private String mOsAppId = null;
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Set the Data Network Name(DNN).
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setDataNetworkName(@NonNull String dnn) {
+ this.mDnn = dnn;
+ return this;
+ }
+
+ /**
+ * Set the OS App ID.
+ *
+ * @return The same instance of the builder.
+ */
+ @NonNull
+ public Builder setOsAppId(@NonNull String osAppId) {
+ this.mOsAppId = osAppId;
+ return this;
+ }
+
+ /**
+ * Build the {@link TrafficDescriptor}.
+ *
+ * @throws IllegalArgumentException if DNN and OS App ID are null.
+ *
+ * @return the {@link TrafficDescriptor} object.
+ */
+ @NonNull
+ public TrafficDescriptor build() {
+ if (this.mDnn == null && this.mOsAppId == null) {
+ throw new IllegalArgumentException("DNN and OS App ID are null");
+ }
+ return new TrafficDescriptor(this.mDnn, this.mOsAppId);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/data/UrspRule.aidl b/telephony/java/android/telephony/data/UrspRule.aidl
new file mode 100644
index 0000000..2bed583
--- /dev/null
+++ b/telephony/java/android/telephony/data/UrspRule.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.
+ */
+
+package android.telephony.data;
+
+parcelable UrspRule;
diff --git a/telephony/java/android/telephony/data/UrspRule.java b/telephony/java/android/telephony/data/UrspRule.java
new file mode 100644
index 0000000..e2c47fd
--- /dev/null
+++ b/telephony/java/android/telephony/data/UrspRule.java
@@ -0,0 +1,178 @@
+/**
+ * Copyright 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.
+ */
+
+package android.telephony.data;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.radio.V1_6.OptionalDnn;
+import android.hardware.radio.V1_6.OptionalOsAppId;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a single URSP rule as defined in 3GPP TS 24.526. URSP stands for UE Route Selection
+ * Policy. In 5G, network can provide URSP information to devices which provides information on
+ * what connection parameters should be used for what traffic.
+ */
+public final class UrspRule implements Parcelable {
+ /**
+ * The min acceptable value for the precedence of a URSP rule.
+ * @hide
+ */
+ public static final int MIN_URSP_PRECEDENCE = 0;
+
+ /**
+ * The max acceptable value for the precedence of a URSP rule.
+ * @hide
+ */
+ public static final int MAX_URSP_PRECEDENCE = 255;
+
+ @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE)
+ private final int mPrecedence;
+ private final List<TrafficDescriptor> mTrafficDescriptors;
+ private final List<RouteSelectionDescriptor> mRouteSelectionDescriptor;
+
+ UrspRule(android.hardware.radio.V1_6.UrspRule ur) {
+ this(ur.precedence, ur.trafficDescriptors, ur.routeSelectionDescriptor);
+ }
+
+ /** @hide */
+ public UrspRule(int precedence,
+ List<android.hardware.radio.V1_6.TrafficDescriptor> trafficDescriptors,
+ List<android.hardware.radio.V1_6.RouteSelectionDescriptor> routeSelectionDescriptor) {
+ mPrecedence = precedence;
+ mTrafficDescriptors = new ArrayList<TrafficDescriptor>();
+ for (android.hardware.radio.V1_6.TrafficDescriptor td : trafficDescriptors) {
+ mTrafficDescriptors.add(convertToTrafficDescriptor(td));
+ }
+ mRouteSelectionDescriptor = new ArrayList<RouteSelectionDescriptor>();
+ for (android.hardware.radio.V1_6.RouteSelectionDescriptor rsd : routeSelectionDescriptor) {
+ mRouteSelectionDescriptor.add(new RouteSelectionDescriptor(rsd));
+ }
+ }
+
+ /** Convert an ArrayList of Bytes to an exactly-sized primitive array */
+ private byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) {
+ byte[] ret = new byte[bytes.size()];
+ for (int i = 0; i < ret.length; i++) {
+ ret[i] = bytes.get(i);
+ }
+ return ret;
+ }
+
+ private TrafficDescriptor convertToTrafficDescriptor(
+ android.hardware.radio.V1_6.TrafficDescriptor td) {
+ String dnn = td.dnn.getDiscriminator() == OptionalDnn.hidl_discriminator.noinit
+ ? null : td.dnn.value();
+ String osAppId = td.osAppId.getDiscriminator() == OptionalOsAppId.hidl_discriminator.noinit
+ ? null : new String(arrayListToPrimitiveArray(td.osAppId.value().osAppId));
+ TrafficDescriptor.Builder builder = new TrafficDescriptor.Builder();
+ if (dnn != null) {
+ builder.setDataNetworkName(dnn);
+ }
+ if (osAppId != null) {
+ builder.setOsAppId(osAppId);
+ }
+ return builder.build();
+ }
+
+ private UrspRule(Parcel p) {
+ mPrecedence = p.readInt();
+ mTrafficDescriptors = p.createTypedArrayList(TrafficDescriptor.CREATOR);
+ mRouteSelectionDescriptor = p.createTypedArrayList(RouteSelectionDescriptor.CREATOR);
+ }
+
+ /**
+ * Precedence value in the range of 0 to 255. Higher value has lower precedence.
+ * @return the precedence value for this URSP rule.
+ */
+ @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE)
+ public int getPrecedence() {
+ return mPrecedence;
+ }
+
+ /**
+ * These traffic descriptors are used as a matcher for network requests.
+ * @return the traffic descriptors which are associated to this URSP rule.
+ */
+ public @NonNull List<TrafficDescriptor> getTrafficDescriptors() {
+ return mTrafficDescriptors;
+ }
+
+ /**
+ * List of routes (connection parameters) that must be used by the device for requests matching
+ * a traffic descriptor.
+ * @return the route selection descriptors which are associated to this URSP rule.
+ */
+ public @NonNull List<RouteSelectionDescriptor> getRouteSelectionDescriptor() {
+ return mRouteSelectionDescriptor;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPrecedence);
+ dest.writeTypedList(mTrafficDescriptors, flags);
+ dest.writeTypedList(mRouteSelectionDescriptor, flags);
+ }
+
+ public static final @NonNull Parcelable.Creator<UrspRule> CREATOR =
+ new Parcelable.Creator<UrspRule>() {
+ @Override
+ public UrspRule createFromParcel(Parcel source) {
+ return new UrspRule(source);
+ }
+
+ @Override
+ public UrspRule[] newArray(int size) {
+ return new UrspRule[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ UrspRule that = (UrspRule) o;
+ return mPrecedence == that.mPrecedence
+ && mTrafficDescriptors.size() == that.mTrafficDescriptors.size()
+ && mTrafficDescriptors.containsAll(that.mTrafficDescriptors)
+ && mRouteSelectionDescriptor.size() == that.mRouteSelectionDescriptor.size()
+ && mRouteSelectionDescriptor.containsAll(that.mRouteSelectionDescriptor);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPrecedence, mTrafficDescriptors, mRouteSelectionDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return "{.precedence = " + mPrecedence + ", .trafficDescriptors = " + mTrafficDescriptors
+ + ", .routeSelectionDescriptor = " + mRouteSelectionDescriptor + "}";
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 0aff997..dfe5e6c9 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -766,7 +766,10 @@
* The method is only valid to call when the session state is in
* {@link ImsCallSession.State#IDLE}.
*
- * @param callee dialed string to make the call to
+ * @param callee dial string to make the call to. The platform passes the dialed number
+ * entered by the user as-is. The {@link ImsService} should ensure that the
+ * number is formatted in SIP messages appropriately (e.g. using
+ * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}).
* @param profile call profile to make the call with the specified service type,
* call type and media information
* @see Listener#callSessionStarted, Listener#callSessionStartFailed
@@ -788,7 +791,10 @@
* The method is only valid to call when the session state is in
* {@link ImsCallSession.State#IDLE}.
*
- * @param participants participant list to initiate an IMS conference call
+ * @param participants participant list to initiate an IMS conference call. The platform passes
+ * the dialed numbers entered by the user as-is. The {@link ImsService} should
+ * ensure that the number is formatted in SIP messages appropriately (e.g. using
+ * {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}).
* @param profile call profile to make the call with the specified service type,
* call type and media information
* @see Listener#callSessionStarted, Listener#callSessionStartFailed
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ae5f997..d812b46 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2447,4 +2447,10 @@
* Gets the current phone capability.
*/
PhoneCapability getPhoneCapability();
+
+ /**
+ * Request to get the current slicing configuration including URSP rules and
+ * NSSAIs (configured, allowed and rejected).
+ */
+ void getSlicingConfig(in ResultReceiver callback);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 822fc44..fe8e671 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -527,6 +527,7 @@
int RIL_REQUEST_SET_DATA_THROTTLING = 221;
int RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP = 222;
int RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP = 223;
+ int RIL_REQUEST_GET_SLICING_CONFIG = 224;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 460a26d..0bb6198 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -32,7 +32,9 @@
":android-test-mock-sources",
// Note: Below are NOT APIs of this library. We only take APIs under
// the android.test.mock package. They however provide private APIs that
- // android.test.mock APIs references to.
+ // android.test.mock APIs references to. We need to have the classes in
+ // source code form to have access to the @hide comment which disappears
+ // when the classes are compiled into a Jar library.
":framework-core-sources-for-test-mock",
":framework_native_aidl",
],
@@ -46,6 +48,14 @@
api_packages: [
"android.test.mock",
],
+ // Only include android.test.mock.* classes. Jarjar rules below removes
+ // classes in other packages like android.content. In order to keep the
+ // list up-to-date, permitted_packages ensures that the library contains
+ // clases under android.test.mock after the jarjar rules are applied.
+ jarjar_rules: "jarjar-rules.txt",
+ permitted_packages: [
+ "android.test.mock",
+ ],
compile_dex: true,
default_to_stubs: true,
dist_group: "android",
diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt
new file mode 100644
index 0000000..4420a44
--- /dev/null
+++ b/test-mock/jarjar-rules.txt
@@ -0,0 +1,7 @@
+zap android.accounts.**
+zap android.app.**
+zap android.content.**
+zap android.database.**
+zap android.os.**
+zap android.util.**
+zap android.view.**
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 840a588..086ef95 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -50,7 +50,7 @@
"cts-install-lib-host",
],
data: [
- ":com.android.apex.cts.shim.v2_prebuilt",
+ ":StagedInstallTestApexV2",
":TestAppAv1",
],
test_suites: ["general-tests"],
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 0f84f6e..c9a8947a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -322,6 +322,7 @@
triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
verify(mSafeModeTimeoutAlarm).cancel();
assertFalse(mGatewayConnection.isInSafeMode());
+ verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
}
@Test
@@ -391,6 +392,7 @@
triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
+ verifySafeModeStateAndCallbackFired(2 /* invocationCount */, false /* isInSafeMode */);
assertFalse(mGatewayConnection.isInSafeMode());
}
@@ -400,7 +402,7 @@
mTestLooper.dispatchAll();
triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
- assertFalse(mGatewayConnection.isInSafeMode());
+ verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
// Trigger a failed validation, and the subsequent safemode timeout.
triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
@@ -416,7 +418,7 @@
runnableCaptor.getValue().run();
mTestLooper.dispatchAll();
- assertTrue(mGatewayConnection.isInSafeMode());
+ verifySafeModeStateAndCallbackFired(2 /* invocationCount */, true /* isInSafeMode */);
}
private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index a696b3a..64d0bca 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -301,6 +300,11 @@
expectCanceled);
}
+ protected void verifySafeModeStateAndCallbackFired(int invocationCount, boolean isInSafeMode) {
+ verify(mGatewayStatusCallback, times(invocationCount)).onSafeModeStatusChanged();
+ assertEquals(isInSafeMode, mGatewayConnection.isInSafeMode());
+ }
+
protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
@NonNull State expectedState) {
// Set a VcnNetworkAgent, and expect it to be unregistered and cleared
@@ -314,9 +318,8 @@
delayedEvent.run();
mTestLooper.dispatchAll();
- verify(mGatewayStatusCallback).onSafeModeStatusChanged();
assertEquals(expectedState, mGatewayConnection.getCurrentState());
- assertTrue(mGatewayConnection.isInSafeMode());
+ verifySafeModeStateAndCallbackFired(1, true);
verify(mockNetworkAgent).unregister();
assertNull(mGatewayConnection.getNetworkAgent());
diff --git a/tools/bit/print.cpp b/tools/bit/print.cpp
index 35feda1..8bc6f16 100644
--- a/tools/bit/print.cpp
+++ b/tools/bit/print.cpp
@@ -17,6 +17,7 @@
#include "print.h"
#include <sys/ioctl.h>
+#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
diff --git a/tools/bit/util.h b/tools/bit/util.h
index 7ccdab1..8c66911 100644
--- a/tools/bit/util.h
+++ b/tools/bit/util.h
@@ -17,6 +17,8 @@
#ifndef UTIL_H
#define UTIL_H
+#include <sys/types.h>
+
#include <map>
#include <string>
#include <vector>
diff --git a/tools/streaming_proto/Errors.cpp b/tools/streaming_proto/Errors.cpp
index 0cd9037..6890d99 100644
--- a/tools/streaming_proto/Errors.cpp
+++ b/tools/streaming_proto/Errors.cpp
@@ -1,5 +1,6 @@
#include "Errors.h"
+#include <stdarg.h>
#include <stdlib.h>
namespace android {