diff options
201 files changed, 3212 insertions, 12594 deletions
diff --git a/Android.bp b/Android.bp index 5bd332947057..9d48cf81e998 100644 --- a/Android.bp +++ b/Android.bp @@ -97,7 +97,7 @@ filegroup { ":platform-compat-native-aidl", // AIDL sources from external directories - ":android.hardware.security.keymint-V1-java-source", + ":android.hardware.security.keymint-V2-java-source", ":android.hardware.security.secureclock-V1-java-source", ":android.security.apc-java-source", ":android.security.authorization-java-source", @@ -175,7 +175,6 @@ java_defaults { "sax/java", "telecomm/java", - "apex/media/aidl/stable", // TODO(b/147699819): remove this "telephony/java", ], @@ -274,6 +273,7 @@ java_defaults { // TODO: remove when moved to the below package "frameworks/base/packages/ConnectivityT/framework-t/aidl-export", "packages/modules/Connectivity/framework/aidl-export", + "packages/modules/Media/apex/aidl/stable", ], }, dxflags: [ @@ -449,21 +449,28 @@ java_library { } // TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp -metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " + - "--hide-package com.android.server " + - "--hide-package android.audio.policy.configuration.V7_0 " + - "--error UnhiddenSystemApi " + - "--hide RequiresPermission " + - "--hide CallbackInterface " + - "--hide MissingPermission --hide BroadcastBehavior " + - "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + - "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " + - "--error NoSettingsProvider " + - "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " + +metalava_framework_docs_args = "" + "--api-lint-ignore-prefix android.icu. " + "--api-lint-ignore-prefix java. " + "--api-lint-ignore-prefix junit. " + - "--api-lint-ignore-prefix org. " + "--api-lint-ignore-prefix org. " + + "--error NoSettingsProvider " + + "--error UnhiddenSystemApi " + + "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " + + "--hide BroadcastBehavior " + + "--hide CallbackInterface " + + "--hide DeprecationMismatch " + + "--hide HiddenSuperclass " + + "--hide HiddenTypeParameter " + + "--hide MissingPermission " + + "--hide-package android.audio.policy.configuration.V7_0 " + + "--hide-package com.android.server " + + "--hide RequiresPermission " + + "--hide SdkConstant " + + "--hide Todo " + + "--hide Typo " + + "--hide UnavailableSymbol " + + "--manifest $(location core/res/AndroidManifest.xml) " packages_to_document = [ "android", @@ -549,16 +556,15 @@ stubs_defaults { stubs_defaults { name: "module-classpath-stubs-defaults", aidl: { - local_include_dirs: [ - "apex/media/aidl/stable", - ], include_dirs: [ "packages/modules/Connectivity/framework/aidl-export", + "packages/modules/Media/apex/aidl/stable", ], }, libs: [ "art.module.public.api", "sdk_module-lib_current_framework-tethering", + "sdk_module-lib_current_framework-connectivity-t", "sdk_public_current_framework-bluetooth", // There are a few classes from modules used by the core that // need to be resolved by metalava. We use a prebuilt stub of the diff --git a/ApiDocs.bp b/ApiDocs.bp index 5595e95631ad..2efeab6da670 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -140,11 +140,9 @@ droidstubs { "api-versions-jars-dir", ], aidl: { - local_include_dirs: [ - "apex/media/aidl/stable", - ], include_dirs: [ "packages/modules/Connectivity/framework/aidl-export", + "packages/modules/Media/apex/aidl/stable", ], }, } diff --git a/StubLibraries.bp b/StubLibraries.bp index 5cb0a785bc2b..fef95e813ec7 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -250,6 +250,7 @@ java_library { srcs: [":module-lib-api-stubs-docs-non-updatable"], libs: [ "sdk_module-lib_current_framework-tethering", + "sdk_module-lib_current_framework-connectivity-t", "sdk_public_current_framework-bluetooth", // NOTE: The below can be removed once the prebuilt stub contains bluetooth. "sdk_system_current_android", @@ -365,6 +366,64 @@ java_library { }, } +//////////////////////////////////////////////////////////////////////// +// api-versions.xml generation, for public and system. This API database +// also contains the android.test.* APIs. +//////////////////////////////////////////////////////////////////////// + +java_library { + name: "android_stubs_current_with_test_libs", + static_libs: [ + "android_stubs_current", + "android.test.base.stubs", + "android.test.mock.stubs", + "android.test.runner.stubs", + ], + defaults: ["android.jar_defaults"], + visibility: [ + "//visibility:override", + "//visibility:private", + ], +} + +java_library { + name: "android_system_stubs_current_with_test_libs", + static_libs: [ + "android_system_stubs_current", + "android.test.base.stubs.system", + "android.test.mock.stubs.system", + "android.test.runner.stubs.system", + ], + defaults: ["android.jar_defaults"], + visibility: [ + "//visibility:override", + "//visibility:private", + ], +} + +droidstubs { + name: "api_versions_public", + srcs: [":android_stubs_current_with_test_libs{.jar}"], + generate_stubs: false, + api_levels_annotations_enabled: true, + api_levels_annotations_dirs: [ + "sdk-dir", + "api-versions-jars-dir", + ], +} + +droidstubs { + name: "api_versions_system", + srcs: [":android_system_stubs_current_with_test_libs{.jar}"], + generate_stubs: false, + api_levels_annotations_enabled: true, + api_levels_annotations_dirs: [ + "sdk-dir", + "api-versions-jars-dir", + ], + api_levels_sdk_type: "system", +} + ///////////////////////////////////////////////////////////////////// // hwbinder.stubs provides APIs required for building HIDL Java // libraries. diff --git a/apex/media/Android.bp b/apex/media/Android.bp deleted file mode 100644 index 1a710a98b0a9..000000000000 --- a/apex/media/Android.bp +++ /dev/null @@ -1,35 +0,0 @@ -// 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 { - default_visibility: [ - ":__subpackages__", - "//frameworks/av/apex", - "//frameworks/av/apex/testing", - ], - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -sdk { - name: "media-module-sdk", - bootclasspath_fragments: ["com.android.media-bootclasspath-fragment"], - systemserverclasspath_fragments: ["com.android.media-systemserverclasspath-fragment"], - java_sdk_libs: [ - "framework-media", - ], -} diff --git a/apex/media/OWNERS b/apex/media/OWNERS deleted file mode 100644 index 2c5965c300e3..000000000000 --- a/apex/media/OWNERS +++ /dev/null @@ -1,12 +0,0 @@ -# Bug component: 1344 -hdmoon@google.com -jinpark@google.com -klhyun@google.com -lnilsson@google.com -sungsoo@google.com - -# go/android-fwk-media-solutions for info on areas of ownership. -include platform/frameworks/av:/media/janitors/media_solutions_OWNERS - -# media reliability team packages/delivers the media mainline builds. -include platform/frameworks/av:/media/janitors/reliability_mainline_OWNERS diff --git a/apex/media/aidl/Android.bp b/apex/media/aidl/Android.bp deleted file mode 100644 index 545a0cd884dc..000000000000 --- a/apex/media/aidl/Android.bp +++ /dev/null @@ -1,44 +0,0 @@ -// -// Copyright 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -filegroup { - name: "stable-media-aidl-srcs", - srcs: ["stable/**/*.aidl"], - path: "stable", -} - -filegroup { - name: "private-media-aidl-srcs", - srcs: ["private/**/I*.aidl"], - path: "private", -} - -filegroup { - name: "media-aidl-srcs", - srcs: [ - ":private-media-aidl-srcs", - ":stable-media-aidl-srcs", - ], -} diff --git a/apex/media/aidl/private/android/media/Controller2Link.aidl b/apex/media/aidl/private/android/media/Controller2Link.aidl deleted file mode 100644 index 64edafcb11fc..000000000000 --- a/apex/media/aidl/private/android/media/Controller2Link.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2018 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.media; - -parcelable Controller2Link; diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl deleted file mode 100644 index fb3172b8c764..000000000000 --- a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 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.media; - -import android.media.Session2Token; -import android.media.IMediaCommunicationServiceCallback; -import android.media.MediaParceledListSlice; - -/** {@hide} */ -interface IMediaCommunicationService { - void notifySession2Created(in Session2Token sessionToken); - boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); - MediaParceledListSlice getSession2Tokens(int userId); - - void registerCallback(IMediaCommunicationServiceCallback callback, String packageName); - void unregisterCallback(IMediaCommunicationServiceCallback callback); -} - diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl deleted file mode 100644 index e347ebf4a983..000000000000 --- a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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.media; - -import android.media.Session2Token; -import android.media.MediaParceledListSlice; - -/** {@hide} */ -oneway interface IMediaCommunicationServiceCallback { - void onSession2Created(in Session2Token token); - void onSession2Changed(in MediaParceledListSlice tokens); -} - diff --git a/apex/media/aidl/private/android/media/IMediaController2.aidl b/apex/media/aidl/private/android/media/IMediaController2.aidl deleted file mode 100644 index 42c6e70529ec..000000000000 --- a/apex/media/aidl/private/android/media/IMediaController2.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2018 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.media; - -import android.os.Bundle; -import android.os.ResultReceiver; -import android.media.Session2Command; - -/** - * Interface from MediaSession2 to MediaController2. - * <p> - * Keep this interface oneway. Otherwise a malicious app may implement fake version of this, - * and holds calls from session to make session owner(s) frozen. - * @hide - */ - // Code for AML only -oneway interface IMediaController2 { - void notifyConnected(int seq, in Bundle connectionResult) = 0; - void notifyDisconnected(int seq) = 1; - void notifyPlaybackActiveChanged(int seq, boolean playbackActive) = 2; - void sendSessionCommand(int seq, in Session2Command command, in Bundle args, - in ResultReceiver resultReceiver) = 3; - void cancelSessionCommand(int seq) = 4; - // Next Id : 5 -} diff --git a/apex/media/aidl/private/android/media/IMediaSession2.aidl b/apex/media/aidl/private/android/media/IMediaSession2.aidl deleted file mode 100644 index 26e717b39afc..000000000000 --- a/apex/media/aidl/private/android/media/IMediaSession2.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2018 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.media; - -import android.os.Bundle; -import android.os.ResultReceiver; -import android.media.Controller2Link; -import android.media.Session2Command; - -/** - * Interface from MediaController2 to MediaSession2. - * <p> - * Keep this interface oneway. Otherwise a malicious app may implement fake version of this, - * and holds calls from session to make session owner(s) frozen. - * @hide - */ - // Code for AML only -oneway interface IMediaSession2 { - void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0; - void disconnect(in Controller2Link caller, int seq) = 1; - void sendSessionCommand(in Controller2Link caller, int seq, in Session2Command sessionCommand, - in Bundle args, in ResultReceiver resultReceiver) = 2; - void cancelSessionCommand(in Controller2Link caller, int seq) = 3; - // Next Id : 4 -} diff --git a/apex/media/aidl/private/android/media/IMediaSession2Service.aidl b/apex/media/aidl/private/android/media/IMediaSession2Service.aidl deleted file mode 100644 index 10ac1be0a36e..000000000000 --- a/apex/media/aidl/private/android/media/IMediaSession2Service.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2019 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.media; - -import android.os.Bundle; -import android.media.Controller2Link; - -/** - * Interface from MediaController2 to MediaSession2Service. - * <p> - * Keep this interface oneway. Otherwise a malicious app may implement fake version of this, - * and holds calls from controller to make controller owner(s) frozen. - * @hide - */ -oneway interface IMediaSession2Service { - void connect(in Controller2Link caller, int seq, in Bundle connectionRequest) = 0; - // Next Id : 1 -} diff --git a/apex/media/aidl/private/android/media/Session2Command.aidl b/apex/media/aidl/private/android/media/Session2Command.aidl deleted file mode 100644 index 43a7b123ed29..000000000000 --- a/apex/media/aidl/private/android/media/Session2Command.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2018 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.media; - -parcelable Session2Command; diff --git a/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl b/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl deleted file mode 100644 index 92d673fd25cb..000000000000 --- a/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 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.media; - -parcelable MediaParceledListSlice<T>; diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp deleted file mode 100644 index 6195f257ecd9..000000000000 --- a/apex/media/framework/Android.bp +++ /dev/null @@ -1,164 +0,0 @@ -// 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -java_library { - name: "updatable-media", - - srcs: [ - ":updatable-media-srcs", - ], - - permitted_packages: [ - "android.media", - ], - - optimize: { - enabled: true, - shrink: true, - proguard_flags_files: ["updatable-media-proguard.flags"], - }, - - installable: true, - - sdk_version: "module_current", - libs: [ - "androidx.annotation_annotation", - "framework-annotations-lib", - ], - static_libs: [ - "exoplayer2-extractor", - "mediatranscoding_aidl_interface-java", - "modules-annotation-minsdk", - "modules-utils-build", - ], - jarjar_rules: "jarjar_rules.txt", - - plugins: ["java_api_finder"], - - hostdex: true, // for hiddenapi check - apex_available: [ - "com.android.media", - "test_com.android.media", - ], - min_sdk_version: "29", - visibility: [ - "//frameworks/av/apex:__subpackages__", - "//frameworks/base/apex/media/service", - "//frameworks/base/api", // For framework-all - ], -} - -filegroup { - name: "updatable-media-srcs", - srcs: [ - "java/android/media/MediaFrameworkInitializer.java", - ":media-aidl-srcs", - ":mediaparceledlistslice-java-srcs", - ":mediaparser-srcs", - ":mediasession2-java-srcs", - ":mediatranscoding-srcs", - ], - visibility: ["//frameworks/base"], -} - -filegroup { - name: "mediasession2-java-srcs", - srcs: [ - "java/android/media/Controller2Link.java", - "java/android/media/MediaConstants.java", - "java/android/media/MediaController2.java", - "java/android/media/MediaSession2.java", - "java/android/media/MediaSession2Service.java", - "java/android/media/Session2Command.java", - "java/android/media/Session2CommandGroup.java", - "java/android/media/Session2Link.java", - "java/android/media/Session2Token.java", - "java/android/media/MediaCommunicationManager.java", - ], - path: "java", -} - -filegroup { - name: "mediaparceledlistslice-java-srcs", - srcs: [ - "java/android/media/MediaParceledListSlice.java", - "java/android/media/BaseMediaParceledListSlice.java", - ], - path: "java", -} - -filegroup { - name: "mediaparser-srcs", - srcs: [ - "java/android/media/MediaParser.java", - ], - path: "java", -} - -filegroup { - name: "mediatranscoding-srcs", - srcs: [ - "java/android/media/ApplicationMediaCapabilities.java", - "java/android/media/MediaFeature.java", - "java/android/media/MediaTranscodingManager.java", - ], - path: "java", -} - -java_sdk_library { - name: "framework-media", - defaults: ["framework-module-defaults"], - - // This is only used to define the APIs for updatable-media. - api_only: true, - - srcs: [ - ":updatable-media-srcs", - ], - - impl_library_visibility: ["//frameworks/av/apex:__subpackages__"], -} - -cc_library_shared { - name: "libmediaparser-jni", - srcs: [ - "jni/android_media_MediaParserJNI.cpp", - ], - header_libs: ["jni_headers"], - shared_libs: [ - "libandroid", - "liblog", - "libmediametrics", - ], - cflags: [ - "-Wall", - "-Werror", - "-Wno-unused-parameter", - "-Wunreachable-code", - "-Wunused", - ], - apex_available: [ - "com.android.media", - ], - min_sdk_version: "29", -} diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING deleted file mode 100644 index 3d2191410413..000000000000 --- a/apex/media/framework/TEST_MAPPING +++ /dev/null @@ -1,10 +0,0 @@ -{ - "presubmit": [ - { - "name": "CtsMediaParserTestCases" - }, - { - "name": "CtsMediaParserHostTestCases" - } - ] -} diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt deleted file mode 100644 index b7d7ed866c89..000000000000 --- a/apex/media/framework/api/current.txt +++ /dev/null @@ -1,267 +0,0 @@ -// Signature format: 2.0 -package android.media { - - public final class ApplicationMediaCapabilities implements android.os.Parcelable { - method @NonNull public static android.media.ApplicationMediaCapabilities createFromXml(@NonNull org.xmlpull.v1.XmlPullParser); - method public int describeContents(); - method @NonNull public java.util.List<java.lang.String> getSupportedHdrTypes(); - method @NonNull public java.util.List<java.lang.String> getSupportedVideoMimeTypes(); - method @NonNull public java.util.List<java.lang.String> getUnsupportedHdrTypes(); - method @NonNull public java.util.List<java.lang.String> getUnsupportedVideoMimeTypes(); - method public boolean isFormatSpecified(@NonNull String); - method public boolean isHdrTypeSupported(@NonNull String); - method public boolean isVideoMimeTypeSupported(@NonNull String); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.ApplicationMediaCapabilities> CREATOR; - } - - public static final class ApplicationMediaCapabilities.Builder { - ctor public ApplicationMediaCapabilities.Builder(); - method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedHdrType(@NonNull String); - method @NonNull public android.media.ApplicationMediaCapabilities.Builder addSupportedVideoMimeType(@NonNull String); - method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedHdrType(@NonNull String); - method @NonNull public android.media.ApplicationMediaCapabilities.Builder addUnsupportedVideoMimeType(@NonNull String); - method @NonNull public android.media.ApplicationMediaCapabilities build(); - } - - public class MediaCommunicationManager { - method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens(); - method @IntRange(from=1) public int getVersion(); - } - - public class MediaController2 implements java.lang.AutoCloseable { - method public void cancelSessionCommand(@NonNull Object); - method public void close(); - method @Nullable public android.media.Session2Token getConnectedToken(); - method public boolean isPlaybackActive(); - method @NonNull public Object sendSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle); - } - - public static final class MediaController2.Builder { - ctor public MediaController2.Builder(@NonNull android.content.Context, @NonNull android.media.Session2Token); - method @NonNull public android.media.MediaController2 build(); - method @NonNull public android.media.MediaController2.Builder setConnectionHints(@NonNull android.os.Bundle); - method @NonNull public android.media.MediaController2.Builder setControllerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaController2.ControllerCallback); - } - - public abstract static class MediaController2.ControllerCallback { - ctor public MediaController2.ControllerCallback(); - method public void onCommandResult(@NonNull android.media.MediaController2, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result); - method public void onConnected(@NonNull android.media.MediaController2, @NonNull android.media.Session2CommandGroup); - method public void onDisconnected(@NonNull android.media.MediaController2); - method public void onPlaybackActiveChanged(@NonNull android.media.MediaController2, boolean); - method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaController2, @NonNull android.media.Session2Command, @Nullable android.os.Bundle); - } - - public final class MediaFeature { - ctor public MediaFeature(); - } - - public static final class MediaFeature.HdrType { - field public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision"; - field public static final String HDR10 = "android.media.feature.hdr.hdr10"; - field public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus"; - field public static final String HLG = "android.media.feature.hdr.hlg"; - } - - public final class MediaParser { - method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException; - method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...); - method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer); - method @NonNull public android.media.metrics.LogSessionId getLogSessionId(); - method @NonNull public String getParserName(); - method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat); - method public void release(); - method public void seek(@NonNull android.media.MediaParser.SeekPoint); - method public void setLogSessionId(@NonNull android.media.metrics.LogSessionId); - method @NonNull public android.media.MediaParser setParameter(@NonNull String, @NonNull Object); - method public boolean supportsParameter(@NonNull String); - field public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = "android.media.mediaparser.adts.enableCbrSeeking"; - field public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "android.media.mediaparser.amr.enableCbrSeeking"; - field public static final String PARAMETER_FLAC_DISABLE_ID3 = "android.media.mediaparser.flac.disableId3"; - field public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = "android.media.mediaparser.matroska.disableCuesSeeking"; - field public static final String PARAMETER_MP3_DISABLE_ID3 = "android.media.mediaparser.mp3.disableId3"; - field public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "android.media.mediaparser.mp3.enableCbrSeeking"; - field public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = "android.media.mediaparser.mp3.enableIndexSeeking"; - field public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "android.media.mediaparser.mp4.ignoreEditLists"; - field public static final String PARAMETER_MP4_IGNORE_TFDT_BOX = "android.media.mediaparser.mp4.ignoreTfdtBox"; - field public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes"; - field public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = "android.media.mediaparser.ts.allowNonIdrAvcKeyframes"; - field public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = "android.media.mediaparser.ts.ignoreDetectAccessUnits"; - field public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = "android.media.mediaparser.ts.enableHdmvDtsAudioStreams"; - field public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "android.media.mediaparser.ts.ignoreAacStream"; - field public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "android.media.mediaparser.ts.ignoreAvcStream"; - field public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = "android.media.mediaparser.ts.ignoreSpliceInfoStream"; - field public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode"; - field public static final String PARSER_NAME_AC3 = "android.media.mediaparser.Ac3Parser"; - field public static final String PARSER_NAME_AC4 = "android.media.mediaparser.Ac4Parser"; - field public static final String PARSER_NAME_ADTS = "android.media.mediaparser.AdtsParser"; - field public static final String PARSER_NAME_AMR = "android.media.mediaparser.AmrParser"; - field public static final String PARSER_NAME_FLAC = "android.media.mediaparser.FlacParser"; - field public static final String PARSER_NAME_FLV = "android.media.mediaparser.FlvParser"; - field public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser"; - field public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser"; - field public static final String PARSER_NAME_MP3 = "android.media.mediaparser.Mp3Parser"; - field public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser"; - field public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser"; - field public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser"; - field public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser"; - field public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN"; - field public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser"; - field public static final int SAMPLE_FLAG_DECODE_ONLY = -2147483648; // 0x80000000 - field public static final int SAMPLE_FLAG_ENCRYPTED = 1073741824; // 0x40000000 - field public static final int SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA = 268435456; // 0x10000000 - field public static final int SAMPLE_FLAG_KEY_FRAME = 1; // 0x1 - field public static final int SAMPLE_FLAG_LAST_SAMPLE = 536870912; // 0x20000000 - } - - public static interface MediaParser.InputReader { - method public long getLength(); - method public long getPosition(); - method public int read(@NonNull byte[], int, int) throws java.io.IOException; - } - - public static interface MediaParser.OutputConsumer { - method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo); - method public void onSampleDataFound(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException; - method public void onSeekMapFound(@NonNull android.media.MediaParser.SeekMap); - method public void onTrackCountFound(int); - method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData); - } - - public static final class MediaParser.ParsingException extends java.io.IOException { - } - - public static final class MediaParser.SeekMap { - method public long getDurationMicros(); - method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long); - method public boolean isSeekable(); - field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000 - } - - public static final class MediaParser.SeekPoint { - field @NonNull public static final android.media.MediaParser.SeekPoint START; - field public final long position; - field public final long timeMicros; - } - - public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader { - method public void seekToPosition(long); - } - - public static final class MediaParser.TrackData { - field @Nullable public final android.media.DrmInitData drmInitData; - field @NonNull public final android.media.MediaFormat mediaFormat; - } - - public static final class MediaParser.UnrecognizedInputFormatException extends java.io.IOException { - } - - public class MediaSession2 implements java.lang.AutoCloseable { - method public void broadcastSessionCommand(@NonNull android.media.Session2Command, @Nullable android.os.Bundle); - method public void cancelSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object); - method public void close(); - method @NonNull public java.util.List<android.media.MediaSession2.ControllerInfo> getConnectedControllers(); - method @NonNull public String getId(); - method @NonNull public android.media.Session2Token getToken(); - method public boolean isPlaybackActive(); - method @NonNull public Object sendSessionCommand(@NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle); - method public void setPlaybackActive(boolean); - } - - public static final class MediaSession2.Builder { - ctor public MediaSession2.Builder(@NonNull android.content.Context); - method @NonNull public android.media.MediaSession2 build(); - method @NonNull public android.media.MediaSession2.Builder setExtras(@NonNull android.os.Bundle); - method @NonNull public android.media.MediaSession2.Builder setId(@NonNull String); - method @NonNull public android.media.MediaSession2.Builder setSessionActivity(@Nullable android.app.PendingIntent); - method @NonNull public android.media.MediaSession2.Builder setSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaSession2.SessionCallback); - } - - public static final class MediaSession2.ControllerInfo { - method @NonNull public android.os.Bundle getConnectionHints(); - method @NonNull public String getPackageName(); - method @NonNull public android.media.session.MediaSessionManager.RemoteUserInfo getRemoteUserInfo(); - method public int getUid(); - } - - public abstract static class MediaSession2.SessionCallback { - ctor public MediaSession2.SessionCallback(); - method public void onCommandResult(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull Object, @NonNull android.media.Session2Command, @NonNull android.media.Session2Command.Result); - method @Nullable public android.media.Session2CommandGroup onConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo); - method public void onDisconnected(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo); - method public void onPostConnect(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo); - method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle); - } - - public abstract class MediaSession2Service extends android.app.Service { - ctor public MediaSession2Service(); - method public final void addSession(@NonNull android.media.MediaSession2); - method @NonNull public final java.util.List<android.media.MediaSession2> getSessions(); - method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); - method @Nullable public abstract android.media.MediaSession2 onGetSession(@NonNull android.media.MediaSession2.ControllerInfo); - method @Nullable public abstract android.media.MediaSession2Service.MediaNotification onUpdateNotification(@NonNull android.media.MediaSession2); - method public final void removeSession(@NonNull android.media.MediaSession2); - field public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service"; - } - - public static class MediaSession2Service.MediaNotification { - ctor public MediaSession2Service.MediaNotification(int, @NonNull android.app.Notification); - method @NonNull public android.app.Notification getNotification(); - method public int getNotificationId(); - } - - public final class Session2Command implements android.os.Parcelable { - ctor public Session2Command(int); - ctor public Session2Command(@NonNull String, @Nullable android.os.Bundle); - method public int describeContents(); - method public int getCommandCode(); - method @Nullable public String getCustomAction(); - method @Nullable public android.os.Bundle getCustomExtras(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int COMMAND_CODE_CUSTOM = 0; // 0x0 - field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Command> CREATOR; - } - - public static final class Session2Command.Result { - ctor public Session2Command.Result(int, @Nullable android.os.Bundle); - method public int getResultCode(); - method @Nullable public android.os.Bundle getResultData(); - field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff - field public static final int RESULT_INFO_SKIPPED = 1; // 0x1 - field public static final int RESULT_SUCCESS = 0; // 0x0 - } - - public final class Session2CommandGroup implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.Set<android.media.Session2Command> getCommands(); - method public boolean hasCommand(@NonNull android.media.Session2Command); - method public boolean hasCommand(int); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2CommandGroup> CREATOR; - } - - public static final class Session2CommandGroup.Builder { - ctor public Session2CommandGroup.Builder(); - ctor public Session2CommandGroup.Builder(@NonNull android.media.Session2CommandGroup); - method @NonNull public android.media.Session2CommandGroup.Builder addCommand(@NonNull android.media.Session2Command); - method @NonNull public android.media.Session2CommandGroup build(); - method @NonNull public android.media.Session2CommandGroup.Builder removeCommand(@NonNull android.media.Session2Command); - } - - public final class Session2Token implements android.os.Parcelable { - ctor public Session2Token(@NonNull android.content.Context, @NonNull android.content.ComponentName); - method public int describeContents(); - method @NonNull public android.os.Bundle getExtras(); - method @NonNull public String getPackageName(); - method @Nullable public String getServiceName(); - method public int getType(); - method public int getUid(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.Session2Token> CREATOR; - field public static final int TYPE_SESSION = 0; // 0x0 - field public static final int TYPE_SESSION_SERVICE = 1; // 0x1 - } - -} - diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt deleted file mode 100644 index eb6397a1826b..000000000000 --- a/apex/media/framework/api/module-lib-current.txt +++ /dev/null @@ -1,30 +0,0 @@ -// Signature format: 2.0 -package android.media { - - public class MediaCommunicationManager { - method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaCommunicationManager.SessionCallback); - method public void unregisterSessionCallback(@NonNull android.media.MediaCommunicationManager.SessionCallback); - } - - public static interface MediaCommunicationManager.SessionCallback { - method public default void onSession2TokenCreated(@NonNull android.media.Session2Token); - method public default void onSession2TokensChanged(@NonNull java.util.List<android.media.Session2Token>); - } - - public class MediaFrameworkInitializer { - method public static void registerServiceWrappers(); - method public static void setMediaServiceManager(@NonNull android.media.MediaServiceManager); - } - - @Deprecated public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable { - ctor @Deprecated public MediaParceledListSlice(@NonNull java.util.List<T>); - method @Deprecated public int describeContents(); - method @Deprecated @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList(); - method @Deprecated public java.util.List<T> getList(); - method @Deprecated public void setInlineCountLimit(int); - method @Deprecated public void writeToParcel(android.os.Parcel, int); - field @Deprecated @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR; - } - -} - diff --git a/apex/media/framework/api/module-lib-removed.txt b/apex/media/framework/api/module-lib-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/media/framework/api/module-lib-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/media/framework/api/removed.txt b/apex/media/framework/api/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/media/framework/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt deleted file mode 100644 index 6eea769d9f57..000000000000 --- a/apex/media/framework/api/system-current.txt +++ /dev/null @@ -1,68 +0,0 @@ -// Signature format: 2.0 -package android.media { - - public final class MediaTranscodingManager { - method @Nullable public android.media.MediaTranscodingManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodingManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodingManager.OnTranscodingFinishedListener); - } - - @java.lang.FunctionalInterface public static interface MediaTranscodingManager.OnTranscodingFinishedListener { - method public void onTranscodingFinished(@NonNull android.media.MediaTranscodingManager.TranscodingSession); - } - - public abstract static class MediaTranscodingManager.TranscodingRequest { - method public int getClientPid(); - method public int getClientUid(); - method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor(); - method @NonNull public android.net.Uri getDestinationUri(); - method @Nullable public android.os.ParcelFileDescriptor getSourceFileDescriptor(); - method @NonNull public android.net.Uri getSourceUri(); - } - - public static class MediaTranscodingManager.TranscodingRequest.VideoFormatResolver { - ctor public MediaTranscodingManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat); - method @Nullable public android.media.MediaFormat resolveVideoFormat(); - method public boolean shouldTranscode(); - } - - public static final class MediaTranscodingManager.TranscodingSession { - method public boolean addClientUid(int); - method public void cancel(); - method @NonNull public java.util.List<java.lang.Integer> getClientUids(); - method public int getErrorCode(); - method @IntRange(from=0, to=100) public int getProgress(); - method public int getResult(); - method public int getSessionId(); - method public int getStatus(); - method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener); - field public static final int ERROR_DROPPED_BY_SERVICE = 1; // 0x1 - field public static final int ERROR_NONE = 0; // 0x0 - field public static final int ERROR_SERVICE_DIED = 2; // 0x2 - field public static final int RESULT_CANCELED = 4; // 0x4 - field public static final int RESULT_ERROR = 3; // 0x3 - field public static final int RESULT_NONE = 1; // 0x1 - field public static final int RESULT_SUCCESS = 2; // 0x2 - field public static final int STATUS_FINISHED = 3; // 0x3 - field public static final int STATUS_PAUSED = 4; // 0x4 - field public static final int STATUS_PENDING = 1; // 0x1 - field public static final int STATUS_RUNNING = 2; // 0x2 - } - - @java.lang.FunctionalInterface public static interface MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener { - method public void onProgressUpdate(@NonNull android.media.MediaTranscodingManager.TranscodingSession, @IntRange(from=0, to=100) int); - } - - public static final class MediaTranscodingManager.VideoTranscodingRequest extends android.media.MediaTranscodingManager.TranscodingRequest { - method @NonNull public android.media.MediaFormat getVideoTrackFormat(); - } - - public static final class MediaTranscodingManager.VideoTranscodingRequest.Builder { - ctor public MediaTranscodingManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat); - method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest build(); - method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientPid(int); - method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientUid(int); - method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor); - method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor); - } - -} - diff --git a/apex/media/framework/api/system-removed.txt b/apex/media/framework/api/system-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/media/framework/api/system-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt deleted file mode 100644 index 91489dcee0a1..000000000000 --- a/apex/media/framework/jarjar_rules.txt +++ /dev/null @@ -1,2 +0,0 @@ -rule com.android.modules.** android.media.internal.@1 -rule com.google.android.exoplayer2.** android.media.internal.exo.@1 diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java deleted file mode 100644 index 97fa0eca0862..000000000000 --- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java +++ /dev/null @@ -1,626 +0,0 @@ -/* - * 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.media; - -import android.annotation.NonNull; -import android.content.ContentResolver; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import com.android.modules.annotation.MinSdk; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities - for handling newer video codec format and media features. - - <p> - Android 12 introduces Compatible media transcoding feature. See - <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding"> - Compatible media transcoding</a>. By default, Android assumes apps can support playback of all - media formats. Apps that would like to request that media be transcoded into a more compatible - format should declare their media capabilities in a media_capabilities.xml resource file and add it - as a property tag in the AndroidManifest.xml file. Here is a example: - <pre> - {@code - <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android"> - <format android:name="HEVC" supported="true"/> - <format android:name="HDR10" supported="false"/> - <format android:name="HDR10Plus" supported="false"/> - </media-capabilities> - } - </pre> - The ApplicationMediaCapabilities class is generated from this xml and used by the platform to - represent an application's media capabilities in order to determine whether modern media files need - to be transcoded for that application. - </p> - - <p> - ApplicationMediaCapabilities objects can also be built by applications at runtime for use with - {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more - control over the transcoding that is built into the platform. ApplicationMediaCapabilities - provided by applications at runtime like this override the default manifest capabilities for that - media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or - through the builder class {@link ApplicationMediaCapabilities.Builder} - - <h3> Video Codec Support</h3> - <p> - Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support - for newer format with this class as they are assumed to support older format like h.264. - - <h3>Capability of handling HDR(high dynamic range) video</h3> - <p> - There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform, - application will only need to specify individual types they supported. - */ -@MinSdk(Build.VERSION_CODES.S) -public final class ApplicationMediaCapabilities implements Parcelable { - private static final String TAG = "ApplicationMediaCapabilities"; - - /** List of supported video codec mime types. */ - private Set<String> mSupportedVideoMimeTypes = new HashSet<>(); - - /** List of unsupported video codec mime types. */ - private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>(); - - /** List of supported hdr types. */ - private Set<String> mSupportedHdrTypes = new HashSet<>(); - - /** List of unsupported hdr types. */ - private Set<String> mUnsupportedHdrTypes = new HashSet<>(); - - private boolean mIsSlowMotionSupported = false; - - private ApplicationMediaCapabilities(Builder b) { - mSupportedVideoMimeTypes.addAll(b.getSupportedVideoMimeTypes()); - mUnsupportedVideoMimeTypes.addAll(b.getUnsupportedVideoMimeTypes()); - mSupportedHdrTypes.addAll(b.getSupportedHdrTypes()); - mUnsupportedHdrTypes.addAll(b.getUnsupportedHdrTypes()); - mIsSlowMotionSupported = b.mIsSlowMotionSupported; - } - - /** - * Query if a video codec format is supported by the application. - * <p> - * If the application has not specified supporting the format or not, this will return false. - * Use {@link #isFormatSpecified(String)} to query if a format is specified or not. - * - * @param videoMime The mime type of the video codec format. Must be the one used in - * {@link MediaFormat#KEY_MIME}. - * @return true if application supports the video codec format, false otherwise. - */ - public boolean isVideoMimeTypeSupported( - @NonNull String videoMime) { - if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) { - return true; - } - return false; - } - - /** - * Query if a HDR type is supported by the application. - * <p> - * If the application has not specified supporting the format or not, this will return false. - * Use {@link #isFormatSpecified(String)} to query if a format is specified or not. - * - * @param hdrType The type of the HDR format. - * @return true if application supports the HDR format, false otherwise. - */ - public boolean isHdrTypeSupported( - @NonNull @MediaFeature.MediaHdrType String hdrType) { - if (mSupportedHdrTypes.contains(hdrType)) { - return true; - } - return false; - } - - /** - * Query if a format is specified by the application. - * <p> - * The format could be either the video format or the hdr format. - * - * @param format The name of the format. - * @return true if application specifies the format, false otherwise. - */ - public boolean isFormatSpecified(@NonNull String format) { - if (mSupportedVideoMimeTypes.contains(format) || mUnsupportedVideoMimeTypes.contains(format) - || mSupportedHdrTypes.contains(format) || mUnsupportedHdrTypes.contains(format)) { - return true; - - } - return false; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - // Write out the supported video mime types. - dest.writeInt(mSupportedVideoMimeTypes.size()); - for (String cap : mSupportedVideoMimeTypes) { - dest.writeString(cap); - } - // Write out the unsupported video mime types. - dest.writeInt(mUnsupportedVideoMimeTypes.size()); - for (String cap : mUnsupportedVideoMimeTypes) { - dest.writeString(cap); - } - // Write out the supported hdr types. - dest.writeInt(mSupportedHdrTypes.size()); - for (String cap : mSupportedHdrTypes) { - dest.writeString(cap); - } - // Write out the unsupported hdr types. - dest.writeInt(mUnsupportedHdrTypes.size()); - for (String cap : mUnsupportedHdrTypes) { - dest.writeString(cap); - } - // Write out the supported slow motion. - dest.writeBoolean(mIsSlowMotionSupported); - } - - @Override - public String toString() { - String caps = new String( - "Supported Video MimeTypes: " + mSupportedVideoMimeTypes.toString()); - caps += "Unsupported Video MimeTypes: " + mUnsupportedVideoMimeTypes.toString(); - caps += "Supported HDR types: " + mSupportedHdrTypes.toString(); - caps += "Unsupported HDR types: " + mUnsupportedHdrTypes.toString(); - caps += "Supported slow motion: " + mIsSlowMotionSupported; - return caps; - } - - @NonNull - public static final Creator<ApplicationMediaCapabilities> CREATOR = - new Creator<ApplicationMediaCapabilities>() { - public ApplicationMediaCapabilities createFromParcel(Parcel in) { - ApplicationMediaCapabilities.Builder builder = - new ApplicationMediaCapabilities.Builder(); - - // Parse supported video codec mime types. - int count = in.readInt(); - for (int readCount = 0; readCount < count; ++readCount) { - builder.addSupportedVideoMimeType(in.readString()); - } - - // Parse unsupported video codec mime types. - count = in.readInt(); - for (int readCount = 0; readCount < count; ++readCount) { - builder.addUnsupportedVideoMimeType(in.readString()); - } - - // Parse supported hdr types. - count = in.readInt(); - for (int readCount = 0; readCount < count; ++readCount) { - builder.addSupportedHdrType(in.readString()); - } - - // Parse unsupported hdr types. - count = in.readInt(); - for (int readCount = 0; readCount < count; ++readCount) { - builder.addUnsupportedHdrType(in.readString()); - } - - boolean supported = in.readBoolean(); - builder.setSlowMotionSupported(supported); - - return builder.build(); - } - - public ApplicationMediaCapabilities[] newArray(int size) { - return new ApplicationMediaCapabilities[size]; - } - }; - - /** - * Query the video codec mime types supported by the application. - * @return List of supported video codec mime types. The list will be empty if there are none. - */ - @NonNull - public List<String> getSupportedVideoMimeTypes() { - return new ArrayList<>(mSupportedVideoMimeTypes); - } - - /** - * Query the video codec mime types that are not supported by the application. - * @return List of unsupported video codec mime types. The list will be empty if there are none. - */ - @NonNull - public List<String> getUnsupportedVideoMimeTypes() { - return new ArrayList<>(mUnsupportedVideoMimeTypes); - } - - /** - * Query all hdr types that are supported by the application. - * @return List of supported hdr types. The list will be empty if there are none. - */ - @NonNull - public List<String> getSupportedHdrTypes() { - return new ArrayList<>(mSupportedHdrTypes); - } - - /** - * Query all hdr types that are not supported by the application. - * @return List of unsupported hdr types. The list will be empty if there are none. - */ - @NonNull - public List<String> getUnsupportedHdrTypes() { - return new ArrayList<>(mUnsupportedHdrTypes); - } - - /** - * Whether handling of slow-motion video is supported - * @hide - */ - public boolean isSlowMotionSupported() { - return mIsSlowMotionSupported; - } - - /** - * Creates {@link ApplicationMediaCapabilities} from an xml. - * - * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml. - * <p> Here is an example: - * - * <pre> - * {@code - * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android"> - * <format android:name="HEVC" supported="true"/> - * <format android:name="HDR10" supported="false"/> - * <format android:name="HDR10Plus" supported="false"/> - * </media-capabilities> - * } - * </pre> - * <p> - * - * @param xmlParser The underlying {@link XmlPullParser} that will read the xml. - * @return An ApplicationMediaCapabilities object. - * @throws UnsupportedOperationException if the capabilities in xml config are invalid or - * incompatible. - */ - // TODO: Add developer.android.com link for the format of the xml. - @NonNull - public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) { - ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder(); - builder.parseXml(xmlParser); - return builder.build(); - } - - /** - * Builder class for {@link ApplicationMediaCapabilities} objects. - * Use this class to configure and create an ApplicationMediaCapabilities instance. Builder - * could be created from an existing ApplicationMediaCapabilities object, from a xml file or - * MediaCodecList. - * //TODO(hkuang): Add xml parsing support to the builder. - */ - public final static class Builder { - /** List of supported video codec mime types. */ - private Set<String> mSupportedVideoMimeTypes = new HashSet<>(); - - /** List of supported hdr types. */ - private Set<String> mSupportedHdrTypes = new HashSet<>(); - - /** List of unsupported video codec mime types. */ - private Set<String> mUnsupportedVideoMimeTypes = new HashSet<>(); - - /** List of unsupported hdr types. */ - private Set<String> mUnsupportedHdrTypes = new HashSet<>(); - - private boolean mIsSlowMotionSupported = false; - - /* Map to save the format read from the xml. */ - private Map<String, Boolean> mFormatSupportedMap = new HashMap<String, Boolean>(); - - /** - * Constructs a new Builder with all the supports default to false. - */ - public Builder() { - } - - private void parseXml(@NonNull XmlPullParser xmlParser) - throws UnsupportedOperationException { - if (xmlParser == null) { - throw new IllegalArgumentException("XmlParser must not be null"); - } - - try { - while (xmlParser.next() != XmlPullParser.START_TAG) { - continue; - } - - // Validates the tag is "media-capabilities". - if (!xmlParser.getName().equals("media-capabilities")) { - throw new UnsupportedOperationException("Invalid tag"); - } - - xmlParser.next(); - while (xmlParser.getEventType() != XmlPullParser.END_TAG) { - while (xmlParser.getEventType() != XmlPullParser.START_TAG) { - if (xmlParser.getEventType() == XmlPullParser.END_DOCUMENT) { - return; - } - xmlParser.next(); - } - - // Validates the tag is "format". - if (xmlParser.getName().equals("format")) { - parseFormatTag(xmlParser); - } else { - throw new UnsupportedOperationException("Invalid tag"); - } - while (xmlParser.getEventType() != XmlPullParser.END_TAG) { - xmlParser.next(); - } - xmlParser.next(); - } - } catch (XmlPullParserException xppe) { - throw new UnsupportedOperationException("Ill-formatted xml file"); - } catch (java.io.IOException ioe) { - throw new UnsupportedOperationException("Unable to read xml file"); - } - } - - private void parseFormatTag(XmlPullParser xmlParser) { - String name = null; - String supported = null; - for (int i = 0; i < xmlParser.getAttributeCount(); i++) { - String attrName = xmlParser.getAttributeName(i); - if (attrName.equals("name")) { - name = xmlParser.getAttributeValue(i); - } else if (attrName.equals("supported")) { - supported = xmlParser.getAttributeValue(i); - } else { - throw new UnsupportedOperationException("Invalid attribute name " + attrName); - } - } - - if (name != null && supported != null) { - if (!supported.equals("true") && !supported.equals("false")) { - throw new UnsupportedOperationException( - ("Supported value must be either true or false")); - } - boolean isSupported = Boolean.parseBoolean(supported); - - // Check if the format is already found before. - if (mFormatSupportedMap.get(name) != null && mFormatSupportedMap.get(name) - != isSupported) { - throw new UnsupportedOperationException( - "Format: " + name + " has conflict supported value"); - } - - switch (name) { - case "HEVC": - if (isSupported) { - mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC); - } else { - mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_HEVC); - } - break; - case "VP9": - if (isSupported) { - mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9); - } else { - mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_VP9); - } - break; - case "AV1": - if (isSupported) { - mSupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1); - } else { - mUnsupportedVideoMimeTypes.add(MediaFormat.MIMETYPE_VIDEO_AV1); - } - break; - case "HDR10": - if (isSupported) { - mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10); - } else { - mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10); - } - break; - case "HDR10Plus": - if (isSupported) { - mSupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS); - } else { - mUnsupportedHdrTypes.add(MediaFeature.HdrType.HDR10_PLUS); - } - break; - case "Dolby-Vision": - if (isSupported) { - mSupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION); - } else { - mUnsupportedHdrTypes.add(MediaFeature.HdrType.DOLBY_VISION); - } - break; - case "HLG": - if (isSupported) { - mSupportedHdrTypes.add(MediaFeature.HdrType.HLG); - } else { - mUnsupportedHdrTypes.add(MediaFeature.HdrType.HLG); - } - break; - case "SlowMotion": - mIsSlowMotionSupported = isSupported; - break; - default: - Log.w(TAG, "Invalid format name " + name); - } - // Save the name and isSupported into the map for validate later. - mFormatSupportedMap.put(name, isSupported); - } else { - throw new UnsupportedOperationException( - "Format name and supported must both be specified"); - } - } - - /** - * Builds a {@link ApplicationMediaCapabilities} object. - * - * @return a new {@link ApplicationMediaCapabilities} instance successfully initialized - * with all the parameters set on this <code>Builder</code>. - * @throws UnsupportedOperationException if the parameters set on the - * <code>Builder</code> were incompatible, or if they - * are not supported by the - * device. - */ - @NonNull - public ApplicationMediaCapabilities build() { - Log.d(TAG, - "Building ApplicationMediaCapabilities with: (Supported HDR: " - + mSupportedHdrTypes.toString() + " Unsupported HDR: " - + mUnsupportedHdrTypes.toString() + ") (Supported Codec: " - + " " + mSupportedVideoMimeTypes.toString() + " Unsupported Codec:" - + mUnsupportedVideoMimeTypes.toString() + ") " - + mIsSlowMotionSupported); - - // If hdr is supported, application must also support hevc. - if (!mSupportedHdrTypes.isEmpty() && !mSupportedVideoMimeTypes.contains( - MediaFormat.MIMETYPE_VIDEO_HEVC)) { - throw new UnsupportedOperationException("Only support HEVC mime type"); - } - return new ApplicationMediaCapabilities(this); - } - - /** - * Adds a supported video codec mime type. - * - * @param codecMime Supported codec mime types. Must be one of the mime type defined - * in {@link MediaFormat}. - * @throws IllegalArgumentException if mime type is not valid. - */ - @NonNull - public Builder addSupportedVideoMimeType( - @NonNull String codecMime) { - mSupportedVideoMimeTypes.add(codecMime); - return this; - } - - private List<String> getSupportedVideoMimeTypes() { - return new ArrayList<>(mSupportedVideoMimeTypes); - } - - private boolean isValidVideoCodecMimeType(@NonNull String codecMime) { - if (!codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC) - && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9) - && !codecMime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AV1)) { - return false; - } - return true; - } - - /** - * Adds an unsupported video codec mime type. - * - * @param codecMime Unsupported codec mime type. Must be one of the mime type defined - * in {@link MediaFormat}. - * @throws IllegalArgumentException if mime type is not valid. - */ - @NonNull - public Builder addUnsupportedVideoMimeType( - @NonNull String codecMime) { - if (!isValidVideoCodecMimeType(codecMime)) { - throw new IllegalArgumentException("Invalid codec mime type: " + codecMime); - } - mUnsupportedVideoMimeTypes.add(codecMime); - return this; - } - - private List<String> getUnsupportedVideoMimeTypes() { - return new ArrayList<>(mUnsupportedVideoMimeTypes); - } - - /** - * Adds a supported hdr type. - * - * @param hdrType Supported hdr type. Must be one of the String defined in - * {@link MediaFeature.HdrType}. - * @throws IllegalArgumentException if hdrType is not valid. - */ - @NonNull - public Builder addSupportedHdrType( - @NonNull @MediaFeature.MediaHdrType String hdrType) { - if (!isValidVideoCodecHdrType(hdrType)) { - throw new IllegalArgumentException("Invalid hdr type: " + hdrType); - } - mSupportedHdrTypes.add(hdrType); - return this; - } - - private List<String> getSupportedHdrTypes() { - return new ArrayList<>(mSupportedHdrTypes); - } - - private boolean isValidVideoCodecHdrType(@NonNull String hdrType) { - if (!hdrType.equals(MediaFeature.HdrType.DOLBY_VISION) - && !hdrType.equals(MediaFeature.HdrType.HDR10) - && !hdrType.equals(MediaFeature.HdrType.HDR10_PLUS) - && !hdrType.equals(MediaFeature.HdrType.HLG)) { - return false; - } - return true; - } - - /** - * Adds an unsupported hdr type. - * - * @param hdrType Unsupported hdr type. Must be one of the String defined in - * {@link MediaFeature.HdrType}. - * @throws IllegalArgumentException if hdrType is not valid. - */ - @NonNull - public Builder addUnsupportedHdrType( - @NonNull @MediaFeature.MediaHdrType String hdrType) { - if (!isValidVideoCodecHdrType(hdrType)) { - throw new IllegalArgumentException("Invalid hdr type: " + hdrType); - } - mUnsupportedHdrTypes.add(hdrType); - return this; - } - - private List<String> getUnsupportedHdrTypes() { - return new ArrayList<>(mUnsupportedHdrTypes); - } - - /** - * Sets whether slow-motion video is supported. - * If an application indicates support for slow-motion, it is application's responsibility - * to parse the slow-motion videos using their own parser or using support library. - * @see android.media.MediaFormat#KEY_SLOW_MOTION_MARKERS - * @hide - */ - @NonNull - public Builder setSlowMotionSupported(boolean slowMotionSupported) { - mIsSlowMotionSupported = slowMotionSupported; - return this; - } - } -} diff --git a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java deleted file mode 100644 index fb666098301a..000000000000 --- a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 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.media; - -import android.os.Binder; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * This is a copied version of BaseParceledListSlice in framework with hidden API usages - * removed. - * - * Transfer a large list of Parcelable objects across an IPC. Splits into - * multiple transactions if needed. - * - * Caveat: for efficiency and security, all elements must be the same concrete type. - * In order to avoid writing the class name of each object, we must ensure that - * each object is the same type, or else unparceling then reparceling the data may yield - * a different result if the class name encoded in the Parcelable is a Base type. - * See b/17671747. - * - * @hide - */ -abstract class BaseMediaParceledListSlice<T> implements Parcelable { - private static String TAG = "BaseMediaParceledListSlice"; - private static boolean DEBUG = false; - - /* - * TODO get this number from somewhere else. For now set it to a quarter of - * the 1MB limit. - */ - // private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes(); - private static final int MAX_IPC_SIZE = 64 * 1024; - - private final List<T> mList; - - private int mInlineCountLimit = Integer.MAX_VALUE; - - public BaseMediaParceledListSlice(List<T> list) { - mList = list; - } - - @SuppressWarnings("unchecked") - BaseMediaParceledListSlice(Parcel p, ClassLoader loader) { - final int N = p.readInt(); - mList = new ArrayList<T>(N); - if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); - if (N <= 0) { - return; - } - - Parcelable.Creator<?> creator = readParcelableCreator(p, loader); - Class<?> listElementClass = null; - - int i = 0; - while (i < N) { - if (p.readInt() == 0) { - break; - } - - final T parcelable = readCreator(creator, p, loader); - if (listElementClass == null) { - listElementClass = parcelable.getClass(); - } else { - verifySameType(listElementClass, parcelable.getClass()); - } - - mList.add(parcelable); - - if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1)); - i++; - } - if (i >= N) { - return; - } - final IBinder retriever = p.readStrongBinder(); - while (i < N) { - if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever); - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - data.writeInt(i); - try { - retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); - } catch (RemoteException e) { - Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); - return; - } - while (i < N && reply.readInt() != 0) { - final T parcelable = readCreator(creator, reply, loader); - verifySameType(listElementClass, parcelable.getClass()); - - mList.add(parcelable); - - if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); - i++; - } - reply.recycle(); - data.recycle(); - } - } - - private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) { - if (creator instanceof Parcelable.ClassLoaderCreator<?>) { - Parcelable.ClassLoaderCreator<?> classLoaderCreator = - (Parcelable.ClassLoaderCreator<?>) creator; - return (T) classLoaderCreator.createFromParcel(p, loader); - } - return (T) creator.createFromParcel(p); - } - - private static void verifySameType(final Class<?> expected, final Class<?> actual) { - if (!actual.equals(expected)) { - throw new IllegalArgumentException("Can't unparcel type " - + (actual == null ? null : actual.getName()) + " in list of type " - + (expected == null ? null : expected.getName())); - } - } - - public List<T> getList() { - return mList; - } - - /** - * Set a limit on the maximum number of entries in the array that will be included - * inline in the initial parcelling of this object. - */ - public void setInlineCountLimit(int maxCount) { - mInlineCountLimit = maxCount; - } - - /** - * Write this to another Parcel. Note that this discards the internal Parcel - * and should not be used anymore. This is so we can pass this to a Binder - * where we won't have a chance to call recycle on this. - */ - @Override - public void writeToParcel(Parcel dest, int flags) { - final int N = mList.size(); - final int callFlags = flags; - dest.writeInt(N); - if (DEBUG) Log.d(TAG, "Writing " + N + " items"); - if (N > 0) { - final Class<?> listElementClass = mList.get(0).getClass(); - writeParcelableCreator(mList.get(0), dest); - int i = 0; - while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) { - dest.writeInt(1); - - final T parcelable = mList.get(i); - verifySameType(listElementClass, parcelable.getClass()); - writeElement(parcelable, dest, callFlags); - - if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); - i++; - } - if (i < N) { - dest.writeInt(0); - Binder retriever = new Binder() { - @Override - protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) - throws RemoteException { - if (code != FIRST_CALL_TRANSACTION) { - return super.onTransact(code, data, reply, flags); - } - int i = data.readInt(); - if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); - while (i < N && reply.dataSize() < MAX_IPC_SIZE) { - reply.writeInt(1); - - final T parcelable = mList.get(i); - verifySameType(listElementClass, parcelable.getClass()); - writeElement(parcelable, reply, callFlags); - - if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); - i++; - } - if (i < N) { - if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); - reply.writeInt(0); - } - return true; - } - }; - if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever); - dest.writeStrongBinder(retriever); - } - } - } - - abstract void writeElement(T parcelable, Parcel reply, int callFlags); - - abstract void writeParcelableCreator(T parcelable, Parcel dest); - - abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader); -} diff --git a/apex/media/framework/java/android/media/BufferingParams.java b/apex/media/framework/java/android/media/BufferingParams.java deleted file mode 100644 index 04af02874bbd..000000000000 --- a/apex/media/framework/java/android/media/BufferingParams.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2017 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.media; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Structure for source buffering management params. - * - * Used by {@link MediaPlayer#getBufferingParams()} and - * {@link MediaPlayer#setBufferingParams(BufferingParams)} - * to control source buffering behavior. - * - * <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering - * (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer} - * is playing back source). {@link BufferingParams} includes corresponding marks for each - * stage of source buffering. The marks are time based (in milliseconds). - * - * <p>{@link MediaPlayer} source component has default marks which can be queried by - * calling {@link MediaPlayer#getBufferingParams()} before any change is made by - * {@link MediaPlayer#setBufferingParams()}. - * <ul> - * <li><strong>initial buffering:</strong> initialMarkMs is used when - * {@link MediaPlayer} is being prepared. When cached data amount exceeds this mark - * {@link MediaPlayer} is prepared. </li> - * <li><strong>rebuffering during playback:</strong> resumePlaybackMarkMs is used when - * {@link MediaPlayer} is playing back content. - * <ul> - * <li> {@link MediaPlayer} has internal mark, namely pausePlaybackMarkMs, to decide when - * to pause playback if cached data amount runs low. This internal mark varies based on - * type of data source. </li> - * <li> When cached data amount exceeds resumePlaybackMarkMs, {@link MediaPlayer} will - * resume playback if it has been paused due to low cached data amount. The internal mark - * pausePlaybackMarkMs shall be less than resumePlaybackMarkMs. </li> - * <li> {@link MediaPlayer} has internal mark, namely pauseRebufferingMarkMs, to decide - * when to pause rebuffering. Apparently, this internal mark shall be no less than - * resumePlaybackMarkMs. </li> - * <li> {@link MediaPlayer} has internal mark, namely resumeRebufferingMarkMs, to decide - * when to resume buffering. This internal mark varies based on type of data source. This - * mark shall be larger than pausePlaybackMarkMs, and less than pauseRebufferingMarkMs. - * </li> - * </ul> </li> - * </ul> - * <p>Users should use {@link Builder} to change {@link BufferingParams}. - * @hide - */ -public final class BufferingParams implements Parcelable { - private static final int BUFFERING_NO_MARK = -1; - - // params - private int mInitialMarkMs = BUFFERING_NO_MARK; - - private int mResumePlaybackMarkMs = BUFFERING_NO_MARK; - - private BufferingParams() { - } - - /** - * Return initial buffering mark in milliseconds. - * @return initial buffering mark in milliseconds - */ - public int getInitialMarkMs() { - return mInitialMarkMs; - } - - /** - * Return the mark in milliseconds for resuming playback. - * @return the mark for resuming playback in milliseconds - */ - public int getResumePlaybackMarkMs() { - return mResumePlaybackMarkMs; - } - - /** - * Builder class for {@link BufferingParams} objects. - * <p> Here is an example where <code>Builder</code> is used to define the - * {@link BufferingParams} to be used by a {@link MediaPlayer} instance: - * - * <pre class="prettyprint"> - * BufferingParams myParams = mediaplayer.getDefaultBufferingParams(); - * myParams = new BufferingParams.Builder(myParams) - * .setInitialMarkMs(10000) - * .setResumePlaybackMarkMs(15000) - * .build(); - * mediaplayer.setBufferingParams(myParams); - * </pre> - */ - public static class Builder { - private int mInitialMarkMs = BUFFERING_NO_MARK; - private int mResumePlaybackMarkMs = BUFFERING_NO_MARK; - - /** - * Constructs a new Builder with the defaults. - * By default, all marks are -1. - */ - public Builder() { - } - - /** - * Constructs a new Builder from a given {@link BufferingParams} instance - * @param bp the {@link BufferingParams} object whose data will be reused - * in the new Builder. - */ - public Builder(BufferingParams bp) { - mInitialMarkMs = bp.mInitialMarkMs; - mResumePlaybackMarkMs = bp.mResumePlaybackMarkMs; - } - - /** - * Combines all of the fields that have been set and return a new - * {@link BufferingParams} object. <code>IllegalStateException</code> will be - * thrown if there is conflict between fields. - * @return a new {@link BufferingParams} object - */ - public BufferingParams build() { - BufferingParams bp = new BufferingParams(); - bp.mInitialMarkMs = mInitialMarkMs; - bp.mResumePlaybackMarkMs = mResumePlaybackMarkMs; - - return bp; - } - - /** - * Sets the time based mark in milliseconds for initial buffering. - * @param markMs time based mark in milliseconds - * @return the same Builder instance. - */ - public Builder setInitialMarkMs(int markMs) { - mInitialMarkMs = markMs; - return this; - } - - /** - * Sets the time based mark in milliseconds for resuming playback. - * @param markMs time based mark in milliseconds for resuming playback - * @return the same Builder instance. - */ - public Builder setResumePlaybackMarkMs(int markMs) { - mResumePlaybackMarkMs = markMs; - return this; - } - } - - private BufferingParams(Parcel in) { - mInitialMarkMs = in.readInt(); - mResumePlaybackMarkMs = in.readInt(); - } - - public static final @android.annotation.NonNull Parcelable.Creator<BufferingParams> CREATOR = - new Parcelable.Creator<BufferingParams>() { - @Override - public BufferingParams createFromParcel(Parcel in) { - return new BufferingParams(in); - } - - @Override - public BufferingParams[] newArray(int size) { - return new BufferingParams[size]; - } - }; - - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mInitialMarkMs); - dest.writeInt(mResumePlaybackMarkMs); - } -} diff --git a/apex/media/framework/java/android/media/Controller2Link.java b/apex/media/framework/java/android/media/Controller2Link.java deleted file mode 100644 index 8eefec73194c..000000000000 --- a/apex/media/framework/java/android/media/Controller2Link.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2018 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.media; - -import android.os.Binder; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.ResultReceiver; - -import java.util.Objects; - -/** - * Handles incoming commands from {@link MediaSession2} to {@link MediaController2}. - * @hide - */ -// @SystemApi -public final class Controller2Link implements Parcelable { - private static final String TAG = "Controller2Link"; - private static final boolean DEBUG = MediaController2.DEBUG; - - public static final @android.annotation.NonNull Parcelable.Creator<Controller2Link> CREATOR = - new Parcelable.Creator<Controller2Link>() { - @Override - public Controller2Link createFromParcel(Parcel in) { - return new Controller2Link(in); - } - - @Override - public Controller2Link[] newArray(int size) { - return new Controller2Link[size]; - } - }; - - - private final MediaController2 mController; - private final IMediaController2 mIController; - - public Controller2Link(MediaController2 controller) { - mController = controller; - mIController = new Controller2Stub(); - } - - Controller2Link(Parcel in) { - mController = null; - mIController = IMediaController2.Stub.asInterface(in.readStrongBinder()); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongBinder(mIController.asBinder()); - } - - @Override - public int hashCode() { - return mIController.asBinder().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Controller2Link)) { - return false; - } - Controller2Link other = (Controller2Link) obj; - return Objects.equals(mIController.asBinder(), other.mIController.asBinder()); - } - - /** Interface method for IMediaController2.notifyConnected */ - public void notifyConnected(int seq, Bundle connectionResult) { - try { - mIController.notifyConnected(seq, connectionResult); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Interface method for IMediaController2.notifyDisonnected */ - public void notifyDisconnected(int seq) { - try { - mIController.notifyDisconnected(seq); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Interface method for IMediaController2.notifyPlaybackActiveChanged */ - public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) { - try { - mIController.notifyPlaybackActiveChanged(seq, playbackActive); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Interface method for IMediaController2.sendSessionCommand */ - public void sendSessionCommand(int seq, Session2Command command, Bundle args, - ResultReceiver resultReceiver) { - try { - mIController.sendSessionCommand(seq, command, args, resultReceiver); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Interface method for IMediaController2.cancelSessionCommand */ - public void cancelSessionCommand(int seq) { - try { - mIController.cancelSessionCommand(seq); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Stub implementation for IMediaController2.notifyConnected */ - public void onConnected(int seq, Bundle connectionResult) { - if (connectionResult == null) { - onDisconnected(seq); - return; - } - mController.onConnected(seq, connectionResult); - } - - /** Stub implementation for IMediaController2.notifyDisonnected */ - public void onDisconnected(int seq) { - mController.onDisconnected(seq); - } - - /** Stub implementation for IMediaController2.notifyPlaybackActiveChanged */ - public void onPlaybackActiveChanged(int seq, boolean playbackActive) { - mController.onPlaybackActiveChanged(seq, playbackActive); - } - - /** Stub implementation for IMediaController2.sendSessionCommand */ - public void onSessionCommand(int seq, Session2Command command, Bundle args, - ResultReceiver resultReceiver) { - mController.onSessionCommand(seq, command, args, resultReceiver); - } - - /** Stub implementation for IMediaController2.cancelSessionCommand */ - public void onCancelCommand(int seq) { - mController.onCancelCommand(seq); - } - - private class Controller2Stub extends IMediaController2.Stub { - @Override - public void notifyConnected(int seq, Bundle connectionResult) { - final long token = Binder.clearCallingIdentity(); - try { - Controller2Link.this.onConnected(seq, connectionResult); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void notifyDisconnected(int seq) { - final long token = Binder.clearCallingIdentity(); - try { - Controller2Link.this.onDisconnected(seq); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void notifyPlaybackActiveChanged(int seq, boolean playbackActive) { - final long token = Binder.clearCallingIdentity(); - try { - Controller2Link.this.onPlaybackActiveChanged(seq, playbackActive); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void sendSessionCommand(int seq, Session2Command command, Bundle args, - ResultReceiver resultReceiver) { - final long token = Binder.clearCallingIdentity(); - try { - Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void cancelSessionCommand(int seq) { - final long token = Binder.clearCallingIdentity(); - try { - Controller2Link.this.onCancelCommand(seq); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } -} diff --git a/apex/media/framework/java/android/media/DataSourceCallback.java b/apex/media/framework/java/android/media/DataSourceCallback.java deleted file mode 100644 index c297ecda249c..000000000000 --- a/apex/media/framework/java/android/media/DataSourceCallback.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017 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.media; - -import android.annotation.NonNull; - -import java.io.Closeable; -import java.io.IOException; - -/** - * For supplying media data to the framework. Implement this if your app has - * special requirements for the way media data is obtained. - * - * <p class="note">Methods of this interface may be called on multiple different - * threads. There will be a thread synchronization point between each call to ensure that - * modifications to the state of your DataSourceCallback are visible to future calls. This means - * you don't need to do your own synchronization unless you're modifying the - * DataSourceCallback from another thread while it's being used by the framework.</p> - * - * @hide - */ -public abstract class DataSourceCallback implements Closeable { - - public static final int END_OF_STREAM = -1; - - /** - * Called to request data from the given position. - * - * Implementations should should write up to {@code size} bytes into - * {@code buffer}, and return the number of bytes written. - * - * Return {@code 0} if size is zero (thus no bytes are read). - * - * Return {@code -1} to indicate that end of stream is reached. - * - * @param position the position in the data source to read from. - * @param buffer the buffer to read the data into. - * @param offset the offset within buffer to read the data into. - * @param size the number of bytes to read. - * @throws IOException on fatal errors. - * @return the number of bytes read, or {@link #END_OF_STREAM} if end of stream is reached. - */ - public abstract int readAt(long position, @NonNull byte[] buffer, int offset, int size) - throws IOException; - - /** - * Called to get the size of the data source. - * - * @throws IOException on fatal errors - * @return the size of data source in bytes, or -1 if the size is unknown. - */ - public abstract long getSize() throws IOException; -} diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java deleted file mode 100644 index f39bcfb267bf..000000000000 --- a/apex/media/framework/java/android/media/MediaCommunicationManager.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright 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.media; - -import static android.Manifest.permission.MEDIA_CONTENT_CONTROL; -import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; - -import android.annotation.CallbackExecutor; -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.annotation.SystemService; -import android.content.Context; -import android.media.session.MediaSession; -import android.media.session.MediaSessionManager; -import android.os.Build; -import android.os.RemoteException; -import android.os.UserHandle; -import android.service.media.MediaBrowserService; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.modules.annotation.MinSdk; -import com.android.modules.utils.build.SdkLevel; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executor; - -/** - * Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s} - * that applications have published to express their ongoing media playback state. - */ -@MinSdk(Build.VERSION_CODES.S) -@SystemService(Context.MEDIA_COMMUNICATION_SERVICE) -public class MediaCommunicationManager { - private static final String TAG = "MediaCommunicationManager"; - - /** - * The manager version used from beginning. - */ - private static final int VERSION_1 = 1; - - /** - * Current manager version. - */ - private static final int CURRENT_VERSION = VERSION_1; - - private final Context mContext; - private final IMediaCommunicationService mService; - - private final Object mLock = new Object(); - private final CopyOnWriteArrayList<SessionCallbackRecord> mTokenCallbackRecords = - new CopyOnWriteArrayList<>(); - - @GuardedBy("mLock") - private MediaCommunicationServiceCallbackStub mCallbackStub; - - /** - * @hide - */ - public MediaCommunicationManager(@NonNull Context context) { - if (!SdkLevel.isAtLeastS()) { - throw new UnsupportedOperationException("Android version must be S or greater."); - } - mContext = context; - mService = IMediaCommunicationService.Stub.asInterface( - MediaFrameworkInitializer.getMediaServiceManager() - .getMediaCommunicationServiceRegisterer() - .get()); - } - - /** - * Gets the version of this {@link MediaCommunicationManager}. - */ - public @IntRange(from = 1) int getVersion() { - return CURRENT_VERSION; - } - - /** - * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is - * created. - * @param token newly created session2 token - * @hide - */ - public void notifySession2Created(@NonNull Session2Token token) { - Objects.requireNonNull(token, "token shouldn't be null"); - if (token.getType() != Session2Token.TYPE_SESSION) { - throw new IllegalArgumentException("token's type should be TYPE_SESSION"); - } - try { - mService.notifySession2Created(token); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - } - - /** - * Checks whether the remote user is a trusted app. - * <p> - * An app is trusted if the app holds the - * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission or has an enabled - * notification listener. - * - * @param userInfo The remote user info from either - * {@link MediaSession#getCurrentControllerInfo()} or - * {@link MediaBrowserService#getCurrentBrowserInfo()}. - * @return {@code true} if the remote user is trusted or {@code false} otherwise. - * @hide - */ - public boolean isTrustedForMediaControl(@NonNull MediaSessionManager.RemoteUserInfo userInfo) { - Objects.requireNonNull(userInfo, "userInfo shouldn't be null"); - if (userInfo.getPackageName() == null) { - return false; - } - try { - return mService.isTrusted( - userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid()); - } catch (RemoteException e) { - Log.w(TAG, "Cannot communicate with the service.", e); - } - return false; - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the - * current user. - * <p> - * Although this API can be used without any restriction, each session owners can accept or - * reject your uses of {@link MediaSession2}. - * - * @return A list of {@link Session2Token}. - */ - @NonNull - public List<Session2Token> getSession2Tokens() { - return getSession2Tokens(UserHandle.myUserId()); - } - - /** - * Adds a callback to be notified when the list of active sessions changes. - * <p> - * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be - * held by the calling app. - * </p> - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - @RequiresPermission(MEDIA_CONTENT_CONTROL) - public void registerSessionCallback(@CallbackExecutor @NonNull Executor executor, - @NonNull SessionCallback callback) { - Objects.requireNonNull(executor, "executor must not be null"); - Objects.requireNonNull(callback, "callback must not be null"); - - if (!mTokenCallbackRecords.addIfAbsent( - new SessionCallbackRecord(executor, callback))) { - Log.w(TAG, "registerSession2TokenCallback: Ignoring the same callback"); - return; - } - synchronized (mLock) { - if (mCallbackStub == null) { - MediaCommunicationServiceCallbackStub callbackStub = - new MediaCommunicationServiceCallbackStub(); - try { - mService.registerCallback(callbackStub, mContext.getPackageName()); - mCallbackStub = callbackStub; - } catch (RemoteException ex) { - Log.e(TAG, "Failed to register callback.", ex); - } - } - } - } - - /** - * Stops receiving active sessions updates on the specified callback. - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public void unregisterSessionCallback(@NonNull SessionCallback callback) { - if (!mTokenCallbackRecords.remove( - new SessionCallbackRecord(null, callback))) { - Log.w(TAG, "unregisterSession2TokenCallback: Ignoring an unknown callback."); - return; - } - synchronized (mLock) { - if (mCallbackStub != null && mTokenCallbackRecords.isEmpty()) { - try { - mService.unregisterCallback(mCallbackStub); - } catch (RemoteException ex) { - Log.e(TAG, "Failed to unregister callback.", ex); - } - mCallbackStub = null; - } - } - } - - private List<Session2Token> getSession2Tokens(int userId) { - try { - MediaParceledListSlice slice = mService.getSession2Tokens(userId); - return slice == null ? Collections.emptyList() : slice.getList(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get session tokens", e); - } - return Collections.emptyList(); - } - - /** - * Callback for listening to changes to the sessions. - * @see #registerSessionCallback(Executor, SessionCallback) - * @hide - */ - @SystemApi(client = MODULE_LIBRARIES) - public interface SessionCallback { - /** - * Called when a new {@link MediaSession2 media session2} is created. - * @param token the newly created token - */ - default void onSession2TokenCreated(@NonNull Session2Token token) {} - - /** - * Called when {@link #getSession2Tokens() session tokens} are changed. - */ - default void onSession2TokensChanged(@NonNull List<Session2Token> tokens) {} - } - - private static final class SessionCallbackRecord { - public final Executor executor; - public final SessionCallback callback; - - SessionCallbackRecord(Executor executor, SessionCallback callback) { - this.executor = executor; - this.callback = callback; - } - - @Override - public int hashCode() { - return Objects.hash(callback); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof SessionCallbackRecord)) { - return false; - } - return Objects.equals(this.callback, ((SessionCallbackRecord) obj).callback); - } - } - - class MediaCommunicationServiceCallbackStub extends IMediaCommunicationServiceCallback.Stub { - @Override - public void onSession2Created(Session2Token token) throws RemoteException { - for (SessionCallbackRecord record : mTokenCallbackRecords) { - record.executor.execute(() -> record.callback.onSession2TokenCreated(token)); - } - } - - @Override - public void onSession2Changed(MediaParceledListSlice tokens) throws RemoteException { - List<Session2Token> tokenList = tokens.getList(); - for (SessionCallbackRecord record : mTokenCallbackRecords) { - record.executor.execute(() -> record.callback.onSession2TokensChanged(tokenList)); - } - } - } -} diff --git a/apex/media/framework/java/android/media/MediaConstants.java b/apex/media/framework/java/android/media/MediaConstants.java deleted file mode 100644 index ce108894b9a5..000000000000 --- a/apex/media/framework/java/android/media/MediaConstants.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2018 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.media; - -class MediaConstants { - // Bundle key for int - static final String KEY_PID = "android.media.key.PID"; - - // Bundle key for String - static final String KEY_PACKAGE_NAME = "android.media.key.PACKAGE_NAME"; - - // Bundle key for Parcelable - static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK"; - static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS"; - static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE"; - static final String KEY_TOKEN_EXTRAS = "android.media.key.TOKEN_EXTRAS"; - static final String KEY_CONNECTION_HINTS = "android.media.key.CONNECTION_HINTS"; - - private MediaConstants() { - } -} diff --git a/apex/media/framework/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java deleted file mode 100644 index 159e8e551d11..000000000000 --- a/apex/media/framework/java/android/media/MediaController2.java +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright 2018 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.media; - -import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS; -import static android.media.MediaConstants.KEY_CONNECTION_HINTS; -import static android.media.MediaConstants.KEY_PACKAGE_NAME; -import static android.media.MediaConstants.KEY_PID; -import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE; -import static android.media.MediaConstants.KEY_SESSION2LINK; -import static android.media.MediaConstants.KEY_TOKEN_EXTRAS; -import static android.media.Session2Command.Result.RESULT_ERROR_UNKNOWN_ERROR; -import static android.media.Session2Command.Result.RESULT_INFO_SKIPPED; -import static android.media.Session2Token.TYPE_SESSION; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; - -import java.util.concurrent.Executor; - -/** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * - * Allows an app to interact with an active {@link MediaSession2} or a - * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other - * commands can be sent to the session. - */ -public class MediaController2 implements AutoCloseable { - static final String TAG = "MediaController2"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - @SuppressWarnings("WeakerAccess") /* synthetic access */ - final ControllerCallback mCallback; - - private final IBinder.DeathRecipient mDeathRecipient = () -> close(); - private final Context mContext; - private final Session2Token mSessionToken; - private final Executor mCallbackExecutor; - private final Controller2Link mControllerStub; - private final Handler mResultHandler; - private final SessionServiceConnection mServiceConnection; - - private final Object mLock = new Object(); - //@GuardedBy("mLock") - private boolean mClosed; - //@GuardedBy("mLock") - private int mNextSeqNumber; - //@GuardedBy("mLock") - private Session2Link mSessionBinder; - //@GuardedBy("mLock") - private Session2CommandGroup mAllowedCommands; - //@GuardedBy("mLock") - private Session2Token mConnectedToken; - //@GuardedBy("mLock") - private ArrayMap<ResultReceiver, Integer> mPendingCommands; - //@GuardedBy("mLock") - private ArraySet<Integer> mRequestedCommandSeqNumbers; - //@GuardedBy("mLock") - private boolean mPlaybackActive; - - /** - * Create a {@link MediaController2} from the {@link Session2Token}. - * This connects to the session and may wake up the service if it's not available. - * - * @param context context - * @param token token to connect to - * @param connectionHints a session-specific argument to send to the session when connecting. - * The contents of this bundle may affect the connection result. - * @param executor executor to run callbacks on. - * @param callback controller callback to receive changes in. - */ - MediaController2(@NonNull Context context, @NonNull Session2Token token, - @NonNull Bundle connectionHints, @NonNull Executor executor, - @NonNull ControllerCallback callback) { - if (context == null) { - throw new IllegalArgumentException("context shouldn't be null"); - } - if (token == null) { - throw new IllegalArgumentException("token shouldn't be null"); - } - mContext = context; - mSessionToken = token; - mCallbackExecutor = (executor == null) ? context.getMainExecutor() : executor; - mCallback = (callback == null) ? new ControllerCallback() {} : callback; - mControllerStub = new Controller2Link(this); - // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked. - mResultHandler = new Handler(context.getMainLooper()); - - mNextSeqNumber = 0; - mPendingCommands = new ArrayMap<>(); - mRequestedCommandSeqNumbers = new ArraySet<>(); - - boolean connectRequested; - if (token.getType() == TYPE_SESSION) { - mServiceConnection = null; - connectRequested = requestConnectToSession(connectionHints); - } else { - mServiceConnection = new SessionServiceConnection(connectionHints); - connectRequested = requestConnectToService(); - } - if (!connectRequested) { - close(); - } - } - - @Override - public void close() { - synchronized (mLock) { - if (mClosed) { - // Already closed. Ignore rest of clean up code. - // Note: unbindService() throws IllegalArgumentException when it's called twice. - return; - } - if (DEBUG) { - Log.d(TAG, "closing " + this); - } - mClosed = true; - if (mServiceConnection != null) { - // Note: This should be called even when the bindService() has returned false. - mContext.unbindService(mServiceConnection); - } - if (mSessionBinder != null) { - try { - mSessionBinder.disconnect(mControllerStub, getNextSeqNumber()); - mSessionBinder.unlinkToDeath(mDeathRecipient, 0); - } catch (RuntimeException e) { - // No-op - } - } - mConnectedToken = null; - mPendingCommands.clear(); - mRequestedCommandSeqNumbers.clear(); - mCallbackExecutor.execute(() -> { - mCallback.onDisconnected(MediaController2.this); - }); - mSessionBinder = null; - } - } - - /** - * Returns {@link Session2Token} of the connected session. - * If it is not connected yet, it returns {@code null}. - * <p> - * This may differ with the {@link Session2Token} from the constructor. For example, if the - * controller is created with the token for {@link MediaSession2Service}, this would return - * token for the {@link MediaSession2} in the service. - * - * @return Session2Token of the connected session, or {@code null} if not connected - */ - @Nullable - public Session2Token getConnectedToken() { - synchronized (mLock) { - return mConnectedToken; - } - } - - /** - * Returns whether the session's playback is active. - * - * @return {@code true} if playback active. {@code false} otherwise. - * @see ControllerCallback#onPlaybackActiveChanged(MediaController2, boolean) - */ - public boolean isPlaybackActive() { - synchronized (mLock) { - return mPlaybackActive; - } - } - - /** - * Sends a session command to the session - * <p> - * @param command the session command - * @param args optional arguments - * @return a token which will be sent together in {@link ControllerCallback#onCommandResult} - * when its result is received. - */ - @NonNull - public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - - ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) { - protected void onReceiveResult(int resultCode, Bundle resultData) { - synchronized (mLock) { - mPendingCommands.remove(this); - } - mCallbackExecutor.execute(() -> { - mCallback.onCommandResult(MediaController2.this, this, - command, new Session2Command.Result(resultCode, resultData)); - }); - } - }; - - synchronized (mLock) { - if (mSessionBinder != null) { - int seq = getNextSeqNumber(); - mPendingCommands.put(resultReceiver, seq); - try { - mSessionBinder.sendSessionCommand(mControllerStub, seq, command, args, - resultReceiver); - } catch (RuntimeException e) { - mPendingCommands.remove(resultReceiver); - resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null); - } - } - } - return resultReceiver; - } - - /** - * Cancels the session command previously sent. - * - * @param token the token which is returned from {@link #sendSessionCommand}. - */ - public void cancelSessionCommand(@NonNull Object token) { - if (token == null) { - throw new IllegalArgumentException("token shouldn't be null"); - } - synchronized (mLock) { - if (mSessionBinder == null) return; - Integer seq = mPendingCommands.remove(token); - if (seq != null) { - mSessionBinder.cancelSessionCommand(mControllerStub, seq); - } - } - } - - // Called by Controller2Link.onConnected - void onConnected(int seq, Bundle connectionResult) { - Session2Link sessionBinder = connectionResult.getParcelable(KEY_SESSION2LINK); - Session2CommandGroup allowedCommands = - connectionResult.getParcelable(KEY_ALLOWED_COMMANDS); - boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE); - - Bundle tokenExtras = connectionResult.getBundle(KEY_TOKEN_EXTRAS); - if (tokenExtras == null) { - Log.w(TAG, "extras shouldn't be null."); - tokenExtras = Bundle.EMPTY; - } else if (MediaSession2.hasCustomParcelable(tokenExtras)) { - Log.w(TAG, "extras contain custom parcelable. Ignoring."); - tokenExtras = Bundle.EMPTY; - } - - if (DEBUG) { - Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder - + ", allowedCommands=" + allowedCommands); - } - if (sessionBinder == null || allowedCommands == null) { - // Connection rejected. - close(); - return; - } - synchronized (mLock) { - mSessionBinder = sessionBinder; - mAllowedCommands = allowedCommands; - mPlaybackActive = playbackActive; - - // Implementation for the local binder is no-op, - // so can be used without worrying about deadlock. - sessionBinder.linkToDeath(mDeathRecipient, 0); - mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION, - mSessionToken.getPackageName(), sessionBinder, tokenExtras); - } - mCallbackExecutor.execute(() -> { - mCallback.onConnected(MediaController2.this, allowedCommands); - }); - } - - // Called by Controller2Link.onDisconnected - void onDisconnected(int seq) { - // close() will call mCallback.onDisconnected - close(); - } - - // Called by Controller2Link.onPlaybackActiveChanged - void onPlaybackActiveChanged(int seq, boolean playbackActive) { - synchronized (mLock) { - mPlaybackActive = playbackActive; - } - mCallbackExecutor.execute(() -> { - mCallback.onPlaybackActiveChanged(MediaController2.this, playbackActive); - }); - } - - // Called by Controller2Link.onSessionCommand - void onSessionCommand(int seq, Session2Command command, Bundle args, - @Nullable ResultReceiver resultReceiver) { - synchronized (mLock) { - mRequestedCommandSeqNumbers.add(seq); - } - mCallbackExecutor.execute(() -> { - boolean isCanceled; - synchronized (mLock) { - isCanceled = !mRequestedCommandSeqNumbers.remove(seq); - } - if (isCanceled) { - if (resultReceiver != null) { - resultReceiver.send(RESULT_INFO_SKIPPED, null); - } - return; - } - Session2Command.Result result = mCallback.onSessionCommand( - MediaController2.this, command, args); - if (resultReceiver != null) { - if (result == null) { - resultReceiver.send(RESULT_INFO_SKIPPED, null); - } else { - resultReceiver.send(result.getResultCode(), result.getResultData()); - } - } - }); - } - - // Called by Controller2Link.onSessionCommand - void onCancelCommand(int seq) { - synchronized (mLock) { - mRequestedCommandSeqNumbers.remove(seq); - } - } - - private int getNextSeqNumber() { - synchronized (mLock) { - return mNextSeqNumber++; - } - } - - private Bundle createConnectionRequest(@NonNull Bundle connectionHints) { - Bundle connectionRequest = new Bundle(); - connectionRequest.putString(KEY_PACKAGE_NAME, mContext.getPackageName()); - connectionRequest.putInt(KEY_PID, Process.myPid()); - connectionRequest.putBundle(KEY_CONNECTION_HINTS, connectionHints); - return connectionRequest; - } - - private boolean requestConnectToSession(@NonNull Bundle connectionHints) { - Session2Link sessionBinder = mSessionToken.getSessionLink(); - Bundle connectionRequest = createConnectionRequest(connectionHints); - try { - sessionBinder.connect(mControllerStub, getNextSeqNumber(), connectionRequest); - } catch (RuntimeException e) { - Log.w(TAG, "Failed to call connection request", e); - return false; - } - return true; - } - - private boolean requestConnectToService() { - // Service. Needs to get fresh binder whenever connection is needed. - final Intent intent = new Intent(MediaSession2Service.SERVICE_INTERFACE); - intent.setClassName(mSessionToken.getPackageName(), mSessionToken.getServiceName()); - - // Use bindService() instead of startForegroundService() to start session service for three - // reasons. - // 1. Prevent session service owner's stopSelf() from destroying service. - // With the startForegroundService(), service's call of stopSelf() will trigger immediate - // onDestroy() calls on the main thread even when onConnect() is running in another - // thread. - // 2. Minimize APIs for developers to take care about. - // With bindService(), developers only need to take care about Service.onBind() - // but Service.onStartCommand() should be also taken care about with the - // startForegroundService(). - // 3. Future support for UI-less playback - // If a service wants to keep running, it should be either foreground service or - // bound service. But there had been request for the feature for system apps - // and using bindService() will be better fit with it. - synchronized (mLock) { - boolean result = mContext.bindService( - intent, mServiceConnection, Context.BIND_AUTO_CREATE); - if (!result) { - Log.w(TAG, "bind to " + mSessionToken + " failed"); - return false; - } else if (DEBUG) { - Log.d(TAG, "bind to " + mSessionToken + " succeeded"); - } - } - return true; - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Builder for {@link MediaController2}. - * <p> - * Any incoming event from the {@link MediaSession2} will be handled on the callback - * executor. If it's not set, {@link Context#getMainExecutor()} will be used by default. - */ - public static final class Builder { - private Context mContext; - private Session2Token mToken; - private Bundle mConnectionHints; - private Executor mCallbackExecutor; - private ControllerCallback mCallback; - - /** - * Creates a builder for {@link MediaController2}. - * - * @param context context - * @param token token of the session to connect to - */ - public Builder(@NonNull Context context, @NonNull Session2Token token) { - if (context == null) { - throw new IllegalArgumentException("context shouldn't be null"); - } - if (token == null) { - throw new IllegalArgumentException("token shouldn't be null"); - } - mContext = context; - mToken = token; - } - - /** - * Set the connection hints for the controller. - * <p> - * {@code connectionHints} is a session-specific argument to send to the session when - * connecting. The contents of this bundle may affect the connection result. - * <p> - * An {@link IllegalArgumentException} will be thrown if the bundle contains any - * non-framework Parcelable objects. - * - * @param connectionHints a bundle which contains the connection hints - * @return The Builder to allow chaining - */ - @NonNull - public Builder setConnectionHints(@NonNull Bundle connectionHints) { - if (connectionHints == null) { - throw new IllegalArgumentException("connectionHints shouldn't be null"); - } - if (MediaSession2.hasCustomParcelable(connectionHints)) { - throw new IllegalArgumentException("connectionHints shouldn't contain any custom " - + "parcelables"); - } - mConnectionHints = new Bundle(connectionHints); - return this; - } - - /** - * Set callback for the controller and its executor. - * - * @param executor callback executor - * @param callback session callback. - * @return The Builder to allow chaining - */ - @NonNull - public Builder setControllerCallback(@NonNull Executor executor, - @NonNull ControllerCallback callback) { - if (executor == null) { - throw new IllegalArgumentException("executor shouldn't be null"); - } - if (callback == null) { - throw new IllegalArgumentException("callback shouldn't be null"); - } - mCallbackExecutor = executor; - mCallback = callback; - return this; - } - - /** - * Build {@link MediaController2}. - * - * @return a new controller - */ - @NonNull - public MediaController2 build() { - if (mCallbackExecutor == null) { - mCallbackExecutor = mContext.getMainExecutor(); - } - if (mCallback == null) { - mCallback = new ControllerCallback() {}; - } - if (mConnectionHints == null) { - mConnectionHints = Bundle.EMPTY; - } - return new MediaController2( - mContext, mToken, mConnectionHints, mCallbackExecutor, mCallback); - } - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Interface for listening to change in activeness of the {@link MediaSession2}. - */ - public abstract static class ControllerCallback { - /** - * Called when the controller is successfully connected to the session. The controller - * becomes available afterwards. - * - * @param controller the controller for this event - * @param allowedCommands commands that's allowed by the session. - */ - public void onConnected(@NonNull MediaController2 controller, - @NonNull Session2CommandGroup allowedCommands) {} - - /** - * Called when the session refuses the controller or the controller is disconnected from - * the session. The controller becomes unavailable afterwards and the callback wouldn't - * be called. - * <p> - * It will be also called after the {@link #close()}, so you can put clean up code here. - * You don't need to call {@link #close()} after this. - * - * @param controller the controller for this event - */ - public void onDisconnected(@NonNull MediaController2 controller) {} - - /** - * Called when the session's playback activeness is changed. - * - * @param controller the controller for this event - * @param playbackActive {@code true} if the session's playback is active. - * {@code false} otherwise. - * @see MediaController2#isPlaybackActive() - */ - public void onPlaybackActiveChanged(@NonNull MediaController2 controller, - boolean playbackActive) {} - - /** - * Called when the connected session sent a session command. - * - * @param controller the controller for this event - * @param command the session command - * @param args optional arguments - * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED - * will be sent to the session. - */ - @Nullable - public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller, - @NonNull Session2Command command, @Nullable Bundle args) { - return null; - } - - /** - * Called when the command sent to the connected session is finished. - * - * @param controller the controller for this event - * @param token the token got from {@link MediaController2#sendSessionCommand} - * @param command the session command - * @param result the result of the session command - */ - public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token, - @NonNull Session2Command command, @NonNull Session2Command.Result result) {} - } - - // This will be called on the main thread. - private class SessionServiceConnection implements ServiceConnection { - private final Bundle mConnectionHints; - - SessionServiceConnection(@Nullable Bundle connectionHints) { - mConnectionHints = connectionHints; - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - // Note that it's always main-thread. - boolean connectRequested = false; - try { - if (DEBUG) { - Log.d(TAG, "onServiceConnected " + name + " " + this); - } - if (!mSessionToken.getPackageName().equals(name.getPackageName())) { - Log.wtf(TAG, "Expected connection to " + mSessionToken.getPackageName() - + " but is connected to " + name); - return; - } - IMediaSession2Service iService = IMediaSession2Service.Stub.asInterface(service); - if (iService == null) { - Log.wtf(TAG, "Service interface is missing."); - return; - } - Bundle connectionRequest = createConnectionRequest(mConnectionHints); - iService.connect(mControllerStub, getNextSeqNumber(), connectionRequest); - connectRequested = true; - } catch (RemoteException e) { - Log.w(TAG, "Service " + name + " has died prematurely", e); - } finally { - if (!connectRequested) { - close(); - } - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - // Temporal lose of the binding because of the service crash. System will automatically - // rebind, so just no-op. - if (DEBUG) { - Log.w(TAG, "Session service " + name + " is disconnected."); - } - close(); - } - - @Override - public void onBindingDied(ComponentName name) { - // Permanent lose of the binding because of the service package update or removed. - // This SessionServiceRecord will be removed accordingly, but forget session binder here - // for sure. - close(); - } - } -} diff --git a/apex/media/framework/java/android/media/MediaFeature.java b/apex/media/framework/java/android/media/MediaFeature.java deleted file mode 100644 index 8d1b159cd70b..000000000000 --- a/apex/media/framework/java/android/media/MediaFeature.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.media; - -import android.annotation.StringDef; -import android.os.Build; - -import com.android.modules.annotation.MinSdk; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * MediaFeature defines various media features, e.g. hdr type. - */ -@MinSdk(Build.VERSION_CODES.S) -public final class MediaFeature { - /** - * Defines tye type of HDR(high dynamic range) video. - */ - public static final class HdrType { - private HdrType() { - } - - /** - * HDR type for dolby-vision. - */ - public static final String DOLBY_VISION = "android.media.feature.hdr.dolby_vision"; - /** - * HDR type for hdr10. - */ - public static final String HDR10 = "android.media.feature.hdr.hdr10"; - /** - * HDR type for hdr10+. - */ - public static final String HDR10_PLUS = "android.media.feature.hdr.hdr10_plus"; - /** - * HDR type for hlg. - */ - public static final String HLG = "android.media.feature.hdr.hlg"; - } - - /** @hide */ - @StringDef({ - HdrType.DOLBY_VISION, - HdrType.HDR10, - HdrType.HDR10_PLUS, - HdrType.HLG, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface MediaHdrType { - } -} diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java deleted file mode 100644 index 75a56b7231d9..000000000000 --- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 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.media; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; -import android.app.SystemServiceRegistry; -import android.content.Context; -import android.os.Build; - -import com.android.modules.annotation.MinSdk; -import com.android.modules.utils.build.SdkLevel; - -/** - * Class for performing registration for all media services on com.android.media apex. - * - * @hide - */ -@MinSdk(Build.VERSION_CODES.S) -@SystemApi(client = Client.MODULE_LIBRARIES) -public class MediaFrameworkInitializer { - private MediaFrameworkInitializer() { - } - - private static volatile MediaServiceManager sMediaServiceManager; - - /** - * Sets an instance of {@link MediaServiceManager} that allows - * the media mainline module to register/obtain media binder services. This is called - * by the platform during the system initialization. - * - * @param mediaServiceManager instance of {@link MediaServiceManager} that allows - * the media mainline module to register/obtain media binder services. - */ - public static void setMediaServiceManager( - @NonNull MediaServiceManager mediaServiceManager) { - if (sMediaServiceManager != null) { - throw new IllegalStateException("setMediaServiceManager called twice!"); - } - - if (mediaServiceManager == null) { - throw new NullPointerException("mediaServiceManager is null!"); - } - - sMediaServiceManager = mediaServiceManager; - } - - /** @hide */ - public static MediaServiceManager getMediaServiceManager() { - return sMediaServiceManager; - } - - /** - * Called by {@link SystemServiceRegistry}'s static initializer and registers all media - * services to {@link Context}, so that {@link Context#getSystemService} can return them. - * - * @throws IllegalStateException if this is called from anywhere besides - * {@link SystemServiceRegistry} - */ - public static void registerServiceWrappers() { - SystemServiceRegistry.registerContextAwareService( - Context.MEDIA_TRANSCODING_SERVICE, - MediaTranscodingManager.class, - context -> new MediaTranscodingManager(context) - ); - if (SdkLevel.isAtLeastS()) { - SystemServiceRegistry.registerContextAwareService( - Context.MEDIA_COMMUNICATION_SERVICE, - MediaCommunicationManager.class, - context -> new MediaCommunicationManager(context) - ); - } - } -} diff --git a/apex/media/framework/java/android/media/MediaParceledListSlice.java b/apex/media/framework/java/android/media/MediaParceledListSlice.java deleted file mode 100644 index 47ac193231a0..000000000000 --- a/apex/media/framework/java/android/media/MediaParceledListSlice.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 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.media; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Collections; -import java.util.List; - -/** - * This is a copied version of MediaParceledListSlice in framework with hidden API usages removed, - * and also with some lint error fixed. - * - * Transfer a large list of Parcelable objects across an IPC. Splits into - * multiple transactions if needed. - * - * TODO: Remove this from @SystemApi once all the MediaSession related classes are moved - * to apex (or ParceledListSlice moved to apex). This class is temporaily added to system API - * for moving classes step by step. - * - * @param <T> The type of the elements in the list. - * @see BaseMediaParceledListSlice - * @deprecated This is temporary marked as @SystemApi. Should be removed from the API surface. - * @hide - */ -@Deprecated -@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) -public final class MediaParceledListSlice<T extends Parcelable> - extends BaseMediaParceledListSlice<T> { - public MediaParceledListSlice(@NonNull List<T> list) { - super(list); - } - - private MediaParceledListSlice(Parcel in, ClassLoader loader) { - super(in, loader); - } - - @NonNull - public static <T extends Parcelable> MediaParceledListSlice<T> emptyList() { - return new MediaParceledListSlice<T>(Collections.<T> emptyList()); - } - - @Override - public int describeContents() { - int contents = 0; - final List<T> list = getList(); - for (int i=0; i<list.size(); i++) { - contents |= list.get(i).describeContents(); - } - return contents; - } - - @Override - void writeElement(T parcelable, Parcel dest, int callFlags) { - parcelable.writeToParcel(dest, callFlags); - } - - @Override - void writeParcelableCreator(T parcelable, Parcel dest) { - dest.writeParcelableCreator((Parcelable) parcelable); - } - - @Override - Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) { - return from.readParcelableCreator(loader); - } - - @NonNull - @SuppressWarnings("unchecked") - public static final Parcelable.ClassLoaderCreator<MediaParceledListSlice> CREATOR = - new Parcelable.ClassLoaderCreator<MediaParceledListSlice>() { - public MediaParceledListSlice createFromParcel(Parcel in) { - return new MediaParceledListSlice(in, null); - } - - @Override - public MediaParceledListSlice createFromParcel(Parcel in, ClassLoader loader) { - return new MediaParceledListSlice(in, loader); - } - - @Override - public MediaParceledListSlice[] newArray(int size) { - return new MediaParceledListSlice[size]; - } - }; -} diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java deleted file mode 100644 index 8cc3bc08dfb5..000000000000 --- a/apex/media/framework/java/android/media/MediaParser.java +++ /dev/null @@ -1,2292 +0,0 @@ -/* - * Copyright (C) 2019 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.media; - -import android.annotation.CheckResult; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.StringDef; -import android.media.MediaCodec.CryptoInfo; -import android.media.metrics.LogSessionId; -import android.os.Build; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; -import android.util.SparseArray; - -import androidx.annotation.RequiresApi; - -import com.android.modules.utils.build.SdkLevel; - -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.ParserException; -import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; -import com.google.android.exoplayer2.extractor.ChunkIndex; -import com.google.android.exoplayer2.extractor.DefaultExtractorInput; -import com.google.android.exoplayer2.extractor.Extractor; -import com.google.android.exoplayer2.extractor.ExtractorInput; -import com.google.android.exoplayer2.extractor.ExtractorOutput; -import com.google.android.exoplayer2.extractor.PositionHolder; -import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints; -import com.google.android.exoplayer2.extractor.TrackOutput; -import com.google.android.exoplayer2.extractor.amr.AmrExtractor; -import com.google.android.exoplayer2.extractor.flac.FlacExtractor; -import com.google.android.exoplayer2.extractor.flv.FlvExtractor; -import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; -import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; -import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor; -import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor; -import com.google.android.exoplayer2.extractor.ogg.OggExtractor; -import com.google.android.exoplayer2.extractor.ts.Ac3Extractor; -import com.google.android.exoplayer2.extractor.ts.Ac4Extractor; -import com.google.android.exoplayer2.extractor.ts.AdtsExtractor; -import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory; -import com.google.android.exoplayer2.extractor.ts.PsExtractor; -import com.google.android.exoplayer2.extractor.ts.TsExtractor; -import com.google.android.exoplayer2.extractor.wav.WavExtractor; -import com.google.android.exoplayer2.upstream.DataReader; -import com.google.android.exoplayer2.util.ParsableByteArray; -import com.google.android.exoplayer2.util.TimestampAdjuster; -import com.google.android.exoplayer2.util.Util; -import com.google.android.exoplayer2.video.ColorInfo; - -import java.io.EOFException; -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; -import java.util.function.Function; - -/** - * Parses media container formats and extracts contained media samples and metadata. - * - * <p>This class provides access to a battery of low-level media container parsers. Each instance of - * this class is associated to a specific media parser implementation which is suitable for - * extraction from a specific media container format. The media parser implementation assignment - * depends on the factory method (see {@link #create} and {@link #createByName}) used to create the - * instance. - * - * <p>Users must implement the following to use this class. - * - * <ul> - * <li>{@link InputReader}: Provides the media container's bytes to parse. - * <li>{@link OutputConsumer}: Provides a sink for all extracted data and metadata. - * </ul> - * - * <p>The following code snippet includes a usage example: - * - * <pre> - * MyOutputConsumer myOutputConsumer = new MyOutputConsumer(); - * MyInputReader myInputReader = new MyInputReader("www.example.com"); - * MediaParser mediaParser = MediaParser.create(myOutputConsumer); - * - * while (mediaParser.advance(myInputReader)) {} - * - * mediaParser.release(); - * mediaParser = null; - * </pre> - * - * <p>The following code snippet provides a rudimentary {@link OutputConsumer} sample implementation - * which extracts and publishes all video samples: - * - * <pre> - * class VideoOutputConsumer implements MediaParser.OutputConsumer { - * - * private byte[] sampleDataBuffer = new byte[4096]; - * private byte[] discardedDataBuffer = new byte[4096]; - * private int videoTrackIndex = -1; - * private int bytesWrittenCount = 0; - * - * @Override - * public void onSeekMapFound(int i, @NonNull MediaFormat mediaFormat) { - * // Do nothing. - * } - * - * @Override - * public void onTrackDataFound(int i, @NonNull TrackData trackData) { - * MediaFormat mediaFormat = trackData.mediaFormat; - * if (videoTrackIndex == -1 && - * mediaFormat - * .getString(MediaFormat.KEY_MIME, /* defaultValue= */ "") - * .startsWith("video/")) { - * videoTrackIndex = i; - * } - * } - * - * @Override - * public void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) - * throws IOException { - * int numberOfBytesToRead = (int) inputReader.getLength(); - * if (videoTrackIndex != trackIndex) { - * // Discard contents. - * inputReader.read( - * discardedDataBuffer, - * /* offset= */ 0, - * Math.min(discardDataBuffer.length, numberOfBytesToRead)); - * } else { - * ensureSpaceInBuffer(numberOfBytesToRead); - * int bytesRead = inputReader.read( - * sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead); - * bytesWrittenCount += bytesRead; - * } - * } - * - * @Override - * public void onSampleCompleted( - * int trackIndex, - * long timeMicros, - * int flags, - * int size, - * int offset, - * @Nullable CryptoInfo cryptoData) { - * if (videoTrackIndex != trackIndex) { - * return; // It's not the video track. Ignore. - * } - * byte[] sampleData = new byte[size]; - * int sampleStartOffset = bytesWrittenCount - size - offset; - * System.arraycopy( - * sampleDataBuffer, - * sampleStartOffset, - * sampleData, - * /* destPos= */ 0, - * size); - * // Place trailing bytes at the start of the buffer. - * System.arraycopy( - * sampleDataBuffer, - * bytesWrittenCount - offset, - * sampleDataBuffer, - * /* destPos= */ 0, - * /* size= */ offset); - * bytesWrittenCount = bytesWrittenCount - offset; - * publishSample(sampleData, timeMicros, flags); - * } - * - * private void ensureSpaceInBuffer(int numberOfBytesToRead) { - * int requiredLength = bytesWrittenCount + numberOfBytesToRead; - * if (requiredLength > sampleDataBuffer.length) { - * sampleDataBuffer = Arrays.copyOf(sampleDataBuffer, requiredLength); - * } - * } - * - * } - * - * </pre> - */ -public final class MediaParser { - - /** - * Maps seek positions to {@link SeekPoint SeekPoints} in the stream. - * - * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start - * playing media samples. - */ - public static final class SeekMap { - - /** Returned by {@link #getDurationMicros()} when the duration is unknown. */ - public static final int UNKNOWN_DURATION = Integer.MIN_VALUE; - - /** - * For each {@link #getSeekPoints} call, returns a single {@link SeekPoint} whose {@link - * SeekPoint#timeMicros} matches the requested timestamp, and whose {@link - * SeekPoint#position} is 0. - * - * @hide - */ - public static final SeekMap DUMMY = new SeekMap(new DummyExoPlayerSeekMap()); - - private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap; - - private SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { - mExoPlayerSeekMap = exoplayerSeekMap; - } - - /** Returns whether seeking is supported. */ - public boolean isSeekable() { - return mExoPlayerSeekMap.isSeekable(); - } - - /** - * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the - * duration is unknown. - */ - public long getDurationMicros() { - long durationUs = mExoPlayerSeekMap.getDurationUs(); - return durationUs != C.TIME_UNSET ? durationUs : UNKNOWN_DURATION; - } - - /** - * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds. - * - * <p>{@code getSeekPoints(timeMicros).first} contains the latest seek point for samples - * with timestamp equal to or smaller than {@code timeMicros}. - * - * <p>{@code getSeekPoints(timeMicros).second} contains the earliest seek point for samples - * with timestamp equal to or greater than {@code timeMicros}. If a seek point exists for - * {@code timeMicros}, the returned pair will contain the same {@link SeekPoint} twice. - * - * @param timeMicros A seek time in microseconds. - * @return The corresponding {@link SeekPoint SeekPoints}. - */ - @NonNull - public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeMicros) { - SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeMicros); - return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second)); - } - } - - /** Holds information associated with a track. */ - public static final class TrackData { - - /** Holds {@link MediaFormat} information for the track. */ - @NonNull public final MediaFormat mediaFormat; - - /** - * Holds {@link DrmInitData} necessary to acquire keys associated with the track, or null if - * the track has no encryption data. - */ - @Nullable public final DrmInitData drmInitData; - - private TrackData(MediaFormat mediaFormat, DrmInitData drmInitData) { - this.mediaFormat = mediaFormat; - this.drmInitData = drmInitData; - } - } - - /** Defines a seek point in a media stream. */ - public static final class SeekPoint { - - /** A {@link SeekPoint} whose time and byte offset are both set to 0. */ - @NonNull public static final SeekPoint START = new SeekPoint(0, 0); - - /** The time of the seek point, in microseconds. */ - public final long timeMicros; - - /** The byte offset of the seek point. */ - public final long position; - - /** - * @param timeMicros The time of the seek point, in microseconds. - * @param position The byte offset of the seek point. - */ - private SeekPoint(long timeMicros, long position) { - this.timeMicros = timeMicros; - this.position = position; - } - - @Override - @NonNull - public String toString() { - return "[timeMicros=" + timeMicros + ", position=" + position + "]"; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - SeekPoint other = (SeekPoint) obj; - return timeMicros == other.timeMicros && position == other.position; - } - - @Override - public int hashCode() { - int result = (int) timeMicros; - result = 31 * result + (int) position; - return result; - } - } - - /** Provides input data to {@link MediaParser}. */ - public interface InputReader { - - /** - * Reads up to {@code readLength} bytes of data and stores them into {@code buffer}, - * starting at index {@code offset}. - * - * <p>This method blocks until at least one byte is read, the end of input is detected, or - * an exception is thrown. The read position advances to the first unread byte. - * - * @param buffer The buffer into which the read data should be stored. - * @param offset The start offset into {@code buffer} at which data should be written. - * @param readLength The maximum number of bytes to read. - * @return The non-zero number of bytes read, or -1 if no data is available because the end - * of the input has been reached. - * @throws java.io.IOException If an error occurs reading from the source. - */ - int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException; - - /** Returns the current read position (byte offset) in the stream. */ - long getPosition(); - - /** Returns the length of the input in bytes, or -1 if the length is unknown. */ - long getLength(); - } - - /** {@link InputReader} that allows setting the read position. */ - public interface SeekableInputReader extends InputReader { - - /** - * Sets the read position at the given {@code position}. - * - * <p>{@link #advance} will immediately return after calling this method. - * - * @param position The position to seek to, in bytes. - */ - void seekToPosition(long position); - } - - /** Receives extracted media sample data and metadata from {@link MediaParser}. */ - public interface OutputConsumer { - - /** - * Called when a {@link SeekMap} has been extracted from the stream. - * - * <p>This method is called at least once before any samples are {@link #onSampleCompleted - * complete}. May be called multiple times after that in order to add {@link SeekPoint - * SeekPoints}. - * - * @param seekMap The extracted {@link SeekMap}. - */ - void onSeekMapFound(@NonNull SeekMap seekMap); - - /** - * Called when the number of tracks is found. - * - * @param numberOfTracks The number of tracks in the stream. - */ - void onTrackCountFound(int numberOfTracks); - - /** - * Called when new {@link TrackData} is found in the stream. - * - * @param trackIndex The index of the track for which the {@link TrackData} was extracted. - * @param trackData The extracted {@link TrackData}. - */ - void onTrackDataFound(int trackIndex, @NonNull TrackData trackData); - - /** - * Called when sample data is found in the stream. - * - * <p>If the invocation of this method returns before the entire {@code inputReader} {@link - * InputReader#getLength() length} is consumed, the method will be called again for the - * implementer to read the remaining data. Implementers should surface any thrown {@link - * IOException} caused by reading from {@code input}. - * - * @param trackIndex The index of the track to which the sample data corresponds. - * @param inputReader The {@link InputReader} from which to read the data. - * @throws IOException If an exception occurs while reading from {@code inputReader}. - */ - void onSampleDataFound(int trackIndex, @NonNull InputReader inputReader) throws IOException; - - /** - * Called once all the data of a sample has been passed to {@link #onSampleDataFound}. - * - * <p>Includes sample metadata, like presentation timestamp and flags. - * - * @param trackIndex The index of the track to which the sample corresponds. - * @param timeMicros The media timestamp associated with the sample, in microseconds. - * @param flags Flags associated with the sample. See the {@code SAMPLE_FLAG_*} constants. - * @param size The size of the sample data, in bytes. - * @param offset The number of bytes that have been consumed by {@code - * onSampleDataFound(int, MediaParser.InputReader)} for the specified track, since the - * last byte belonging to the sample whose metadata is being passed. - * @param cryptoInfo Encryption data required to decrypt the sample. May be null for - * unencrypted samples. Implementors should treat any output {@link CryptoInfo} - * instances as immutable. MediaParser will not modify any output {@code cryptoInfos} - * and implementors should not modify them either. - */ - void onSampleCompleted( - int trackIndex, - long timeMicros, - @SampleFlags int flags, - int size, - int offset, - @Nullable CryptoInfo cryptoInfo); - } - - /** - * Thrown if all parser implementations provided to {@link #create} failed to sniff the input - * content. - */ - public static final class UnrecognizedInputFormatException extends IOException { - - /** - * Creates a new instance which signals that the parsers with the given names failed to - * parse the input. - */ - @NonNull - @CheckResult - private static UnrecognizedInputFormatException createForExtractors( - @NonNull String... extractorNames) { - StringBuilder builder = new StringBuilder(); - builder.append("None of the available parsers ( "); - builder.append(extractorNames[0]); - for (int i = 1; i < extractorNames.length; i++) { - builder.append(", "); - builder.append(extractorNames[i]); - } - builder.append(") could read the stream."); - return new UnrecognizedInputFormatException(builder.toString()); - } - - private UnrecognizedInputFormatException(String extractorNames) { - super(extractorNames); - } - } - - /** Thrown when an error occurs while parsing a media stream. */ - public static final class ParsingException extends IOException { - - private ParsingException(ParserException cause) { - super(cause); - } - } - - // Sample flags. - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef( - flag = true, - value = { - SAMPLE_FLAG_KEY_FRAME, - SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA, - SAMPLE_FLAG_LAST_SAMPLE, - SAMPLE_FLAG_ENCRYPTED, - SAMPLE_FLAG_DECODE_ONLY - }) - public @interface SampleFlags {} - /** Indicates that the sample holds a synchronization sample. */ - public static final int SAMPLE_FLAG_KEY_FRAME = MediaCodec.BUFFER_FLAG_KEY_FRAME; - /** - * Indicates that the sample has supplemental data. - * - * <p>Samples will not have this flag set unless the {@code - * "android.media.mediaparser.includeSupplementalData"} parameter is set to {@code true} via - * {@link #setParameter}. - * - * <p>Samples with supplemental data have the following sample data format: - * - * <ul> - * <li>If the {@code "android.media.mediaparser.inBandCryptoInfo"} parameter is set, all - * encryption information. - * <li>(4 bytes) {@code sample_data_size}: The size of the actual sample data, not including - * supplemental data or encryption information. - * <li>({@code sample_data_size} bytes): The media sample data. - * <li>(remaining bytes) The supplemental data. - * </ul> - */ - public static final int SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA = 1 << 28; - /** Indicates that the sample is known to contain the last media sample of the stream. */ - public static final int SAMPLE_FLAG_LAST_SAMPLE = 1 << 29; - /** Indicates that the sample is (at least partially) encrypted. */ - public static final int SAMPLE_FLAG_ENCRYPTED = 1 << 30; - /** Indicates that the sample should be decoded but not rendered. */ - public static final int SAMPLE_FLAG_DECODE_ONLY = 1 << 31; - - // Parser implementation names. - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @StringDef( - prefix = {"PARSER_NAME_"}, - value = { - PARSER_NAME_UNKNOWN, - PARSER_NAME_MATROSKA, - PARSER_NAME_FMP4, - PARSER_NAME_MP4, - PARSER_NAME_MP3, - PARSER_NAME_ADTS, - PARSER_NAME_AC3, - PARSER_NAME_TS, - PARSER_NAME_FLV, - PARSER_NAME_OGG, - PARSER_NAME_PS, - PARSER_NAME_WAV, - PARSER_NAME_AMR, - PARSER_NAME_AC4, - PARSER_NAME_FLAC - }) - public @interface ParserName {} - - /** Parser name returned by {@link #getParserName()} when no parser has been selected yet. */ - public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN"; - /** - * Parser for the Matroska container format, as defined in the <a - * href="https://matroska.org/technical/specs/">spec</a>. - */ - public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser"; - /** - * Parser for fragmented files using the MP4 container format, as defined in ISO/IEC 14496-12. - */ - public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser"; - /** - * Parser for non-fragmented files using the MP4 container format, as defined in ISO/IEC - * 14496-12. - */ - public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser"; - /** Parser for the MP3 container format, as defined in ISO/IEC 11172-3. */ - public static final String PARSER_NAME_MP3 = "android.media.mediaparser.Mp3Parser"; - /** Parser for the ADTS container format, as defined in ISO/IEC 13818-7. */ - public static final String PARSER_NAME_ADTS = "android.media.mediaparser.AdtsParser"; - /** - * Parser for the AC-3 container format, as defined in Digital Audio Compression Standard - * (AC-3). - */ - public static final String PARSER_NAME_AC3 = "android.media.mediaparser.Ac3Parser"; - /** Parser for the TS container format, as defined in ISO/IEC 13818-1. */ - public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser"; - /** - * Parser for the FLV container format, as defined in Adobe Flash Video File Format - * Specification. - */ - public static final String PARSER_NAME_FLV = "android.media.mediaparser.FlvParser"; - /** Parser for the OGG container format, as defined in RFC 3533. */ - public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser"; - /** Parser for the PS container format, as defined in ISO/IEC 11172-1. */ - public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser"; - /** - * Parser for the WAV container format, as defined in Multimedia Programming Interface and Data - * Specifications. - */ - public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser"; - /** Parser for the AMR container format, as defined in RFC 4867. */ - public static final String PARSER_NAME_AMR = "android.media.mediaparser.AmrParser"; - /** - * Parser for the AC-4 container format, as defined by Dolby AC-4: Audio delivery for - * Next-Generation Entertainment Services. - */ - public static final String PARSER_NAME_AC4 = "android.media.mediaparser.Ac4Parser"; - /** - * Parser for the FLAC container format, as defined in the <a - * href="https://xiph.org/flac/">spec</a>. - */ - public static final String PARSER_NAME_FLAC = "android.media.mediaparser.FlacParser"; - - // MediaParser parameters. - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @StringDef( - prefix = {"PARAMETER_"}, - value = { - PARAMETER_ADTS_ENABLE_CBR_SEEKING, - PARAMETER_AMR_ENABLE_CBR_SEEKING, - PARAMETER_FLAC_DISABLE_ID3, - PARAMETER_MP4_IGNORE_EDIT_LISTS, - PARAMETER_MP4_IGNORE_TFDT_BOX, - PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, - PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, - PARAMETER_MP3_DISABLE_ID3, - PARAMETER_MP3_ENABLE_CBR_SEEKING, - PARAMETER_MP3_ENABLE_INDEX_SEEKING, - PARAMETER_TS_MODE, - PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, - PARAMETER_TS_IGNORE_AAC_STREAM, - PARAMETER_TS_IGNORE_AVC_STREAM, - PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, - PARAMETER_TS_DETECT_ACCESS_UNITS, - PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, - PARAMETER_IN_BAND_CRYPTO_INFO, - PARAMETER_INCLUDE_SUPPLEMENTAL_DATA - }) - public @interface ParameterName {} - - /** - * Sets whether constant bitrate seeking should be enabled for ADTS parsing. {@code boolean} - * expected. Default value is {@code false}. - */ - public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = - "android.media.mediaparser.adts.enableCbrSeeking"; - /** - * Sets whether constant bitrate seeking should be enabled for AMR. {@code boolean} expected. - * Default value is {@code false}. - */ - public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = - "android.media.mediaparser.amr.enableCbrSeeking"; - /** - * Sets whether the ID3 track should be disabled for FLAC. {@code boolean} expected. Default - * value is {@code false}. - */ - public static final String PARAMETER_FLAC_DISABLE_ID3 = - "android.media.mediaparser.flac.disableId3"; - /** - * Sets whether MP4 parsing should ignore edit lists. {@code boolean} expected. Default value is - * {@code false}. - */ - public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = - "android.media.mediaparser.mp4.ignoreEditLists"; - /** - * Sets whether MP4 parsing should ignore the tfdt box. {@code boolean} expected. Default value - * is {@code false}. - */ - public static final String PARAMETER_MP4_IGNORE_TFDT_BOX = - "android.media.mediaparser.mp4.ignoreTfdtBox"; - /** - * Sets whether MP4 parsing should treat all video frames as key frames. {@code boolean} - * expected. Default value is {@code false}. - */ - public static final String PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = - "android.media.mediaparser.mp4.treatVideoFramesAsKeyframes"; - /** - * Sets whether Matroska parsing should avoid seeking to the cues element. {@code boolean} - * expected. Default value is {@code false}. - * - * <p>If this flag is enabled and the cues element occurs after the first cluster, then the - * media is treated as unseekable. - */ - public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = - "android.media.mediaparser.matroska.disableCuesSeeking"; - /** - * Sets whether the ID3 track should be disabled for MP3. {@code boolean} expected. Default - * value is {@code false}. - */ - public static final String PARAMETER_MP3_DISABLE_ID3 = - "android.media.mediaparser.mp3.disableId3"; - /** - * Sets whether constant bitrate seeking should be enabled for MP3. {@code boolean} expected. - * Default value is {@code false}. - */ - public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = - "android.media.mediaparser.mp3.enableCbrSeeking"; - /** - * Sets whether MP3 parsing should generate a time-to-byte mapping. {@code boolean} expected. - * Default value is {@code false}. - * - * <p>Enabling this flag may require to scan a significant portion of the file to compute a seek - * point. Therefore, it should only be used if: - * - * <ul> - * <li>the file is small, or - * <li>the bitrate is variable (or the type of bitrate is unknown) and the seeking metadata - * provided in the file is not precise enough (or is not present). - * </ul> - */ - public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = - "android.media.mediaparser.mp3.enableIndexSeeking"; - /** - * Sets the operation mode for TS parsing. {@code String} expected. Valid values are {@code - * "single_pmt"}, {@code "multi_pmt"}, and {@code "hls"}. Default value is {@code "single_pmt"}. - * - * <p>The operation modes alter the way TS behaves so that it can handle certain kinds of - * commonly-occurring malformed media. - * - * <ul> - * <li>{@code "single_pmt"}: Only the first found PMT is parsed. Others are ignored, even if - * more PMTs are declared in the PAT. - * <li>{@code "multi_pmt"}: Behave as described in ISO/IEC 13818-1. - * <li>{@code "hls"}: Enable {@code "single_pmt"} mode, and ignore continuity counters. - * </ul> - */ - public static final String PARAMETER_TS_MODE = "android.media.mediaparser.ts.mode"; - /** - * Sets whether TS should treat samples consisting of non-IDR I slices as synchronization - * samples (key-frames). {@code boolean} expected. Default value is {@code false}. - */ - public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = - "android.media.mediaparser.ts.allowNonIdrAvcKeyframes"; - /** - * Sets whether TS parsing should ignore AAC elementary streams. {@code boolean} expected. - * Default value is {@code false}. - */ - public static final String PARAMETER_TS_IGNORE_AAC_STREAM = - "android.media.mediaparser.ts.ignoreAacStream"; - /** - * Sets whether TS parsing should ignore AVC elementary streams. {@code boolean} expected. - * Default value is {@code false}. - */ - public static final String PARAMETER_TS_IGNORE_AVC_STREAM = - "android.media.mediaparser.ts.ignoreAvcStream"; - /** - * Sets whether TS parsing should ignore splice information streams. {@code boolean} expected. - * Default value is {@code false}. - */ - public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = - "android.media.mediaparser.ts.ignoreSpliceInfoStream"; - /** - * Sets whether TS parsing should split AVC stream into access units based on slice headers. - * {@code boolean} expected. Default value is {@code false}. - * - * <p>This flag should be left disabled if the stream contains access units delimiters in order - * to avoid unnecessary computational costs. - */ - public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = - "android.media.mediaparser.ts.ignoreDetectAccessUnits"; - /** - * Sets whether TS parsing should handle HDMV DTS audio streams. {@code boolean} expected. - * Default value is {@code false}. - * - * <p>Enabling this flag will disable the detection of SCTE subtitles. - */ - public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = - "android.media.mediaparser.ts.enableHdmvDtsAudioStreams"; - /** - * Sets whether encryption data should be sent in-band with the sample data, as per {@link - * OutputConsumer#onSampleDataFound}. {@code boolean} expected. Default value is {@code false}. - * - * <p>If this parameter is set, encrypted samples' data will be prefixed with the encryption - * information bytes. The format for in-band encryption information is: - * - * <ul> - * <li>(1 byte) {@code encryption_signal_byte}: Most significant bit signals whether the - * encryption data contains subsample encryption data. The remaining bits contain {@code - * initialization_vector_size}. - * <li>({@code initialization_vector_size} bytes) Initialization vector. - * <li>If subsample encryption data is present, as per {@code encryption_signal_byte}, the - * encryption data also contains: - * <ul> - * <li>(2 bytes) {@code subsample_encryption_data_length}. - * <li>({@code subsample_encryption_data_length * 6} bytes) Subsample encryption data - * (repeated {@code subsample_encryption_data_length} times): - * <ul> - * <li>(2 bytes) Size of a clear section in sample. - * <li>(4 bytes) Size of an encryption section in sample. - * </ul> - * </ul> - * </ul> - * - * @hide - */ - public static final String PARAMETER_IN_BAND_CRYPTO_INFO = - "android.media.mediaparser.inBandCryptoInfo"; - /** - * Sets whether supplemental data should be included as part of the sample data. {@code boolean} - * expected. Default value is {@code false}. See {@link #SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA} for - * information about the sample data format. - * - * @hide - */ - public static final String PARAMETER_INCLUDE_SUPPLEMENTAL_DATA = - "android.media.mediaparser.includeSupplementalData"; - /** - * Sets whether sample timestamps may start from non-zero offsets. {@code boolean} expected. - * Default value is {@code false}. - * - * <p>When set to true, sample timestamps will not be offset to start from zero, and the media - * provided timestamps will be used instead. For example, transport stream sample timestamps - * will not be converted to a zero-based timebase. - * - * @hide - */ - public static final String PARAMETER_IGNORE_TIMESTAMP_OFFSET = - "android.media.mediaparser.ignoreTimestampOffset"; - /** - * Sets whether each track type should be eagerly exposed. {@code boolean} expected. Default - * value is {@code false}. - * - * <p>When set to true, each track type will be eagerly exposed through a call to {@link - * OutputConsumer#onTrackDataFound} containing a single-value {@link MediaFormat}. The key for - * the track type is {@code "track-type-string"}, and the possible values are {@code "video"}, - * {@code "audio"}, {@code "text"}, {@code "metadata"}, and {@code "unknown"}. - * - * @hide - */ - public static final String PARAMETER_EAGERLY_EXPOSE_TRACKTYPE = - "android.media.mediaparser.eagerlyExposeTrackType"; - /** - * Sets whether a dummy {@link SeekMap} should be exposed before starting extraction. {@code - * boolean} expected. Default value is {@code false}. - * - * <p>For each {@link SeekMap#getSeekPoints} call, the dummy {@link SeekMap} returns a single - * {@link SeekPoint} whose {@link SeekPoint#timeMicros} matches the requested timestamp, and - * whose {@link SeekPoint#position} is 0. - * - * @hide - */ - public static final String PARAMETER_EXPOSE_DUMMY_SEEKMAP = - "android.media.mediaparser.exposeDummySeekMap"; - - /** - * Sets whether chunk indices available in the extracted media should be exposed as {@link - * MediaFormat MediaFormats}. {@code boolean} expected. Default value is {@link false}. - * - * <p>When set to true, any information about media segmentation will be exposed as a {@link - * MediaFormat} (with track index 0) containing four {@link ByteBuffer} elements under the - * following keys: - * - * <ul> - * <li>"chunk-index-int-sizes": Contains {@code ints} representing the sizes in bytes of each - * of the media segments. - * <li>"chunk-index-long-offsets": Contains {@code longs} representing the byte offsets of - * each segment in the stream. - * <li>"chunk-index-long-us-durations": Contains {@code longs} representing the media duration - * of each segment, in microseconds. - * <li>"chunk-index-long-us-times": Contains {@code longs} representing the start time of each - * segment, in microseconds. - * </ul> - * - * @hide - */ - public static final String PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT = - "android.media.mediaParser.exposeChunkIndexAsMediaFormat"; - /** - * Sets a list of closed-caption {@link MediaFormat MediaFormats} that should be exposed as part - * of the extracted media. {@code List<MediaFormat>} expected. Default value is an empty list. - * - * <p>Expected keys in the {@link MediaFormat} are: - * - * <ul> - * <p>{@link MediaFormat#KEY_MIME}: Determine the type of captions (for example, - * application/cea-608). Mandatory. - * <p>{@link MediaFormat#KEY_CAPTION_SERVICE_NUMBER}: Determine the channel on which the - * captions are transmitted. Optional. - * </ul> - * - * @hide - */ - public static final String PARAMETER_EXPOSE_CAPTION_FORMATS = - "android.media.mediaParser.exposeCaptionFormats"; - /** - * Sets whether the value associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS} should - * override any in-band caption service declarations. {@code boolean} expected. Default value is - * {@link false}. - * - * <p>When {@code false}, any present in-band caption services information will override the - * values associated with {@link #PARAMETER_EXPOSE_CAPTION_FORMATS}. - * - * @hide - */ - public static final String PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS = - "android.media.mediaParser.overrideInBandCaptionDeclarations"; - /** - * Sets whether a track for EMSG events should be exposed in case of parsing a container that - * supports them. {@code boolean} expected. Default value is {@link false}. - * - * @hide - */ - public static final String PARAMETER_EXPOSE_EMSG_TRACK = - "android.media.mediaParser.exposeEmsgTrack"; - - // Private constants. - - private static final String TAG = "MediaParser"; - private static final String JNI_LIBRARY_NAME = "mediaparser-jni"; - private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME; - private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME; - private static final String TS_MODE_SINGLE_PMT = "single_pmt"; - private static final String TS_MODE_MULTI_PMT = "multi_pmt"; - private static final String TS_MODE_HLS = "hls"; - private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6; - private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - private static final String MEDIAMETRICS_ELEMENT_SEPARATOR = "|"; - private static final int MEDIAMETRICS_MAX_STRING_SIZE = 200; - private static final int MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH; - /** - * Intentional error introduced to reported metrics to prevent identification of the parsed - * media. Note: Increasing this value may cause older hostside CTS tests to fail. - */ - private static final float MEDIAMETRICS_DITHER = .02f; - - @IntDef( - value = { - STATE_READING_SIGNAL_BYTE, - STATE_READING_INIT_VECTOR, - STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE, - STATE_READING_SUBSAMPLE_ENCRYPTION_DATA - }) - private @interface EncryptionDataReadState {} - - private static final int STATE_READING_SIGNAL_BYTE = 0; - private static final int STATE_READING_INIT_VECTOR = 1; - private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE = 2; - private static final int STATE_READING_SUBSAMPLE_ENCRYPTION_DATA = 3; - - // Instance creation methods. - - /** - * Creates an instance backed by the parser with the given {@code name}. The returned instance - * will attempt parsing without sniffing the content. - * - * @param name The name of the parser that will be associated with the created instance. - * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed. - * @return A new instance. - * @throws IllegalArgumentException If an invalid name is provided. - */ - @NonNull - public static MediaParser createByName( - @NonNull @ParserName String name, @NonNull OutputConsumer outputConsumer) { - String[] nameAsArray = new String[] {name}; - assertValidNames(nameAsArray); - return new MediaParser(outputConsumer, /* createdByName= */ true, name); - } - - /** - * Creates an instance whose backing parser will be selected by sniffing the content during the - * first {@link #advance} call. Parser implementations will sniff the content in order of - * appearance in {@code parserNames}. - * - * @param outputConsumer The {@link OutputConsumer} to which extracted data is output. - * @param parserNames The names of the parsers to sniff the content with. If empty, a default - * array of names is used. - * @return A new instance. - */ - @NonNull - public static MediaParser create( - @NonNull OutputConsumer outputConsumer, @NonNull @ParserName String... parserNames) { - assertValidNames(parserNames); - if (parserNames.length == 0) { - parserNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]); - } - return new MediaParser(outputConsumer, /* createdByName= */ false, parserNames); - } - - // Misc static methods. - - /** - * Returns an immutable list with the names of the parsers that are suitable for container - * formats with the given {@link MediaFormat}. - * - * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link - * MediaFormat#KEY_MIME} corresponds to the supported container format. - * - * @param mediaFormat The {@link MediaFormat} to check support for. - * @return The parser names that support the given {@code mediaFormat}, or the list of all - * parsers available if no container specific format information is provided. - */ - @NonNull - @ParserName - public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) { - String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME); - mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim()); - if (TextUtils.isEmpty(mimeType)) { - // No MIME type provided. Return all. - return Collections.unmodifiableList( - new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet())); - } - ArrayList<String> result = new ArrayList<>(); - switch (mimeType) { - case "video/x-matroska": - case "audio/x-matroska": - case "video/x-webm": - case "audio/x-webm": - result.add(PARSER_NAME_MATROSKA); - break; - case "video/mp4": - case "audio/mp4": - case "application/mp4": - result.add(PARSER_NAME_MP4); - result.add(PARSER_NAME_FMP4); - break; - case "audio/mpeg": - result.add(PARSER_NAME_MP3); - break; - case "audio/aac": - result.add(PARSER_NAME_ADTS); - break; - case "audio/ac3": - result.add(PARSER_NAME_AC3); - break; - case "video/mp2t": - case "audio/mp2t": - result.add(PARSER_NAME_TS); - break; - case "video/x-flv": - result.add(PARSER_NAME_FLV); - break; - case "video/ogg": - case "audio/ogg": - case "application/ogg": - result.add(PARSER_NAME_OGG); - break; - case "video/mp2p": - case "video/mp1s": - result.add(PARSER_NAME_PS); - break; - case "audio/vnd.wave": - case "audio/wav": - case "audio/wave": - case "audio/x-wav": - result.add(PARSER_NAME_WAV); - break; - case "audio/amr": - result.add(PARSER_NAME_AMR); - break; - case "audio/ac4": - result.add(PARSER_NAME_AC4); - break; - case "audio/flac": - case "audio/x-flac": - result.add(PARSER_NAME_FLAC); - break; - default: - // No parsers support the given mime type. Do nothing. - break; - } - return Collections.unmodifiableList(result); - } - - // Private fields. - - private final Map<String, Object> mParserParameters; - private final OutputConsumer mOutputConsumer; - private final String[] mParserNamesPool; - private final PositionHolder mPositionHolder; - private final InputReadingDataReader mExoDataReader; - private final DataReaderAdapter mScratchDataReaderAdapter; - private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter; - @Nullable private final Constructor<DrmInitData.SchemeInitData> mSchemeInitDataConstructor; - private final ArrayList<Format> mMuxedCaptionFormats; - private boolean mInBandCryptoInfo; - private boolean mIncludeSupplementalData; - private boolean mIgnoreTimestampOffset; - private boolean mEagerlyExposeTrackType; - private boolean mExposeDummySeekMap; - private boolean mExposeChunkIndexAsMediaFormat; - private String mParserName; - private Extractor mExtractor; - private ExtractorInput mExtractorInput; - private boolean mPendingExtractorInit; - private long mPendingSeekPosition; - private long mPendingSeekTimeMicros; - private boolean mLoggedSchemeInitDataCreationException; - private boolean mReleased; - - // MediaMetrics fields. - @Nullable private LogSessionId mLogSessionId; - private final boolean mCreatedByName; - private final SparseArray<Format> mTrackFormats; - private String mLastObservedExceptionName; - private long mDurationMillis; - private long mResourceByteCount; - - // Public methods. - - /** - * Sets parser-specific parameters which allow customizing behavior. - * - * <p>Must be called before the first call to {@link #advance}. - * - * @param parameterName The name of the parameter to set. See {@code PARAMETER_*} constants for - * documentation on possible values. - * @param value The value to set for the given {@code parameterName}. See {@code PARAMETER_*} - * constants for documentation on the expected types. - * @return This instance, for convenience. - * @throws IllegalStateException If called after calling {@link #advance} on the same instance. - */ - @NonNull - public MediaParser setParameter( - @NonNull @ParameterName String parameterName, @NonNull Object value) { - if (mExtractor != null) { - throw new IllegalStateException( - "setParameters() must be called before the first advance() call."); - } - Class expectedType = EXPECTED_TYPE_BY_PARAMETER_NAME.get(parameterName); - // Ignore parameter names that are not contained in the map, in case the client is passing - // a parameter that is being added in a future version of this library. - if (expectedType != null && !expectedType.isInstance(value)) { - throw new IllegalArgumentException( - parameterName - + " expects a " - + expectedType.getSimpleName() - + " but a " - + value.getClass().getSimpleName() - + " was passed."); - } - if (PARAMETER_TS_MODE.equals(parameterName) - && !TS_MODE_SINGLE_PMT.equals(value) - && !TS_MODE_HLS.equals(value) - && !TS_MODE_MULTI_PMT.equals(value)) { - throw new IllegalArgumentException(PARAMETER_TS_MODE + " does not accept: " + value); - } - if (PARAMETER_IN_BAND_CRYPTO_INFO.equals(parameterName)) { - mInBandCryptoInfo = (boolean) value; - } - if (PARAMETER_INCLUDE_SUPPLEMENTAL_DATA.equals(parameterName)) { - mIncludeSupplementalData = (boolean) value; - } - if (PARAMETER_IGNORE_TIMESTAMP_OFFSET.equals(parameterName)) { - mIgnoreTimestampOffset = (boolean) value; - } - if (PARAMETER_EAGERLY_EXPOSE_TRACKTYPE.equals(parameterName)) { - mEagerlyExposeTrackType = (boolean) value; - } - if (PARAMETER_EXPOSE_DUMMY_SEEKMAP.equals(parameterName)) { - mExposeDummySeekMap = (boolean) value; - } - if (PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT.equals(parameterName)) { - mExposeChunkIndexAsMediaFormat = (boolean) value; - } - if (PARAMETER_EXPOSE_CAPTION_FORMATS.equals(parameterName)) { - setMuxedCaptionFormats((List<MediaFormat>) value); - } - mParserParameters.put(parameterName, value); - return this; - } - - /** - * Returns whether the given {@code parameterName} is supported by this parser. - * - * @param parameterName The parameter name to check support for. One of the {@code PARAMETER_*} - * constants. - * @return Whether the given {@code parameterName} is supported. - */ - public boolean supportsParameter(@NonNull @ParameterName String parameterName) { - return EXPECTED_TYPE_BY_PARAMETER_NAME.containsKey(parameterName); - } - - /** - * Returns the name of the backing parser implementation. - * - * <p>If this instance was creating using {@link #createByName}, the provided name is returned. - * If this instance was created using {@link #create}, this method will return {@link - * #PARSER_NAME_UNKNOWN} until the first call to {@link #advance}, after which the name of the - * backing parser implementation is returned. - * - * @return The name of the backing parser implementation, or null if the backing parser - * implementation has not yet been selected. - */ - @NonNull - @ParserName - public String getParserName() { - return mParserName; - } - - /** - * Makes progress in the extraction of the input media stream, unless the end of the input has - * been reached. - * - * <p>This method will block until some progress has been made. - * - * <p>If this instance was created using {@link #create}, the first call to this method will - * sniff the content using the selected parser implementations. - * - * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media - * container data. - * @return Whether there is any data left to extract. Returns false if the end of input has been - * reached. - * @throws IOException If an error occurs while reading from the {@link SeekableInputReader}. - * @throws UnrecognizedInputFormatException If the format cannot be recognized by any of the - * underlying parser implementations. - */ - public boolean advance(@NonNull SeekableInputReader seekableInputReader) throws IOException { - if (mExtractorInput == null) { - // TODO: For efficiency, the same implementation should be used, by providing a - // clearBuffers() method, or similar. - long resourceLength = seekableInputReader.getLength(); - if (mResourceByteCount == 0) { - // For resource byte count metric collection, we only take into account the length - // of the first provided input reader. - mResourceByteCount = resourceLength; - } - mExtractorInput = - new DefaultExtractorInput( - mExoDataReader, seekableInputReader.getPosition(), resourceLength); - } - mExoDataReader.mInputReader = seekableInputReader; - - if (mExtractor == null) { - mPendingExtractorInit = true; - if (!mParserName.equals(PARSER_NAME_UNKNOWN)) { - mExtractor = createExtractor(mParserName); - } else { - for (String parserName : mParserNamesPool) { - Extractor extractor = createExtractor(parserName); - try { - if (extractor.sniff(mExtractorInput)) { - mParserName = parserName; - mExtractor = extractor; - mPendingExtractorInit = true; - break; - } - } catch (EOFException e) { - // Do nothing. - } finally { - mExtractorInput.resetPeekPosition(); - } - } - if (mExtractor == null) { - UnrecognizedInputFormatException exception = - UnrecognizedInputFormatException.createForExtractors(mParserNamesPool); - mLastObservedExceptionName = exception.getClass().getName(); - throw exception; - } - return true; - } - } - - if (mPendingExtractorInit) { - if (mExposeDummySeekMap) { - // We propagate the dummy seek map before initializing the extractor, in case the - // extractor initialization outputs a seek map. - mOutputConsumer.onSeekMapFound(SeekMap.DUMMY); - } - mExtractor.init(new ExtractorOutputAdapter()); - mPendingExtractorInit = false; - // We return after initialization to allow clients use any output information before - // starting actual extraction. - return true; - } - - if (isPendingSeek()) { - mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeMicros); - removePendingSeek(); - } - - mPositionHolder.position = seekableInputReader.getPosition(); - int result; - try { - result = mExtractor.read(mExtractorInput, mPositionHolder); - } catch (Exception e) { - mLastObservedExceptionName = e.getClass().getName(); - if (e instanceof ParserException) { - throw new ParsingException((ParserException) e); - } else { - throw e; - } - } - if (result == Extractor.RESULT_END_OF_INPUT) { - mExtractorInput = null; - return false; - } - if (result == Extractor.RESULT_SEEK) { - mExtractorInput = null; - seekableInputReader.seekToPosition(mPositionHolder.position); - } - return true; - } - - /** - * Seeks within the media container being extracted. - * - * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link - * OutputConsumer#onSeekMapFound(SeekMap)}. - * - * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of - * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream. - * - * @param seekPoint The {@link SeekPoint} to seek to. - */ - public void seek(@NonNull SeekPoint seekPoint) { - if (mExtractor == null) { - mPendingSeekPosition = seekPoint.position; - mPendingSeekTimeMicros = seekPoint.timeMicros; - } else { - mExtractor.seek(seekPoint.position, seekPoint.timeMicros); - } - } - - /** - * Releases any acquired resources. - * - * <p>After calling this method, this instance becomes unusable and no other methods should be - * invoked. - */ - public void release() { - mExtractorInput = null; - mExtractor = null; - if (mReleased) { - // Nothing to do. - return; - } - mReleased = true; - - String trackMimeTypes = buildMediaMetricsString(format -> format.sampleMimeType); - String trackCodecs = buildMediaMetricsString(format -> format.codecs); - int videoWidth = -1; - int videoHeight = -1; - for (int i = 0; i < mTrackFormats.size(); i++) { - Format format = mTrackFormats.valueAt(i); - if (format.width != Format.NO_VALUE && format.height != Format.NO_VALUE) { - videoWidth = format.width; - videoHeight = format.height; - break; - } - } - - String alteredParameters = - String.join( - MEDIAMETRICS_ELEMENT_SEPARATOR, - mParserParameters.keySet().toArray(new String[0])); - alteredParameters = - alteredParameters.substring( - 0, - Math.min( - alteredParameters.length(), - MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH)); - - nativeSubmitMetrics( - SdkLevel.isAtLeastS() ? getLogSessionIdStringV31() : "", - mParserName, - mCreatedByName, - String.join(MEDIAMETRICS_ELEMENT_SEPARATOR, mParserNamesPool), - mLastObservedExceptionName, - addDither(mResourceByteCount), - addDither(mDurationMillis), - trackMimeTypes, - trackCodecs, - alteredParameters, - videoWidth, - videoHeight); - } - - @RequiresApi(31) - public void setLogSessionId(@NonNull LogSessionId logSessionId) { - this.mLogSessionId = Objects.requireNonNull(logSessionId); - } - - @RequiresApi(31) - @NonNull - public LogSessionId getLogSessionId() { - return mLogSessionId != null ? mLogSessionId : LogSessionId.LOG_SESSION_ID_NONE; - } - - // Private methods. - - private MediaParser( - OutputConsumer outputConsumer, boolean createdByName, String... parserNamesPool) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { - throw new UnsupportedOperationException("Android version must be R or greater."); - } - mParserParameters = new HashMap<>(); - mOutputConsumer = outputConsumer; - mParserNamesPool = parserNamesPool; - mCreatedByName = createdByName; - mParserName = createdByName ? parserNamesPool[0] : PARSER_NAME_UNKNOWN; - mPositionHolder = new PositionHolder(); - mExoDataReader = new InputReadingDataReader(); - removePendingSeek(); - mScratchDataReaderAdapter = new DataReaderAdapter(); - mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter(); - mSchemeInitDataConstructor = getSchemeInitDataConstructor(); - mMuxedCaptionFormats = new ArrayList<>(); - - // MediaMetrics. - mTrackFormats = new SparseArray<>(); - mLastObservedExceptionName = ""; - mDurationMillis = -1; - } - - private String buildMediaMetricsString(Function<Format, String> formatFieldGetter) { - StringBuilder stringBuilder = new StringBuilder(); - for (int i = 0; i < mTrackFormats.size(); i++) { - if (i > 0) { - stringBuilder.append(MEDIAMETRICS_ELEMENT_SEPARATOR); - } - String fieldValue = formatFieldGetter.apply(mTrackFormats.valueAt(i)); - stringBuilder.append(fieldValue != null ? fieldValue : ""); - } - return stringBuilder.substring( - 0, Math.min(stringBuilder.length(), MEDIAMETRICS_MAX_STRING_SIZE)); - } - - private void setMuxedCaptionFormats(List<MediaFormat> mediaFormats) { - mMuxedCaptionFormats.clear(); - for (MediaFormat mediaFormat : mediaFormats) { - mMuxedCaptionFormats.add(toExoPlayerCaptionFormat(mediaFormat)); - } - } - - private boolean isPendingSeek() { - return mPendingSeekPosition >= 0; - } - - private void removePendingSeek() { - mPendingSeekPosition = -1; - mPendingSeekTimeMicros = -1; - } - - private Extractor createExtractor(String parserName) { - int flags = 0; - TimestampAdjuster timestampAdjuster = null; - if (mIgnoreTimestampOffset) { - timestampAdjuster = new TimestampAdjuster(TimestampAdjuster.DO_NOT_OFFSET); - } - switch (parserName) { - case PARSER_NAME_MATROSKA: - flags = - getBooleanParameter(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING) - ? MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES - : 0; - return new MatroskaExtractor(flags); - case PARSER_NAME_FMP4: - flags |= - getBooleanParameter(PARAMETER_EXPOSE_EMSG_TRACK) - ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK - : 0; - flags |= - getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS) - ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS - : 0; - flags |= - getBooleanParameter(PARAMETER_MP4_IGNORE_TFDT_BOX) - ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX - : 0; - flags |= - getBooleanParameter(PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES) - ? FragmentedMp4Extractor - .FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME - : 0; - return new FragmentedMp4Extractor( - flags, - timestampAdjuster, - /* sideloadedTrack= */ null, - mMuxedCaptionFormats); - case PARSER_NAME_MP4: - flags |= - getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS) - ? Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS - : 0; - return new Mp4Extractor(flags); - case PARSER_NAME_MP3: - flags |= - getBooleanParameter(PARAMETER_MP3_DISABLE_ID3) - ? Mp3Extractor.FLAG_DISABLE_ID3_METADATA - : 0; - flags |= - getBooleanParameter(PARAMETER_MP3_ENABLE_CBR_SEEKING) - ? Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING - : 0; - // TODO: Add index seeking once we update the ExoPlayer version. - return new Mp3Extractor(flags); - case PARSER_NAME_ADTS: - flags |= - getBooleanParameter(PARAMETER_ADTS_ENABLE_CBR_SEEKING) - ? AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING - : 0; - return new AdtsExtractor(flags); - case PARSER_NAME_AC3: - return new Ac3Extractor(); - case PARSER_NAME_TS: - flags |= - getBooleanParameter(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES) - ? DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES - : 0; - flags |= - getBooleanParameter(PARAMETER_TS_DETECT_ACCESS_UNITS) - ? DefaultTsPayloadReaderFactory.FLAG_DETECT_ACCESS_UNITS - : 0; - flags |= - getBooleanParameter(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS) - ? DefaultTsPayloadReaderFactory.FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS - : 0; - flags |= - getBooleanParameter(PARAMETER_TS_IGNORE_AAC_STREAM) - ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM - : 0; - flags |= - getBooleanParameter(PARAMETER_TS_IGNORE_AVC_STREAM) - ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM - : 0; - flags |= - getBooleanParameter(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM) - ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM - : 0; - flags |= - getBooleanParameter(PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS) - ? DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS - : 0; - String tsMode = getStringParameter(PARAMETER_TS_MODE, TS_MODE_SINGLE_PMT); - int hlsMode = - TS_MODE_SINGLE_PMT.equals(tsMode) - ? TsExtractor.MODE_SINGLE_PMT - : TS_MODE_HLS.equals(tsMode) - ? TsExtractor.MODE_HLS - : TsExtractor.MODE_MULTI_PMT; - return new TsExtractor( - hlsMode, - timestampAdjuster != null - ? timestampAdjuster - : new TimestampAdjuster(/* firstSampleTimestampUs= */ 0), - new DefaultTsPayloadReaderFactory(flags, mMuxedCaptionFormats)); - case PARSER_NAME_FLV: - return new FlvExtractor(); - case PARSER_NAME_OGG: - return new OggExtractor(); - case PARSER_NAME_PS: - return new PsExtractor(); - case PARSER_NAME_WAV: - return new WavExtractor(); - case PARSER_NAME_AMR: - flags |= - getBooleanParameter(PARAMETER_AMR_ENABLE_CBR_SEEKING) - ? AmrExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING - : 0; - return new AmrExtractor(flags); - case PARSER_NAME_AC4: - return new Ac4Extractor(); - case PARSER_NAME_FLAC: - flags |= - getBooleanParameter(PARAMETER_FLAC_DISABLE_ID3) - ? FlacExtractor.FLAG_DISABLE_ID3_METADATA - : 0; - return new FlacExtractor(flags); - default: - // Should never happen. - throw new IllegalStateException("Unexpected attempt to create: " + parserName); - } - } - - private boolean getBooleanParameter(String name) { - return (boolean) mParserParameters.getOrDefault(name, false); - } - - private String getStringParameter(String name, String defaultValue) { - return (String) mParserParameters.getOrDefault(name, defaultValue); - } - - @RequiresApi(31) - private String getLogSessionIdStringV31() { - return mLogSessionId != null ? mLogSessionId.getStringId() : ""; - } - - // Private classes. - - private static final class InputReadingDataReader implements DataReader { - - public InputReader mInputReader; - - @Override - public int read(byte[] buffer, int offset, int readLength) throws IOException { - return mInputReader.read(buffer, offset, readLength); - } - } - - private final class MediaParserDrmInitData extends DrmInitData { - - private final SchemeInitData[] mSchemeDatas; - - private MediaParserDrmInitData(com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData) - throws IllegalAccessException, InstantiationException, InvocationTargetException { - mSchemeDatas = new SchemeInitData[exoDrmInitData.schemeDataCount]; - for (int i = 0; i < mSchemeDatas.length; i++) { - mSchemeDatas[i] = toFrameworkSchemeInitData(exoDrmInitData.get(i)); - } - } - - @Override - @Nullable - public SchemeInitData get(UUID schemeUuid) { - for (SchemeInitData schemeInitData : mSchemeDatas) { - if (schemeInitData.uuid.equals(schemeUuid)) { - return schemeInitData; - } - } - return null; - } - - @Override - public SchemeInitData getSchemeInitDataAt(int index) { - return mSchemeDatas[index]; - } - - @Override - public int getSchemeInitDataCount() { - return mSchemeDatas.length; - } - - private DrmInitData.SchemeInitData toFrameworkSchemeInitData(SchemeData exoSchemeData) - throws IllegalAccessException, InvocationTargetException, InstantiationException { - return mSchemeInitDataConstructor.newInstance( - exoSchemeData.uuid, exoSchemeData.mimeType, exoSchemeData.data); - } - } - - private final class ExtractorOutputAdapter implements ExtractorOutput { - - private final SparseArray<TrackOutput> mTrackOutputAdapters; - private boolean mTracksEnded; - - private ExtractorOutputAdapter() { - mTrackOutputAdapters = new SparseArray<>(); - } - - @Override - public TrackOutput track(int id, int type) { - TrackOutput trackOutput = mTrackOutputAdapters.get(id); - if (trackOutput == null) { - int trackIndex = mTrackOutputAdapters.size(); - trackOutput = new TrackOutputAdapter(trackIndex); - mTrackOutputAdapters.put(id, trackOutput); - if (mEagerlyExposeTrackType) { - MediaFormat mediaFormat = new MediaFormat(); - mediaFormat.setString("track-type-string", toTypeString(type)); - mOutputConsumer.onTrackDataFound( - trackIndex, new TrackData(mediaFormat, /* drmInitData= */ null)); - } - } - return trackOutput; - } - - @Override - public void endTracks() { - mOutputConsumer.onTrackCountFound(mTrackOutputAdapters.size()); - } - - @Override - public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { - long durationUs = exoplayerSeekMap.getDurationUs(); - if (durationUs != C.TIME_UNSET) { - mDurationMillis = C.usToMs(durationUs); - } - if (mExposeChunkIndexAsMediaFormat && exoplayerSeekMap instanceof ChunkIndex) { - ChunkIndex chunkIndex = (ChunkIndex) exoplayerSeekMap; - MediaFormat mediaFormat = new MediaFormat(); - mediaFormat.setByteBuffer("chunk-index-int-sizes", toByteBuffer(chunkIndex.sizes)); - mediaFormat.setByteBuffer( - "chunk-index-long-offsets", toByteBuffer(chunkIndex.offsets)); - mediaFormat.setByteBuffer( - "chunk-index-long-us-durations", toByteBuffer(chunkIndex.durationsUs)); - mediaFormat.setByteBuffer( - "chunk-index-long-us-times", toByteBuffer(chunkIndex.timesUs)); - mOutputConsumer.onTrackDataFound( - /* trackIndex= */ 0, new TrackData(mediaFormat, /* drmInitData= */ null)); - } - mOutputConsumer.onSeekMapFound(new SeekMap(exoplayerSeekMap)); - } - } - - private class TrackOutputAdapter implements TrackOutput { - - private final int mTrackIndex; - - private CryptoInfo mLastOutputCryptoInfo; - private CryptoInfo.Pattern mLastOutputEncryptionPattern; - private CryptoData mLastReceivedCryptoData; - - @EncryptionDataReadState private int mEncryptionDataReadState; - private int mEncryptionDataSizeToSubtractFromSampleDataSize; - private int mEncryptionVectorSize; - private byte[] mScratchIvSpace; - private int mSubsampleEncryptionDataSize; - private int[] mScratchSubsampleEncryptedBytesCount; - private int[] mScratchSubsampleClearBytesCount; - private boolean mHasSubsampleEncryptionData; - private int mSkippedSupplementalDataBytes; - - private TrackOutputAdapter(int trackIndex) { - mTrackIndex = trackIndex; - mScratchIvSpace = new byte[16]; // Size documented in CryptoInfo. - mScratchSubsampleEncryptedBytesCount = new int[32]; - mScratchSubsampleClearBytesCount = new int[32]; - mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE; - mLastOutputEncryptionPattern = - new CryptoInfo.Pattern(/* blocksToEncrypt= */ 0, /* blocksToSkip= */ 0); - } - - @Override - public void format(Format format) { - mTrackFormats.put(mTrackIndex, format); - mOutputConsumer.onTrackDataFound( - mTrackIndex, - new TrackData( - toMediaFormat(format), toFrameworkDrmInitData(format.drmInitData))); - } - - @Override - public int sampleData( - DataReader input, - int length, - boolean allowEndOfInput, - @SampleDataPart int sampleDataPart) - throws IOException { - mScratchDataReaderAdapter.setDataReader(input, length); - long positionBeforeReading = mScratchDataReaderAdapter.getPosition(); - mOutputConsumer.onSampleDataFound(mTrackIndex, mScratchDataReaderAdapter); - return (int) (mScratchDataReaderAdapter.getPosition() - positionBeforeReading); - } - - @Override - public void sampleData( - ParsableByteArray data, int length, @SampleDataPart int sampleDataPart) { - if (sampleDataPart == SAMPLE_DATA_PART_ENCRYPTION && !mInBandCryptoInfo) { - while (length > 0) { - switch (mEncryptionDataReadState) { - case STATE_READING_SIGNAL_BYTE: - int encryptionSignalByte = data.readUnsignedByte(); - length--; - mHasSubsampleEncryptionData = ((encryptionSignalByte >> 7) & 1) != 0; - mEncryptionVectorSize = encryptionSignalByte & 0x7F; - mEncryptionDataSizeToSubtractFromSampleDataSize = - mEncryptionVectorSize + 1; // Signal byte. - mEncryptionDataReadState = STATE_READING_INIT_VECTOR; - break; - case STATE_READING_INIT_VECTOR: - Arrays.fill(mScratchIvSpace, (byte) 0); // Ensure 0-padding. - data.readBytes(mScratchIvSpace, /* offset= */ 0, mEncryptionVectorSize); - length -= mEncryptionVectorSize; - if (mHasSubsampleEncryptionData) { - mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE; - } else { - mSubsampleEncryptionDataSize = 0; - mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE; - } - break; - case STATE_READING_SUBSAMPLE_ENCRYPTION_SIZE: - mSubsampleEncryptionDataSize = data.readUnsignedShort(); - if (mScratchSubsampleClearBytesCount.length - < mSubsampleEncryptionDataSize) { - mScratchSubsampleClearBytesCount = - new int[mSubsampleEncryptionDataSize]; - mScratchSubsampleEncryptedBytesCount = - new int[mSubsampleEncryptionDataSize]; - } - length -= 2; - mEncryptionDataSizeToSubtractFromSampleDataSize += - 2 - + mSubsampleEncryptionDataSize - * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY; - mEncryptionDataReadState = STATE_READING_SUBSAMPLE_ENCRYPTION_DATA; - break; - case STATE_READING_SUBSAMPLE_ENCRYPTION_DATA: - for (int i = 0; i < mSubsampleEncryptionDataSize; i++) { - mScratchSubsampleClearBytesCount[i] = data.readUnsignedShort(); - mScratchSubsampleEncryptedBytesCount[i] = data.readInt(); - } - length -= - mSubsampleEncryptionDataSize - * BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY; - mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE; - if (length != 0) { - throw new IllegalStateException(); - } - break; - default: - // Never happens. - throw new IllegalStateException(); - } - } - } else if (sampleDataPart == SAMPLE_DATA_PART_SUPPLEMENTAL - && !mIncludeSupplementalData) { - mSkippedSupplementalDataBytes += length; - data.skipBytes(length); - } else { - outputSampleData(data, length); - } - } - - @Override - public void sampleMetadata( - long timeUs, int flags, int size, int offset, @Nullable CryptoData cryptoData) { - size -= mSkippedSupplementalDataBytes; - mSkippedSupplementalDataBytes = 0; - mOutputConsumer.onSampleCompleted( - mTrackIndex, - timeUs, - getMediaParserFlags(flags), - size - mEncryptionDataSizeToSubtractFromSampleDataSize, - offset, - getPopulatedCryptoInfo(cryptoData)); - mEncryptionDataReadState = STATE_READING_SIGNAL_BYTE; - mEncryptionDataSizeToSubtractFromSampleDataSize = 0; - } - - @Nullable - private CryptoInfo getPopulatedCryptoInfo(@Nullable CryptoData cryptoData) { - if (cryptoData == null) { - // The sample is not encrypted. - return null; - } else if (mInBandCryptoInfo) { - if (cryptoData != mLastReceivedCryptoData) { - mLastOutputCryptoInfo = - createNewCryptoInfoAndPopulateWithCryptoData(cryptoData); - // We are using in-band crypto info, so the IV will be ignored. But we prevent - // it from being null because toString assumes it non-null. - mLastOutputCryptoInfo.iv = EMPTY_BYTE_ARRAY; - } - } else /* We must populate the full CryptoInfo. */ { - // CryptoInfo.pattern is not accessible to the user, so the user needs to feed - // this CryptoInfo directly to MediaCodec. We need to create a new CryptoInfo per - // sample because of per-sample initialization vector changes. - CryptoInfo newCryptoInfo = createNewCryptoInfoAndPopulateWithCryptoData(cryptoData); - newCryptoInfo.iv = Arrays.copyOf(mScratchIvSpace, mScratchIvSpace.length); - boolean canReuseSubsampleInfo = - mLastOutputCryptoInfo != null - && mLastOutputCryptoInfo.numSubSamples - == mSubsampleEncryptionDataSize; - for (int i = 0; i < mSubsampleEncryptionDataSize && canReuseSubsampleInfo; i++) { - canReuseSubsampleInfo = - mLastOutputCryptoInfo.numBytesOfClearData[i] - == mScratchSubsampleClearBytesCount[i] - && mLastOutputCryptoInfo.numBytesOfEncryptedData[i] - == mScratchSubsampleEncryptedBytesCount[i]; - } - newCryptoInfo.numSubSamples = mSubsampleEncryptionDataSize; - if (canReuseSubsampleInfo) { - newCryptoInfo.numBytesOfClearData = mLastOutputCryptoInfo.numBytesOfClearData; - newCryptoInfo.numBytesOfEncryptedData = - mLastOutputCryptoInfo.numBytesOfEncryptedData; - } else { - newCryptoInfo.numBytesOfClearData = - Arrays.copyOf( - mScratchSubsampleClearBytesCount, mSubsampleEncryptionDataSize); - newCryptoInfo.numBytesOfEncryptedData = - Arrays.copyOf( - mScratchSubsampleEncryptedBytesCount, - mSubsampleEncryptionDataSize); - } - mLastOutputCryptoInfo = newCryptoInfo; - } - mLastReceivedCryptoData = cryptoData; - return mLastOutputCryptoInfo; - } - - private CryptoInfo createNewCryptoInfoAndPopulateWithCryptoData(CryptoData cryptoData) { - CryptoInfo cryptoInfo = new CryptoInfo(); - cryptoInfo.key = cryptoData.encryptionKey; - cryptoInfo.mode = cryptoData.cryptoMode; - if (cryptoData.clearBlocks != mLastOutputEncryptionPattern.getSkipBlocks() - || cryptoData.encryptedBlocks - != mLastOutputEncryptionPattern.getEncryptBlocks()) { - mLastOutputEncryptionPattern = - new CryptoInfo.Pattern(cryptoData.encryptedBlocks, cryptoData.clearBlocks); - } - cryptoInfo.setPattern(mLastOutputEncryptionPattern); - return cryptoInfo; - } - - private void outputSampleData(ParsableByteArray data, int length) { - mScratchParsableByteArrayAdapter.resetWithByteArray(data, length); - try { - // Read all bytes from data. ExoPlayer extractors expect all sample data to be - // consumed by TrackOutput implementations when passing a ParsableByteArray. - while (mScratchParsableByteArrayAdapter.getLength() > 0) { - mOutputConsumer.onSampleDataFound( - mTrackIndex, mScratchParsableByteArrayAdapter); - } - } catch (IOException e) { - // Unexpected. - throw new RuntimeException(e); - } - } - } - - private static final class DataReaderAdapter implements InputReader { - - private DataReader mDataReader; - private int mCurrentPosition; - private long mLength; - - public void setDataReader(DataReader dataReader, long length) { - mDataReader = dataReader; - mCurrentPosition = 0; - mLength = length; - } - - // Input implementation. - - @Override - public int read(byte[] buffer, int offset, int readLength) throws IOException { - int readBytes = 0; - readBytes = mDataReader.read(buffer, offset, readLength); - mCurrentPosition += readBytes; - return readBytes; - } - - @Override - public long getPosition() { - return mCurrentPosition; - } - - @Override - public long getLength() { - return mLength - mCurrentPosition; - } - } - - private static final class ParsableByteArrayAdapter implements InputReader { - - private ParsableByteArray mByteArray; - private long mLength; - private int mCurrentPosition; - - public void resetWithByteArray(ParsableByteArray byteArray, long length) { - mByteArray = byteArray; - mCurrentPosition = 0; - mLength = length; - } - - // Input implementation. - - @Override - public int read(byte[] buffer, int offset, int readLength) { - mByteArray.readBytes(buffer, offset, readLength); - mCurrentPosition += readLength; - return readLength; - } - - @Override - public long getPosition() { - return mCurrentPosition; - } - - @Override - public long getLength() { - return mLength - mCurrentPosition; - } - } - - private static final class DummyExoPlayerSeekMap - implements com.google.android.exoplayer2.extractor.SeekMap { - - @Override - public boolean isSeekable() { - return true; - } - - @Override - public long getDurationUs() { - return C.TIME_UNSET; - } - - @Override - public SeekPoints getSeekPoints(long timeUs) { - com.google.android.exoplayer2.extractor.SeekPoint seekPoint = - new com.google.android.exoplayer2.extractor.SeekPoint( - timeUs, /* position= */ 0); - return new SeekPoints(seekPoint, seekPoint); - } - } - - /** Creates extractor instances. */ - private interface ExtractorFactory { - - /** Returns a new extractor instance. */ - Extractor createInstance(); - } - - // Private static methods. - - private static Format toExoPlayerCaptionFormat(MediaFormat mediaFormat) { - Format.Builder formatBuilder = - new Format.Builder().setSampleMimeType(mediaFormat.getString(MediaFormat.KEY_MIME)); - if (mediaFormat.containsKey(MediaFormat.KEY_CAPTION_SERVICE_NUMBER)) { - formatBuilder.setAccessibilityChannel( - mediaFormat.getInteger(MediaFormat.KEY_CAPTION_SERVICE_NUMBER)); - } - return formatBuilder.build(); - } - - private static MediaFormat toMediaFormat(Format format) { - MediaFormat result = new MediaFormat(); - setOptionalMediaFormatInt(result, MediaFormat.KEY_BIT_RATE, format.bitrate); - setOptionalMediaFormatInt(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount); - - ColorInfo colorInfo = format.colorInfo; - if (colorInfo != null) { - setOptionalMediaFormatInt( - result, MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer); - setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_RANGE, colorInfo.colorRange); - setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_STANDARD, colorInfo.colorSpace); - - if (format.colorInfo.hdrStaticInfo != null) { - result.setByteBuffer( - MediaFormat.KEY_HDR_STATIC_INFO, - ByteBuffer.wrap(format.colorInfo.hdrStaticInfo)); - } - } - - setOptionalMediaFormatString(result, MediaFormat.KEY_MIME, format.sampleMimeType); - setOptionalMediaFormatString(result, MediaFormat.KEY_CODECS_STRING, format.codecs); - if (format.frameRate != Format.NO_VALUE) { - result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate); - } - setOptionalMediaFormatInt(result, MediaFormat.KEY_WIDTH, format.width); - setOptionalMediaFormatInt(result, MediaFormat.KEY_HEIGHT, format.height); - - List<byte[]> initData = format.initializationData; - for (int i = 0; i < initData.size(); i++) { - result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i))); - } - setPcmEncoding(format, result); - setOptionalMediaFormatString(result, MediaFormat.KEY_LANGUAGE, format.language); - setOptionalMediaFormatInt(result, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); - setOptionalMediaFormatInt(result, MediaFormat.KEY_ROTATION, format.rotationDegrees); - setOptionalMediaFormatInt(result, MediaFormat.KEY_SAMPLE_RATE, format.sampleRate); - setOptionalMediaFormatInt( - result, MediaFormat.KEY_CAPTION_SERVICE_NUMBER, format.accessibilityChannel); - - int selectionFlags = format.selectionFlags; - result.setInteger( - MediaFormat.KEY_IS_AUTOSELECT, selectionFlags & C.SELECTION_FLAG_AUTOSELECT); - result.setInteger(MediaFormat.KEY_IS_DEFAULT, selectionFlags & C.SELECTION_FLAG_DEFAULT); - result.setInteger( - MediaFormat.KEY_IS_FORCED_SUBTITLE, selectionFlags & C.SELECTION_FLAG_FORCED); - - setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay); - setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding); - - if (format.pixelWidthHeightRatio != Format.NO_VALUE && format.pixelWidthHeightRatio != 0) { - int parWidth = 1; - int parHeight = 1; - if (format.pixelWidthHeightRatio < 1.0f) { - parHeight = 1 << 30; - parWidth = (int) (format.pixelWidthHeightRatio * parHeight); - } else if (format.pixelWidthHeightRatio > 1.0f) { - parWidth = 1 << 30; - parHeight = (int) (parWidth / format.pixelWidthHeightRatio); - } - result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, parWidth); - result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, parHeight); - result.setFloat("pixel-width-height-ratio-float", format.pixelWidthHeightRatio); - } - if (format.drmInitData != null) { - // The crypto mode is propagated along with sample metadata. We also include it in the - // format for convenient use from ExoPlayer. - result.setString("crypto-mode-fourcc", format.drmInitData.schemeType); - } - if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) { - result.setLong("subsample-offset-us-long", format.subsampleOffsetUs); - } - // LACK OF SUPPORT FOR: - // format.id; - // format.metadata; - // format.stereoMode; - return result; - } - - private static ByteBuffer toByteBuffer(long[] longArray) { - ByteBuffer byteBuffer = ByteBuffer.allocateDirect(longArray.length * Long.BYTES); - for (long element : longArray) { - byteBuffer.putLong(element); - } - byteBuffer.flip(); - return byteBuffer; - } - - private static ByteBuffer toByteBuffer(int[] intArray) { - ByteBuffer byteBuffer = ByteBuffer.allocateDirect(intArray.length * Integer.BYTES); - for (int element : intArray) { - byteBuffer.putInt(element); - } - byteBuffer.flip(); - return byteBuffer; - } - - private static String toTypeString(int type) { - switch (type) { - case C.TRACK_TYPE_VIDEO: - return "video"; - case C.TRACK_TYPE_AUDIO: - return "audio"; - case C.TRACK_TYPE_TEXT: - return "text"; - case C.TRACK_TYPE_METADATA: - return "metadata"; - default: - return "unknown"; - } - } - - private static void setPcmEncoding(Format format, MediaFormat result) { - int exoPcmEncoding = format.pcmEncoding; - setOptionalMediaFormatInt(result, "exo-pcm-encoding", format.pcmEncoding); - int mediaFormatPcmEncoding; - switch (exoPcmEncoding) { - case C.ENCODING_PCM_8BIT: - mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_8BIT; - break; - case C.ENCODING_PCM_16BIT: - mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_16BIT; - break; - case C.ENCODING_PCM_FLOAT: - mediaFormatPcmEncoding = AudioFormat.ENCODING_PCM_FLOAT; - break; - default: - // No matching value. Do nothing. - return; - } - result.setInteger(MediaFormat.KEY_PCM_ENCODING, mediaFormatPcmEncoding); - } - - private static void setOptionalMediaFormatInt(MediaFormat mediaFormat, String key, int value) { - if (value != Format.NO_VALUE) { - mediaFormat.setInteger(key, value); - } - } - - private static void setOptionalMediaFormatString( - MediaFormat mediaFormat, String key, @Nullable String value) { - if (value != null) { - mediaFormat.setString(key, value); - } - } - - private DrmInitData toFrameworkDrmInitData( - com.google.android.exoplayer2.drm.DrmInitData exoDrmInitData) { - try { - return exoDrmInitData != null && mSchemeInitDataConstructor != null - ? new MediaParserDrmInitData(exoDrmInitData) - : null; - } catch (Throwable e) { - if (!mLoggedSchemeInitDataCreationException) { - mLoggedSchemeInitDataCreationException = true; - Log.e(TAG, "Unable to create SchemeInitData instance."); - } - return null; - } - } - - /** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */ - private static SeekPoint toSeekPoint( - com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) { - return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position); - } - - /** - * Introduces random error to the given metric value in order to prevent the identification of - * the parsed media. - */ - private static long addDither(long value) { - // Generate a random in [0, 1]. - double randomDither = ThreadLocalRandom.current().nextFloat(); - // Clamp the random number to [0, 2 * MEDIAMETRICS_DITHER]. - randomDither *= 2 * MEDIAMETRICS_DITHER; - // Translate the random number to [1 - MEDIAMETRICS_DITHER, 1 + MEDIAMETRICS_DITHER]. - randomDither += 1 - MEDIAMETRICS_DITHER; - return value != -1 ? (long) (value * randomDither) : -1; - } - - private static void assertValidNames(@NonNull String[] names) { - for (String name : names) { - if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) { - throw new IllegalArgumentException( - "Invalid extractor name: " - + name - + ". Supported parsers are: " - + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet()) - + "."); - } - } - } - - private int getMediaParserFlags(int flags) { - @SampleFlags int result = 0; - result |= (flags & C.BUFFER_FLAG_ENCRYPTED) != 0 ? SAMPLE_FLAG_ENCRYPTED : 0; - result |= (flags & C.BUFFER_FLAG_KEY_FRAME) != 0 ? SAMPLE_FLAG_KEY_FRAME : 0; - result |= (flags & C.BUFFER_FLAG_DECODE_ONLY) != 0 ? SAMPLE_FLAG_DECODE_ONLY : 0; - result |= - (flags & C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA) != 0 && mIncludeSupplementalData - ? SAMPLE_FLAG_HAS_SUPPLEMENTAL_DATA - : 0; - result |= (flags & C.BUFFER_FLAG_LAST_SAMPLE) != 0 ? SAMPLE_FLAG_LAST_SAMPLE : 0; - return result; - } - - @Nullable - private static Constructor<DrmInitData.SchemeInitData> getSchemeInitDataConstructor() { - // TODO: Use constructor statically when available. - Constructor<DrmInitData.SchemeInitData> constructor; - try { - return DrmInitData.SchemeInitData.class.getConstructor( - UUID.class, String.class, byte[].class); - } catch (Throwable e) { - Log.e(TAG, "Unable to get SchemeInitData constructor."); - return null; - } - } - - // Native methods. - - private native void nativeSubmitMetrics( - String logSessionId, - String parserName, - boolean createdByName, - String parserPool, - String lastObservedExceptionName, - long resourceByteCount, - long durationMillis, - String trackMimeTypes, - String trackCodecs, - String alteredParameters, - int videoWidth, - int videoHeight); - - // Static initialization. - - static { - System.loadLibrary(JNI_LIBRARY_NAME); - - // Using a LinkedHashMap to keep the insertion order when iterating over the keys. - LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>(); - // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering, - // which in turn aims to minimize the chances of incorrect extractor selections. - extractorFactoriesByName.put(PARSER_NAME_MATROSKA, MatroskaExtractor::new); - extractorFactoriesByName.put(PARSER_NAME_FMP4, FragmentedMp4Extractor::new); - extractorFactoriesByName.put(PARSER_NAME_MP4, Mp4Extractor::new); - extractorFactoriesByName.put(PARSER_NAME_MP3, Mp3Extractor::new); - extractorFactoriesByName.put(PARSER_NAME_ADTS, AdtsExtractor::new); - extractorFactoriesByName.put(PARSER_NAME_AC3, Ac3Extractor::new); - extractorFactoriesByName.put(PARSER_NAME_TS, TsExtractor::new); - extractorFactoriesByName.put(PARSER_NAME_FLV, FlvExtractor::new); - extractorFactoriesByName.put(PARSER_NAME_OGG, OggExtractor::new); - extractorFactoriesByName.put(PARSER_NAME_PS, PsExtractor::new); - extractorFactoriesByName.put(PARSER_NAME_WAV, WavExtractor::new); - extractorFactoriesByName.put(PARSER_NAME_AMR, AmrExtractor::new); - extractorFactoriesByName.put(PARSER_NAME_AC4, Ac4Extractor::new); - extractorFactoriesByName.put(PARSER_NAME_FLAC, FlacExtractor::new); - EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName); - - HashMap<String, Class> expectedTypeByParameterName = new HashMap<>(); - expectedTypeByParameterName.put(PARAMETER_ADTS_ENABLE_CBR_SEEKING, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_AMR_ENABLE_CBR_SEEKING, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_FLAC_DISABLE_ID3, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_TFDT_BOX, Boolean.class); - expectedTypeByParameterName.put( - PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_MP3_DISABLE_ID3, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_CBR_SEEKING, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_INDEX_SEEKING, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_TS_MODE, String.class); - expectedTypeByParameterName.put(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AAC_STREAM, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AVC_STREAM, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_IN_BAND_CRYPTO_INFO, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_INCLUDE_SUPPLEMENTAL_DATA, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_IGNORE_TIMESTAMP_OFFSET, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_EAGERLY_EXPOSE_TRACKTYPE, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_EXPOSE_DUMMY_SEEKMAP, Boolean.class); - expectedTypeByParameterName.put( - PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT, Boolean.class); - expectedTypeByParameterName.put( - PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS, Boolean.class); - expectedTypeByParameterName.put(PARAMETER_EXPOSE_EMSG_TRACK, Boolean.class); - // We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters - // instead. Checking that the value is a List is insufficient to catch wrong parameter - // value types. - int sumOfParameterNameLengths = - expectedTypeByParameterName.keySet().stream() - .map(String::length) - .reduce(0, Integer::sum); - sumOfParameterNameLengths += PARAMETER_EXPOSE_CAPTION_FORMATS.length(); - // Add space for any required separators. - MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH = - sumOfParameterNameLengths + expectedTypeByParameterName.size(); - - EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName); - } -} diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java deleted file mode 100644 index e76d61cf8965..000000000000 --- a/apex/media/framework/java/android/media/MediaSession2.java +++ /dev/null @@ -1,931 +0,0 @@ -/* - * Copyright 2018 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.media; - -import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS; -import static android.media.MediaConstants.KEY_CONNECTION_HINTS; -import static android.media.MediaConstants.KEY_PACKAGE_NAME; -import static android.media.MediaConstants.KEY_PID; -import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE; -import static android.media.MediaConstants.KEY_SESSION2LINK; -import static android.media.MediaConstants.KEY_TOKEN_EXTRAS; -import static android.media.Session2Command.Result.RESULT_ERROR_UNKNOWN_ERROR; -import static android.media.Session2Command.Result.RESULT_INFO_SKIPPED; -import static android.media.Session2Token.TYPE_SESSION; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.media.session.MediaSessionManager; -import android.media.session.MediaSessionManager.RemoteUserInfo; -import android.os.BadParcelableException; -import android.os.Bundle; -import android.os.Handler; -import android.os.Parcel; -import android.os.Process; -import android.os.ResultReceiver; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; - -import com.android.modules.utils.build.SdkLevel; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Allows a media app to expose its transport controls and playback information in a process to - * other processes including the Android framework and other apps. - */ -public class MediaSession2 implements AutoCloseable { - static final String TAG = "MediaSession2"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - // Note: This checks the uniqueness of a session ID only in a single process. - // When the framework becomes able to check the uniqueness, this logic should be removed. - //@GuardedBy("MediaSession.class") - private static final List<String> SESSION_ID_LIST = new ArrayList<>(); - - @SuppressWarnings("WeakerAccess") /* synthetic access */ - final Object mLock = new Object(); - //@GuardedBy("mLock") - @SuppressWarnings("WeakerAccess") /* synthetic access */ - final Map<Controller2Link, ControllerInfo> mConnectedControllers = new HashMap<>(); - - @SuppressWarnings("WeakerAccess") /* synthetic access */ - final Context mContext; - @SuppressWarnings("WeakerAccess") /* synthetic access */ - final Executor mCallbackExecutor; - @SuppressWarnings("WeakerAccess") /* synthetic access */ - final SessionCallback mCallback; - @SuppressWarnings("WeakerAccess") /* synthetic access */ - final Session2Link mSessionStub; - - private final String mSessionId; - private final PendingIntent mSessionActivity; - private final Session2Token mSessionToken; - private final MediaSessionManager mMediaSessionManager; - private final MediaCommunicationManager mCommunicationManager; - private final Handler mResultHandler; - - //@GuardedBy("mLock") - private boolean mClosed; - //@GuardedBy("mLock") - private boolean mPlaybackActive; - //@GuardedBy("mLock") - private ForegroundServiceEventCallback mForegroundServiceEventCallback; - - MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity, - @NonNull Executor callbackExecutor, @NonNull SessionCallback callback, - @NonNull Bundle tokenExtras) { - synchronized (MediaSession2.class) { - if (SESSION_ID_LIST.contains(id)) { - throw new IllegalStateException("Session ID must be unique. ID=" + id); - } - SESSION_ID_LIST.add(id); - } - - mContext = context; - mSessionId = id; - mSessionActivity = sessionActivity; - mCallbackExecutor = callbackExecutor; - mCallback = callback; - mSessionStub = new Session2Link(this); - mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(), - mSessionStub, tokenExtras); - if (SdkLevel.isAtLeastS()) { - mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class); - mMediaSessionManager = null; - } else { - mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class); - mCommunicationManager = null; - } - // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked. - mResultHandler = new Handler(context.getMainLooper()); - mClosed = false; - } - - @Override - public void close() { - try { - List<ControllerInfo> controllerInfos; - ForegroundServiceEventCallback callback; - synchronized (mLock) { - if (mClosed) { - return; - } - mClosed = true; - controllerInfos = getConnectedControllers(); - mConnectedControllers.clear(); - callback = mForegroundServiceEventCallback; - mForegroundServiceEventCallback = null; - } - synchronized (MediaSession2.class) { - SESSION_ID_LIST.remove(mSessionId); - } - if (callback != null) { - callback.onSessionClosed(this); - } - for (ControllerInfo info : controllerInfos) { - info.notifyDisconnected(); - } - } catch (Exception e) { - // Should not be here. - } - } - - /** - * Returns the session ID - */ - @NonNull - public String getId() { - return mSessionId; - } - - /** - * Returns the {@link Session2Token} for creating {@link MediaController2}. - */ - @NonNull - public Session2Token getToken() { - return mSessionToken; - } - - /** - * Broadcasts a session command to all the connected controllers - * <p> - * @param command the session command - * @param args optional arguments - */ - public void broadcastSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - List<ControllerInfo> controllerInfos = getConnectedControllers(); - for (ControllerInfo controller : controllerInfos) { - controller.sendSessionCommand(command, args, null); - } - } - - /** - * Sends a session command to a specific controller - * <p> - * @param controller the controller to get the session command - * @param command the session command - * @param args optional arguments - * @return a token which will be sent together in {@link SessionCallback#onCommandResult} - * when its result is received. - */ - @NonNull - public Object sendSessionCommand(@NonNull ControllerInfo controller, - @NonNull Session2Command command, @Nullable Bundle args) { - if (controller == null) { - throw new IllegalArgumentException("controller shouldn't be null"); - } - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) { - protected void onReceiveResult(int resultCode, Bundle resultData) { - controller.receiveCommandResult(this); - mCallbackExecutor.execute(() -> { - mCallback.onCommandResult(MediaSession2.this, controller, this, - command, new Session2Command.Result(resultCode, resultData)); - }); - } - }; - controller.sendSessionCommand(command, args, resultReceiver); - return resultReceiver; - } - - /** - * Cancels the session command previously sent. - * - * @param controller the controller to get the session command - * @param token the token which is returned from {@link #sendSessionCommand}. - */ - public void cancelSessionCommand(@NonNull ControllerInfo controller, @NonNull Object token) { - if (controller == null) { - throw new IllegalArgumentException("controller shouldn't be null"); - } - if (token == null) { - throw new IllegalArgumentException("token shouldn't be null"); - } - controller.cancelSessionCommand(token); - } - - /** - * Sets whether the playback is active (i.e. playing something) - * - * @param playbackActive {@code true} if the playback active, {@code false} otherwise. - **/ - public void setPlaybackActive(boolean playbackActive) { - final ForegroundServiceEventCallback serviceCallback; - synchronized (mLock) { - if (mPlaybackActive == playbackActive) { - return; - } - mPlaybackActive = playbackActive; - serviceCallback = mForegroundServiceEventCallback; - } - if (serviceCallback != null) { - serviceCallback.onPlaybackActiveChanged(this, playbackActive); - } - List<ControllerInfo> controllerInfos = getConnectedControllers(); - for (ControllerInfo controller : controllerInfos) { - controller.notifyPlaybackActiveChanged(playbackActive); - } - } - - /** - * Returns whehther the playback is active (i.e. playing something) - * - * @return {@code true} if the playback active, {@code false} otherwise. - */ - public boolean isPlaybackActive() { - synchronized (mLock) { - return mPlaybackActive; - } - } - - /** - * Gets the list of the connected controllers - * - * @return list of the connected controllers. - */ - @NonNull - public List<ControllerInfo> getConnectedControllers() { - List<ControllerInfo> controllers = new ArrayList<>(); - synchronized (mLock) { - controllers.addAll(mConnectedControllers.values()); - } - return controllers; - } - - /** - * Returns whether the given bundle includes non-framework Parcelables. - */ - static boolean hasCustomParcelable(@Nullable Bundle bundle) { - if (bundle == null) { - return false; - } - - // Try writing the bundle to parcel, and read it with framework classloader. - Parcel parcel = null; - try { - parcel = Parcel.obtain(); - parcel.writeBundle(bundle); - parcel.setDataPosition(0); - Bundle out = parcel.readBundle(null); - - // Calling Bundle#size() will trigger Bundle#unparcel(). - out.size(); - } catch (BadParcelableException e) { - Log.d(TAG, "Custom parcelable in bundle.", e); - return true; - } finally { - if (parcel != null) { - parcel.recycle(); - } - } - return false; - } - - boolean isClosed() { - synchronized (mLock) { - return mClosed; - } - } - - SessionCallback getCallback() { - return mCallback; - } - - boolean isTrustedForMediaControl(RemoteUserInfo remoteUserInfo) { - if (SdkLevel.isAtLeastS()) { - return mCommunicationManager.isTrustedForMediaControl(remoteUserInfo); - } else { - return mMediaSessionManager.isTrustedForMediaControl(remoteUserInfo); - } - } - - void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) { - synchronized (mLock) { - if (mForegroundServiceEventCallback == callback) { - return; - } - if (mForegroundServiceEventCallback != null && callback != null) { - throw new IllegalStateException("A session cannot be added to multiple services"); - } - mForegroundServiceEventCallback = callback; - } - } - - // Called by Session2Link.onConnect and MediaSession2Service.MediaSession2ServiceStub.connect - void onConnect(final Controller2Link controller, int callingPid, int callingUid, int seq, - Bundle connectionRequest) { - if (callingPid == 0) { - // The pid here is from Binder.getCallingPid(), which can be 0 for an oneway call from - // the remote process. If it's the case, use PID from the connectionRequest. - callingPid = connectionRequest.getInt(KEY_PID); - } - String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME); - - RemoteUserInfo remoteUserInfo = new RemoteUserInfo(callingPkg, callingPid, callingUid); - - Bundle connectionHints = connectionRequest.getBundle(KEY_CONNECTION_HINTS); - if (connectionHints == null) { - Log.w(TAG, "connectionHints shouldn't be null."); - connectionHints = Bundle.EMPTY; - } else if (hasCustomParcelable(connectionHints)) { - Log.w(TAG, "connectionHints contain custom parcelable. Ignoring."); - connectionHints = Bundle.EMPTY; - } - - final ControllerInfo controllerInfo = new ControllerInfo( - remoteUserInfo, - isTrustedForMediaControl(remoteUserInfo), - controller, - connectionHints); - mCallbackExecutor.execute(() -> { - boolean connected = false; - try { - if (isClosed()) { - return; - } - controllerInfo.mAllowedCommands = - mCallback.onConnect(MediaSession2.this, controllerInfo); - // Don't reject connection for the request from trusted app. - // Otherwise server will fail to retrieve session's information to dispatch - // media keys to. - if (controllerInfo.mAllowedCommands == null && !controllerInfo.isTrusted()) { - return; - } - if (controllerInfo.mAllowedCommands == null) { - // For trusted apps, send non-null allowed commands to keep - // connection. - controllerInfo.mAllowedCommands = - new Session2CommandGroup.Builder().build(); - } - if (DEBUG) { - Log.d(TAG, "Accepting connection: " + controllerInfo); - } - // If connection is accepted, notify the current state to the controller. - // It's needed because we cannot call synchronous calls between - // session/controller. - Bundle connectionResult = new Bundle(); - connectionResult.putParcelable(KEY_SESSION2LINK, mSessionStub); - connectionResult.putParcelable(KEY_ALLOWED_COMMANDS, - controllerInfo.mAllowedCommands); - connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive()); - connectionResult.putBundle(KEY_TOKEN_EXTRAS, mSessionToken.getExtras()); - - // Double check if session is still there, because close() can be called in - // another thread. - if (isClosed()) { - return; - } - controllerInfo.notifyConnected(connectionResult); - synchronized (mLock) { - if (mConnectedControllers.containsKey(controller)) { - Log.w(TAG, "Controller " + controllerInfo + " has sent connection" - + " request multiple times"); - } - mConnectedControllers.put(controller, controllerInfo); - } - mCallback.onPostConnect(MediaSession2.this, controllerInfo); - connected = true; - } finally { - if (!connected || isClosed()) { - if (DEBUG) { - Log.d(TAG, "Rejecting connection or notifying that session is closed" - + ", controllerInfo=" + controllerInfo); - } - synchronized (mLock) { - mConnectedControllers.remove(controller); - } - controllerInfo.notifyDisconnected(); - } - } - }); - } - - // Called by Session2Link.onDisconnect - void onDisconnect(@NonNull final Controller2Link controller, int seq) { - final ControllerInfo controllerInfo; - synchronized (mLock) { - controllerInfo = mConnectedControllers.remove(controller); - } - if (controllerInfo == null) { - return; - } - mCallbackExecutor.execute(() -> { - mCallback.onDisconnected(MediaSession2.this, controllerInfo); - }); - } - - // Called by Session2Link.onSessionCommand - void onSessionCommand(@NonNull final Controller2Link controller, final int seq, - final Session2Command command, final Bundle args, - @Nullable ResultReceiver resultReceiver) { - if (controller == null) { - return; - } - final ControllerInfo controllerInfo; - synchronized (mLock) { - controllerInfo = mConnectedControllers.get(controller); - } - if (controllerInfo == null) { - return; - } - - // TODO: check allowed commands. - synchronized (mLock) { - controllerInfo.addRequestedCommandSeqNumber(seq); - } - mCallbackExecutor.execute(() -> { - if (!controllerInfo.removeRequestedCommandSeqNumber(seq)) { - if (resultReceiver != null) { - resultReceiver.send(RESULT_INFO_SKIPPED, null); - } - return; - } - Session2Command.Result result = mCallback.onSessionCommand( - MediaSession2.this, controllerInfo, command, args); - if (resultReceiver != null) { - if (result == null) { - resultReceiver.send(RESULT_INFO_SKIPPED, null); - } else { - resultReceiver.send(result.getResultCode(), result.getResultData()); - } - } - }); - } - - // Called by Session2Link.onCancelCommand - void onCancelCommand(@NonNull final Controller2Link controller, final int seq) { - final ControllerInfo controllerInfo; - synchronized (mLock) { - controllerInfo = mConnectedControllers.get(controller); - } - if (controllerInfo == null) { - return; - } - controllerInfo.removeRequestedCommandSeqNumber(seq); - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Builder for {@link MediaSession2}. - * <p> - * Any incoming event from the {@link MediaController2} will be handled on the callback - * executor. If it's not set, {@link Context#getMainExecutor()} will be used by default. - */ - public static final class Builder { - private Context mContext; - private String mId; - private PendingIntent mSessionActivity; - private Executor mCallbackExecutor; - private SessionCallback mCallback; - private Bundle mExtras; - - /** - * Creates a builder for {@link MediaSession2}. - * - * @param context Context - * @throws IllegalArgumentException if context is {@code null}. - */ - public Builder(@NonNull Context context) { - if (context == null) { - throw new IllegalArgumentException("context shouldn't be null"); - } - mContext = context; - } - - /** - * Set an intent for launching UI for this Session. This can be used as a - * quick link to an ongoing media screen. The intent should be for an - * activity that may be started using {@link Context#startActivity(Intent)}. - * - * @param pi The intent to launch to show UI for this session. - * @return The Builder to allow chaining - */ - @NonNull - public Builder setSessionActivity(@Nullable PendingIntent pi) { - mSessionActivity = pi; - return this; - } - - /** - * Set ID of the session. If it's not set, an empty string will be used to create a session. - * <p> - * Use this if and only if your app supports multiple playback at the same time and also - * wants to provide external apps to have finer controls of them. - * - * @param id id of the session. Must be unique per package. - * @throws IllegalArgumentException if id is {@code null}. - * @return The Builder to allow chaining - */ - @NonNull - public Builder setId(@NonNull String id) { - if (id == null) { - throw new IllegalArgumentException("id shouldn't be null"); - } - mId = id; - return this; - } - - /** - * Set callback for the session and its executor. - * - * @param executor callback executor - * @param callback session callback. - * @return The Builder to allow chaining - */ - @NonNull - public Builder setSessionCallback(@NonNull Executor executor, - @NonNull SessionCallback callback) { - mCallbackExecutor = executor; - mCallback = callback; - return this; - } - - /** - * Set extras for the session token. If null or not set, {@link Session2Token#getExtras()} - * will return an empty {@link Bundle}. An {@link IllegalArgumentException} will be thrown - * if the bundle contains any non-framework Parcelable objects. - * - * @return The Builder to allow chaining - * @see Session2Token#getExtras() - */ - @NonNull - public Builder setExtras(@NonNull Bundle extras) { - if (extras == null) { - throw new NullPointerException("extras shouldn't be null"); - } - if (hasCustomParcelable(extras)) { - throw new IllegalArgumentException( - "extras shouldn't contain any custom parcelables"); - } - mExtras = new Bundle(extras); - return this; - } - - /** - * Build {@link MediaSession2}. - * - * @return a new session - * @throws IllegalStateException if the session with the same id is already exists for the - * package. - */ - @NonNull - public MediaSession2 build() { - if (mCallbackExecutor == null) { - mCallbackExecutor = mContext.getMainExecutor(); - } - if (mCallback == null) { - mCallback = new SessionCallback() {}; - } - if (mId == null) { - mId = ""; - } - if (mExtras == null) { - mExtras = Bundle.EMPTY; - } - MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity, - mCallbackExecutor, mCallback, mExtras); - - // Notify framework about the newly create session after the constructor is finished. - // Otherwise, framework may access the session before the initialization is finished. - try { - if (SdkLevel.isAtLeastS()) { - MediaCommunicationManager manager = - mContext.getSystemService(MediaCommunicationManager.class); - manager.notifySession2Created(session2.getToken()); - } else { - MediaSessionManager manager = - mContext.getSystemService(MediaSessionManager.class); - manager.notifySession2Created(session2.getToken()); - } - } catch (Exception e) { - session2.close(); - throw e; - } - - return session2; - } - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Information of a controller. - */ - public static final class ControllerInfo { - private final RemoteUserInfo mRemoteUserInfo; - private final boolean mIsTrusted; - private final Controller2Link mControllerBinder; - private final Bundle mConnectionHints; - private final Object mLock = new Object(); - //@GuardedBy("mLock") - private int mNextSeqNumber; - //@GuardedBy("mLock") - private ArrayMap<ResultReceiver, Integer> mPendingCommands; - //@GuardedBy("mLock") - private ArraySet<Integer> mRequestedCommandSeqNumbers; - - @SuppressWarnings("WeakerAccess") /* synthetic access */ - Session2CommandGroup mAllowedCommands; - - /** - * @param remoteUserInfo remote user info - * @param trusted {@code true} if trusted, {@code false} otherwise - * @param controllerBinder Controller2Link for the connected controller. - * @param connectionHints a session-specific argument sent from the controller for the - * connection. The contents of this bundle may affect the - * connection result. - */ - ControllerInfo(@NonNull RemoteUserInfo remoteUserInfo, boolean trusted, - @Nullable Controller2Link controllerBinder, @NonNull Bundle connectionHints) { - mRemoteUserInfo = remoteUserInfo; - mIsTrusted = trusted; - mControllerBinder = controllerBinder; - mConnectionHints = connectionHints; - mPendingCommands = new ArrayMap<>(); - mRequestedCommandSeqNumbers = new ArraySet<>(); - } - - /** - * @return remote user info of the controller. - */ - @NonNull - public RemoteUserInfo getRemoteUserInfo() { - return mRemoteUserInfo; - } - - /** - * @return package name of the controller. - */ - @NonNull - public String getPackageName() { - return mRemoteUserInfo.getPackageName(); - } - - /** - * @return uid of the controller. Can be a negative value if the uid cannot be obtained. - */ - public int getUid() { - return mRemoteUserInfo.getUid(); - } - - /** - * @return connection hints sent from controller. - */ - @NonNull - public Bundle getConnectionHints() { - return new Bundle(mConnectionHints); - } - - /** - * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or - * has a enabled notification listener so can be trusted to accept connection and incoming - * command request. - * - * @return {@code true} if the controller is trusted. - * @hide - */ - public boolean isTrusted() { - return mIsTrusted; - } - - @Override - public int hashCode() { - return Objects.hash(mControllerBinder, mRemoteUserInfo); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof ControllerInfo)) return false; - if (this == obj) return true; - - ControllerInfo other = (ControllerInfo) obj; - if (mControllerBinder != null || other.mControllerBinder != null) { - return Objects.equals(mControllerBinder, other.mControllerBinder); - } - return mRemoteUserInfo.equals(other.mRemoteUserInfo); - } - - @Override - @NonNull - public String toString() { - return "ControllerInfo {pkg=" + mRemoteUserInfo.getPackageName() + ", uid=" - + mRemoteUserInfo.getUid() + ", allowedCommands=" + mAllowedCommands + "})"; - } - - void notifyConnected(Bundle connectionResult) { - if (mControllerBinder == null) return; - - try { - mControllerBinder.notifyConnected(getNextSeqNumber(), connectionResult); - } catch (RuntimeException e) { - // Controller may be died prematurely. - } - } - - void notifyDisconnected() { - if (mControllerBinder == null) return; - - try { - mControllerBinder.notifyDisconnected(getNextSeqNumber()); - } catch (RuntimeException e) { - // Controller may be died prematurely. - } - } - - void notifyPlaybackActiveChanged(boolean playbackActive) { - if (mControllerBinder == null) return; - - try { - mControllerBinder.notifyPlaybackActiveChanged(getNextSeqNumber(), playbackActive); - } catch (RuntimeException e) { - // Controller may be died prematurely. - } - } - - void sendSessionCommand(Session2Command command, Bundle args, - ResultReceiver resultReceiver) { - if (mControllerBinder == null) return; - - try { - int seq = getNextSeqNumber(); - synchronized (mLock) { - mPendingCommands.put(resultReceiver, seq); - } - mControllerBinder.sendSessionCommand(seq, command, args, resultReceiver); - } catch (RuntimeException e) { - // Controller may be died prematurely. - synchronized (mLock) { - mPendingCommands.remove(resultReceiver); - } - resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null); - } - } - - void cancelSessionCommand(@NonNull Object token) { - if (mControllerBinder == null) return; - Integer seq; - synchronized (mLock) { - seq = mPendingCommands.remove(token); - } - if (seq != null) { - mControllerBinder.cancelSessionCommand(seq); - } - } - - void receiveCommandResult(ResultReceiver resultReceiver) { - synchronized (mLock) { - mPendingCommands.remove(resultReceiver); - } - } - - void addRequestedCommandSeqNumber(int seq) { - synchronized (mLock) { - mRequestedCommandSeqNumbers.add(seq); - } - } - - boolean removeRequestedCommandSeqNumber(int seq) { - synchronized (mLock) { - return mRequestedCommandSeqNumbers.remove(seq); - } - } - - private int getNextSeqNumber() { - synchronized (mLock) { - return mNextSeqNumber++; - } - } - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Callback to be called for all incoming commands from {@link MediaController2}s. - */ - public abstract static class SessionCallback { - /** - * Called when a controller is created for this session. Return allowed commands for - * controller. By default it returns {@code null}. - * <p> - * You can reject the connection by returning {@code null}. In that case, controller - * receives {@link MediaController2.ControllerCallback#onDisconnected(MediaController2)} - * and cannot be used. - * <p> - * The controller hasn't connected yet in this method, so calls to the controller - * (e.g. {@link #sendSessionCommand}) would be ignored. Override {@link #onPostConnect} for - * the custom initialization for the controller instead. - * - * @param session the session for this event - * @param controller controller information. - * @return allowed commands. Can be {@code null} to reject connection. - */ - @Nullable - public Session2CommandGroup onConnect(@NonNull MediaSession2 session, - @NonNull ControllerInfo controller) { - return null; - } - - /** - * Called immediately after a controller is connected. This is a convenient method to add - * custom initialization between the session and a controller. - * <p> - * Note that calls to the controller (e.g. {@link #sendSessionCommand}) work here but don't - * work in {@link #onConnect} because the controller hasn't connected yet in - * {@link #onConnect}. - * - * @param session the session for this event - * @param controller controller information. - */ - public void onPostConnect(@NonNull MediaSession2 session, - @NonNull ControllerInfo controller) { - } - - /** - * Called when a controller is disconnected - * - * @param session the session for this event - * @param controller controller information - */ - public void onDisconnected(@NonNull MediaSession2 session, - @NonNull ControllerInfo controller) {} - - /** - * Called when a controller sent a session command. - * - * @param session the session for this event - * @param controller controller information - * @param command the session command - * @param args optional arguments - * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED - * will be sent to the session. - */ - @Nullable - public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session, - @NonNull ControllerInfo controller, @NonNull Session2Command command, - @Nullable Bundle args) { - return null; - } - - /** - * Called when the command sent to the controller is finished. - * - * @param session the session for this event - * @param controller controller information - * @param token the token got from {@link MediaSession2#sendSessionCommand} - * @param command the session command - * @param result the result of the session command - */ - public void onCommandResult(@NonNull MediaSession2 session, - @NonNull ControllerInfo controller, @NonNull Object token, - @NonNull Session2Command command, @NonNull Session2Command.Result result) {} - } - - abstract static class ForegroundServiceEventCallback { - public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {} - public void onSessionClosed(MediaSession2 session) {} - } -} diff --git a/apex/media/framework/java/android/media/MediaSession2Service.java b/apex/media/framework/java/android/media/MediaSession2Service.java deleted file mode 100644 index f6fd509fd245..000000000000 --- a/apex/media/framework/java/android/media/MediaSession2Service.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright 2019 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.media; - -import static android.media.MediaConstants.KEY_CONNECTION_HINTS; -import static android.media.MediaConstants.KEY_PACKAGE_NAME; -import static android.media.MediaConstants.KEY_PID; - -import android.annotation.CallSuper; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.media.MediaSession2.ControllerInfo; -import android.media.session.MediaSessionManager; -import android.media.session.MediaSessionManager.RemoteUserInfo; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.util.ArrayMap; -import android.util.Log; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Service containing {@link MediaSession2}. - */ -public abstract class MediaSession2Service extends Service { - /** - * The {@link Intent} that must be declared as handled by the service. - */ - public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service"; - - private static final String TAG = "MediaSession2Service"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private final MediaSession2.ForegroundServiceEventCallback mForegroundServiceEventCallback = - new MediaSession2.ForegroundServiceEventCallback() { - @Override - public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) { - MediaSession2Service.this.onPlaybackActiveChanged(session, playbackActive); - } - - @Override - public void onSessionClosed(MediaSession2 session) { - removeSession(session); - } - }; - - private final Object mLock = new Object(); - //@GuardedBy("mLock") - private NotificationManager mNotificationManager; - //@GuardedBy("mLock") - private MediaSessionManager mMediaSessionManager; - //@GuardedBy("mLock") - private Intent mStartSelfIntent; - //@GuardedBy("mLock") - private Map<String, MediaSession2> mSessions = new ArrayMap<>(); - //@GuardedBy("mLock") - private Map<MediaSession2, MediaNotification> mNotifications = new ArrayMap<>(); - //@GuardedBy("mLock") - private MediaSession2ServiceStub mStub; - - /** - * Called by the system when the service is first created. Do not call this method directly. - * <p> - * Override this method if you need your own initialization. Derived classes MUST call through - * to the super class's implementation of this method. - */ - @CallSuper - @Override - public void onCreate() { - super.onCreate(); - synchronized (mLock) { - mStub = new MediaSession2ServiceStub(this); - mStartSelfIntent = new Intent(this, this.getClass()); - mNotificationManager = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mMediaSessionManager = - (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE); - } - } - - @CallSuper - @Override - @Nullable - public IBinder onBind(@NonNull Intent intent) { - if (SERVICE_INTERFACE.equals(intent.getAction())) { - synchronized (mLock) { - return mStub; - } - } - return null; - } - - /** - * Called by the system to notify that it is no longer used and is being removed. Do not call - * this method directly. - * <p> - * Override this method if you need your own clean up. Derived classes MUST call through - * to the super class's implementation of this method. - */ - @CallSuper - @Override - public void onDestroy() { - super.onDestroy(); - synchronized (mLock) { - List<MediaSession2> sessions = getSessions(); - for (MediaSession2 session : sessions) { - removeSession(session); - } - mSessions.clear(); - mNotifications.clear(); - } - mStub.close(); - } - - /** - * Called when a {@link MediaController2} is created with the this service's - * {@link Session2Token}. Return the session for telling the controller which session to - * connect. Return {@code null} to reject the connection from this controller. - * <p> - * Session returned here will be added to this service automatically. You don't need to call - * {@link #addSession(MediaSession2)} for that. - * <p> - * This method is always called on the main thread. - * - * @param controllerInfo information of the controller which is trying to connect. - * @return a {@link MediaSession2} instance for the controller to connect to, or {@code null} - * to reject connection - * @see MediaSession2.Builder - * @see #getSessions() - */ - @Nullable - public abstract MediaSession2 onGetSession(@NonNull ControllerInfo controllerInfo); - - /** - * Called when notification UI needs update. Override this method to show or cancel your own - * notification UI. - * <p> - * This would be called on {@link MediaSession2}'s callback executor when playback state is - * changed. - * <p> - * With the notification returned here, the service becomes foreground service when the playback - * is started. Apps must request the permission - * {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use this API. It becomes - * background service after the playback is stopped. - * - * @param session a session that needs notification update. - * @return a {@link MediaNotification}. Can be {@code null}. - */ - @Nullable - public abstract MediaNotification onUpdateNotification(@NonNull MediaSession2 session); - - /** - * Adds a session to this service. - * <p> - * Added session will be removed automatically when it's closed, or removed when - * {@link #removeSession} is called. - * - * @param session a session to be added. - * @see #removeSession(MediaSession2) - */ - public final void addSession(@NonNull MediaSession2 session) { - if (session == null) { - throw new IllegalArgumentException("session shouldn't be null"); - } - if (session.isClosed()) { - throw new IllegalArgumentException("session is already closed"); - } - synchronized (mLock) { - MediaSession2 previousSession = mSessions.get(session.getId()); - if (previousSession != null) { - if (previousSession != session) { - Log.w(TAG, "Session ID should be unique, ID=" + session.getId() - + ", previous=" + previousSession + ", session=" + session); - } - return; - } - mSessions.put(session.getId(), session); - session.setForegroundServiceEventCallback(mForegroundServiceEventCallback); - } - } - - /** - * Removes a session from this service. - * - * @param session a session to be removed. - * @see #addSession(MediaSession2) - */ - public final void removeSession(@NonNull MediaSession2 session) { - if (session == null) { - throw new IllegalArgumentException("session shouldn't be null"); - } - MediaNotification notification; - synchronized (mLock) { - if (mSessions.get(session.getId()) != session) { - // Session isn't added or removed already. - return; - } - mSessions.remove(session.getId()); - notification = mNotifications.remove(session); - } - session.setForegroundServiceEventCallback(null); - if (notification != null) { - mNotificationManager.cancel(notification.getNotificationId()); - } - if (getSessions().isEmpty()) { - stopForeground(false); - } - } - - /** - * Gets the list of {@link MediaSession2}s that you've added to this service. - * - * @return sessions - */ - public final @NonNull List<MediaSession2> getSessions() { - List<MediaSession2> list = new ArrayList<>(); - synchronized (mLock) { - list.addAll(mSessions.values()); - } - return list; - } - - /** - * Returns the {@link MediaSessionManager}. - */ - @NonNull - MediaSessionManager getMediaSessionManager() { - synchronized (mLock) { - return mMediaSessionManager; - } - } - - /** - * Called by registered {@link MediaSession2.ForegroundServiceEventCallback} - * - * @param session session with change - * @param playbackActive {@code true} if playback is active. - */ - void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) { - MediaNotification mediaNotification = onUpdateNotification(session); - if (mediaNotification == null) { - // The service implementation doesn't want to use the automatic start/stopForeground - // feature. - return; - } - synchronized (mLock) { - mNotifications.put(session, mediaNotification); - } - int id = mediaNotification.getNotificationId(); - Notification notification = mediaNotification.getNotification(); - if (!playbackActive) { - mNotificationManager.notify(id, notification); - return; - } - // playbackActive == true - startForegroundService(mStartSelfIntent); - startForeground(id, notification); - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Returned by {@link #onUpdateNotification(MediaSession2)} for making session service - * foreground service to keep playback running in the background. It's highly recommended to - * show media style notification here. - */ - public static class MediaNotification { - private final int mNotificationId; - private final Notification mNotification; - - /** - * Default constructor - * - * @param notificationId notification id to be used for - * {@link NotificationManager#notify(int, Notification)}. - * @param notification a notification to make session service run in the foreground. Media - * style notification is recommended here. - */ - public MediaNotification(int notificationId, @NonNull Notification notification) { - if (notification == null) { - throw new IllegalArgumentException("notification shouldn't be null"); - } - mNotificationId = notificationId; - mNotification = notification; - } - - /** - * Gets the id of the notification. - * - * @return the notification id - */ - public int getNotificationId() { - return mNotificationId; - } - - /** - * Gets the notification. - * - * @return the notification - */ - @NonNull - public Notification getNotification() { - return mNotification; - } - } - - private static final class MediaSession2ServiceStub extends IMediaSession2Service.Stub - implements AutoCloseable { - final WeakReference<MediaSession2Service> mService; - final Handler mHandler; - - MediaSession2ServiceStub(MediaSession2Service service) { - mService = new WeakReference<>(service); - mHandler = new Handler(service.getMainLooper()); - } - - @Override - public void connect(Controller2Link caller, int seq, Bundle connectionRequest) { - if (mService.get() == null) { - if (DEBUG) { - Log.d(TAG, "Service is already destroyed"); - } - return; - } - if (caller == null || connectionRequest == null) { - if (DEBUG) { - Log.d(TAG, "Ignoring calls with illegal arguments, caller=" + caller - + ", connectionRequest=" + connectionRequest); - } - return; - } - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - mHandler.post(() -> { - boolean shouldNotifyDisconnected = true; - try { - final MediaSession2Service service = mService.get(); - if (service == null) { - if (DEBUG) { - Log.d(TAG, "Service isn't available"); - } - return; - } - - String callingPkg = connectionRequest.getString(KEY_PACKAGE_NAME); - // The Binder.getCallingPid() can be 0 for an oneway call from the - // remote process. If it's the case, use PID from the connectionRequest. - RemoteUserInfo remoteUserInfo = new RemoteUserInfo( - callingPkg, - pid == 0 ? connectionRequest.getInt(KEY_PID) : pid, - uid); - - Bundle connectionHints = connectionRequest.getBundle(KEY_CONNECTION_HINTS); - if (connectionHints == null) { - Log.w(TAG, "connectionHints shouldn't be null."); - connectionHints = Bundle.EMPTY; - } else if (MediaSession2.hasCustomParcelable(connectionHints)) { - Log.w(TAG, "connectionHints contain custom parcelable. Ignoring."); - connectionHints = Bundle.EMPTY; - } - - final ControllerInfo controllerInfo = new ControllerInfo( - remoteUserInfo, - service.getMediaSessionManager() - .isTrustedForMediaControl(remoteUserInfo), - caller, - connectionHints); - - if (DEBUG) { - Log.d(TAG, "Handling incoming connection request from the" - + " controller=" + controllerInfo); - } - - final MediaSession2 session; - session = service.onGetSession(controllerInfo); - - if (session == null) { - if (DEBUG) { - Log.d(TAG, "Rejecting incoming connection request from the" - + " controller=" + controllerInfo); - } - // Note: Trusted controllers also can be rejected according to the - // service implementation. - return; - } - service.addSession(session); - shouldNotifyDisconnected = false; - session.onConnect(caller, pid, uid, seq, connectionRequest); - } catch (Exception e) { - // Don't propagate exception in service to the controller. - Log.w(TAG, "Failed to add a session to session service", e); - } finally { - // Trick to call onDisconnected() in one place. - if (shouldNotifyDisconnected) { - if (DEBUG) { - Log.d(TAG, "Notifying the controller of its disconnection"); - } - try { - caller.notifyDisconnected(0); - } catch (RuntimeException e) { - // Controller may be died prematurely. - // Not an issue because we'll ignore it anyway. - } - } - } - }); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void close() { - mHandler.removeCallbacksAndMessages(null); - mService.clear(); - } - } -} diff --git a/apex/media/framework/java/android/media/MediaTranscodingManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java deleted file mode 100644 index 1a84929c678f..000000000000 --- a/apex/media/framework/java/android/media/MediaTranscodingManager.java +++ /dev/null @@ -1,1752 +0,0 @@ -/* - * Copyright (C) 2019 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.media; - -import android.annotation.CallbackExecutor; -import android.annotation.IntDef; -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.app.ActivityManager; -import android.content.ContentResolver; -import android.content.Context; -import android.content.res.AssetFileDescriptor; -import android.net.Uri; -import android.os.Build; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.os.ServiceSpecificException; -import android.system.Os; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.modules.annotation.MinSdk; -import com.android.modules.utils.build.SdkLevel; - -import java.io.FileNotFoundException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - Android 12 introduces Compatible media transcoding feature. See - <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding"> - Compatible media transcoding</a>. MediaTranscodingManager provides an interface to the system's media - transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to - AVC. - - <h3>Transcoding Types</h3> - <h4>Video Transcoding</h4> - When transcoding a video file, the video track will be transcoded based on the desired track format - and the audio track will be pass through without any modification. - <p class=note> - Note that currently only support transcoding video file in mp4 format and with single video track. - - <h3>Transcoding Request</h3> - <p> - To transcode a media file, first create a {@link TranscodingRequest} through its builder class - {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through - {@link MediaTranscodingManager#enqueueRequest( - TranscodingRequest, Executor, OnTranscodingFinishedListener)} - TranscodeRequest are processed based on client process's priority and request priority. When a - transcode operation is completed the caller is notified via its - {@link OnTranscodingFinishedListener}. - In the meantime the caller may use the returned TranscodingSession object to cancel or check the - status of a specific transcode operation. - <p> - Here is an example where <code>Builder</code> is used to specify all parameters - - <pre class=prettyprint> - VideoTranscodingRequest request = - new VideoTranscodingRequest.Builder(srcUri, dstUri, videoFormat).build(); - }</pre> - @hide - */ -@MinSdk(Build.VERSION_CODES.S) -@SystemApi -public final class MediaTranscodingManager { - private static final String TAG = "MediaTranscodingManager"; - - /** Maximum number of retry to connect to the service. */ - private static final int CONNECT_SERVICE_RETRY_COUNT = 100; - - /** Interval between trying to reconnect to the service. */ - private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40; - - /** Default bpp(bits-per-pixel) to use for calculating default bitrate. */ - private static final float BPP = 0.25f; - - /** - * Listener that gets notified when a transcoding operation has finished. - * This listener gets notified regardless of how the operation finished. It is up to the - * listener implementation to check the result and take appropriate action. - */ - @FunctionalInterface - public interface OnTranscodingFinishedListener { - /** - * Called when the transcoding operation has finished. The receiver may use the - * TranscodingSession to check the result, i.e. whether the operation succeeded, was - * canceled or if an error occurred. - * - * @param session The TranscodingSession instance for the finished transcoding operation. - */ - void onTranscodingFinished(@NonNull TranscodingSession session); - } - - private final Context mContext; - private ContentResolver mContentResolver; - private final String mPackageName; - private final int mPid; - private final int mUid; - private final boolean mIsLowRamDevice; - private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); - private final HashMap<Integer, TranscodingSession> mPendingTranscodingSessions = new HashMap(); - private final Object mLock = new Object(); - @GuardedBy("mLock") - @NonNull private ITranscodingClient mTranscodingClient = null; - private static MediaTranscodingManager sMediaTranscodingManager; - - private void handleTranscodingFinished(int sessionId, TranscodingResultParcel result) { - synchronized (mPendingTranscodingSessions) { - // Gets the session associated with the sessionId and removes it from - // mPendingTranscodingSessions. - final TranscodingSession session = mPendingTranscodingSessions.remove(sessionId); - - if (session == null) { - // This should not happen in reality. - Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions"); - return; - } - - // Updates the session status and result. - session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED, - TranscodingSession.RESULT_SUCCESS, - TranscodingSession.ERROR_NONE); - - // Notifies client the session is done. - if (session.mListener != null && session.mListenerExecutor != null) { - session.mListenerExecutor.execute( - () -> session.mListener.onTranscodingFinished(session)); - } - } - } - - private void handleTranscodingFailed(int sessionId, int errorCode) { - synchronized (mPendingTranscodingSessions) { - // Gets the session associated with the sessionId and removes it from - // mPendingTranscodingSessions. - final TranscodingSession session = mPendingTranscodingSessions.remove(sessionId); - - if (session == null) { - // This should not happen in reality. - Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions"); - return; - } - - // Updates the session status and result. - session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED, - TranscodingSession.RESULT_ERROR, errorCode); - - // Notifies client the session failed. - if (session.mListener != null && session.mListenerExecutor != null) { - session.mListenerExecutor.execute( - () -> session.mListener.onTranscodingFinished(session)); - } - } - } - - private void handleTranscodingProgressUpdate(int sessionId, int newProgress) { - synchronized (mPendingTranscodingSessions) { - // Gets the session associated with the sessionId. - final TranscodingSession session = mPendingTranscodingSessions.get(sessionId); - - if (session == null) { - // This should not happen in reality. - Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions"); - return; - } - - // Updates the session progress. - session.updateProgress(newProgress); - - // Notifies client the progress update. - if (session.mProgressUpdateExecutor != null - && session.mProgressUpdateListener != null) { - session.mProgressUpdateExecutor.execute( - () -> session.mProgressUpdateListener.onProgressUpdate(session, - newProgress)); - } - } - } - - private IMediaTranscodingService getService(boolean retry) { - // Do not try to get the service on pre-S. The service is lazy-start and getting the - // service could block. - if (!SdkLevel.isAtLeastS()) { - return null; - } - // Do not try to get the service on AndroidGo (low-ram) devices. - if (mIsLowRamDevice) { - return null; - } - int retryCount = !retry ? 1 : CONNECT_SERVICE_RETRY_COUNT; - Log.i(TAG, "get service with retry " + retryCount); - for (int count = 1; count <= retryCount; count++) { - Log.d(TAG, "Trying to connect to service. Try count: " + count); - IMediaTranscodingService service = IMediaTranscodingService.Stub.asInterface( - MediaFrameworkInitializer - .getMediaServiceManager() - .getMediaTranscodingServiceRegisterer() - .get()); - if (service != null) { - return service; - } - try { - // Sleep a bit before retry. - Thread.sleep(INTERVAL_CONNECT_SERVICE_RETRY_MS); - } catch (InterruptedException ie) { - /* ignore */ - } - } - Log.w(TAG, "Failed to get service"); - return null; - } - - /* - * Handle client binder died event. - * Upon receiving a binder died event of the client, we will do the following: - * 1) For the session that is running, notify the client that the session is failed with - * error code, so client could choose to retry the session or not. - * TODO(hkuang): Add a new error code to signal service died error. - * 2) For the sessions that is still pending or paused, we will resubmit the session - * once we successfully reconnect to the service and register a new client. - * 3) When trying to connect to the service and register a new client. The service may need time - * to reboot or never boot up again. So we will retry for a number of times. If we still - * could not connect, we will notify client session failure for the pending and paused - * sessions. - */ - private void onClientDied() { - synchronized (mLock) { - mTranscodingClient = null; - } - - // Delegates the session notification and retry to the executor as it may take some time. - mExecutor.execute(() -> { - // List to track the sessions that we want to retry. - List<TranscodingSession> retrySessions = new ArrayList<TranscodingSession>(); - - // First notify the client of session failure for all the running sessions. - synchronized (mPendingTranscodingSessions) { - for (Map.Entry<Integer, TranscodingSession> entry : - mPendingTranscodingSessions.entrySet()) { - TranscodingSession session = entry.getValue(); - - if (session.getStatus() == TranscodingSession.STATUS_RUNNING) { - session.updateStatusAndResult(TranscodingSession.STATUS_FINISHED, - TranscodingSession.RESULT_ERROR, - TranscodingSession.ERROR_SERVICE_DIED); - - // Remove the session from pending sessions. - mPendingTranscodingSessions.remove(entry.getKey()); - - if (session.mListener != null && session.mListenerExecutor != null) { - Log.i(TAG, "Notify client session failed"); - session.mListenerExecutor.execute( - () -> session.mListener.onTranscodingFinished(session)); - } - } else if (session.getStatus() == TranscodingSession.STATUS_PENDING - || session.getStatus() == TranscodingSession.STATUS_PAUSED) { - // Add the session to retrySessions to handle them later. - retrySessions.add(session); - } - } - } - - // Try to register with the service once it boots up. - IMediaTranscodingService service = getService(true /*retry*/); - boolean haveTranscodingClient = false; - if (service != null) { - synchronized (mLock) { - mTranscodingClient = registerClient(service); - if (mTranscodingClient != null) { - haveTranscodingClient = true; - } - } - } - - for (TranscodingSession session : retrySessions) { - // Notify the session failure if we fails to connect to the service or fail - // to retry the session. - if (!haveTranscodingClient) { - // TODO(hkuang): Return correct error code to the client. - handleTranscodingFailed(session.getSessionId(), 0 /*unused */); - } - - try { - // Do not set hasRetried for retry initiated by MediaTranscodingManager. - session.retryInternal(false /*setHasRetried*/); - } catch (Exception re) { - // TODO(hkuang): Return correct error code to the client. - handleTranscodingFailed(session.getSessionId(), 0 /*unused */); - } - } - }); - } - - private void updateStatus(int sessionId, int status) { - synchronized (mPendingTranscodingSessions) { - final TranscodingSession session = mPendingTranscodingSessions.get(sessionId); - - if (session == null) { - // This should not happen in reality. - Log.e(TAG, "Session " + sessionId + " is not in Pendingsessions"); - return; - } - - // Updates the session status. - session.updateStatus(status); - } - } - - // Just forwards all the events to the event handler. - private ITranscodingClientCallback mTranscodingClientCallback = - new ITranscodingClientCallback.Stub() { - // TODO(hkuang): Add more unit test to test difference file open mode. - @Override - public ParcelFileDescriptor openFileDescriptor(String fileUri, String mode) - throws RemoteException { - if (!mode.equals("r") && !mode.equals("w") && !mode.equals("rw")) { - Log.e(TAG, "Unsupport mode: " + mode); - return null; - } - - Uri uri = Uri.parse(fileUri); - try { - AssetFileDescriptor afd = mContentResolver.openAssetFileDescriptor(uri, - mode); - if (afd != null) { - return afd.getParcelFileDescriptor(); - } - } catch (FileNotFoundException e) { - Log.w(TAG, "Cannot find content uri: " + uri, e); - } catch (SecurityException e) { - Log.w(TAG, "Cannot open content uri: " + uri, e); - } catch (Exception e) { - Log.w(TAG, "Unknown content uri: " + uri, e); - } - return null; - } - - @Override - public void onTranscodingStarted(int sessionId) throws RemoteException { - updateStatus(sessionId, TranscodingSession.STATUS_RUNNING); - } - - @Override - public void onTranscodingPaused(int sessionId) throws RemoteException { - updateStatus(sessionId, TranscodingSession.STATUS_PAUSED); - } - - @Override - public void onTranscodingResumed(int sessionId) throws RemoteException { - updateStatus(sessionId, TranscodingSession.STATUS_RUNNING); - } - - @Override - public void onTranscodingFinished(int sessionId, TranscodingResultParcel result) - throws RemoteException { - handleTranscodingFinished(sessionId, result); - } - - @Override - public void onTranscodingFailed(int sessionId, int errorCode) - throws RemoteException { - handleTranscodingFailed(sessionId, errorCode); - } - - @Override - public void onAwaitNumberOfSessionsChanged(int sessionId, int oldAwaitNumber, - int newAwaitNumber) throws RemoteException { - //TODO(hkuang): Implement this. - } - - @Override - public void onProgressUpdate(int sessionId, int newProgress) - throws RemoteException { - handleTranscodingProgressUpdate(sessionId, newProgress); - } - }; - - private ITranscodingClient registerClient(IMediaTranscodingService service) { - synchronized (mLock) { - try { - // Registers the client with MediaTranscoding service. - mTranscodingClient = service.registerClient( - mTranscodingClientCallback, - mPackageName, - mPackageName); - - if (mTranscodingClient != null) { - mTranscodingClient.asBinder().linkToDeath(() -> onClientDied(), /* flags */ 0); - } - } catch (Exception ex) { - Log.e(TAG, "Failed to register new client due to exception " + ex); - mTranscodingClient = null; - } - } - return mTranscodingClient; - } - - /** - * @hide - */ - public MediaTranscodingManager(@NonNull Context context) { - mContext = context; - mContentResolver = mContext.getContentResolver(); - mPackageName = mContext.getPackageName(); - mUid = Os.getuid(); - mPid = Os.getpid(); - mIsLowRamDevice = mContext.getSystemService(ActivityManager.class).isLowRamDevice(); - } - - /** - * Abstract base class for all the TranscodingRequest. - * <p> TranscodingRequest encapsulates the desired configuration for the transcoding. - */ - public abstract static class TranscodingRequest { - /** - * - * Default transcoding type. - * @hide - */ - public static final int TRANSCODING_TYPE_UNKNOWN = 0; - - /** - * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video. - * <p>Note that currently only support transcoding video file in mp4 format. - * @hide - */ - public static final int TRANSCODING_TYPE_VIDEO = 1; - - /** - * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image. - * @hide - */ - public static final int TRANSCODING_TYPE_IMAGE = 2; - - /** @hide */ - @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = { - TRANSCODING_TYPE_UNKNOWN, - TRANSCODING_TYPE_VIDEO, - TRANSCODING_TYPE_IMAGE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TranscodingType {} - - /** - * Default value. - * - * @hide - */ - public static final int PRIORITY_UNKNOWN = 0; - /** - * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the - * client wants the transcoding result as soon as possible. - * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve - * performance penalty due to resource reallocation to prioritize the sessions with higher - * priority. - * - * @hide - */ - public static final int PRIORITY_REALTIME = 1; - - /** - * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not - * need the transcoding result as soon as possible. - * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set - * to - * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept - * delay of the transcoding result. - * - * @hide - * - */ - public static final int PRIORITY_OFFLINE = 2; - - /** @hide */ - @IntDef(prefix = {"PRIORITY_"}, value = { - PRIORITY_UNKNOWN, - PRIORITY_REALTIME, - PRIORITY_OFFLINE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TranscodingPriority {} - - /** Uri of the source media file. */ - private @NonNull Uri mSourceUri; - - /** Uri of the destination media file. */ - private @NonNull Uri mDestinationUri; - - /** FileDescriptor of the source media file. */ - private @Nullable ParcelFileDescriptor mSourceFileDescriptor; - - /** FileDescriptor of the destination media file. */ - private @Nullable ParcelFileDescriptor mDestinationFileDescriptor; - - /** - * The UID of the client that the TranscodingRequest is for. Only privileged caller could - * set this Uid as only they could do the transcoding on behalf of the client. - * -1 means not available. - */ - private int mClientUid = -1; - - /** - * The Pid of the client that the TranscodingRequest is for. Only privileged caller could - * set this Uid as only they could do the transcoding on behalf of the client. - * -1 means not available. - */ - private int mClientPid = -1; - - /** Type of the transcoding. */ - private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN; - - /** Priority of the transcoding. */ - private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN; - - /** - * Desired image format for the destination file. - * <p> If this is null, source file's image track will be passed through and copied to the - * destination file. - * @hide - */ - private @Nullable MediaFormat mImageFormat = null; - - @VisibleForTesting - private TranscodingTestConfig mTestConfig = null; - - /** - * Prevent public constructor access. - */ - /* package private */ TranscodingRequest() { - } - - private TranscodingRequest(Builder b) { - mSourceUri = b.mSourceUri; - mSourceFileDescriptor = b.mSourceFileDescriptor; - mDestinationUri = b.mDestinationUri; - mDestinationFileDescriptor = b.mDestinationFileDescriptor; - mClientUid = b.mClientUid; - mClientPid = b.mClientPid; - mPriority = b.mPriority; - mType = b.mType; - mTestConfig = b.mTestConfig; - } - - /** - * Return the type of the transcoding. - * @hide - */ - @TranscodingType - public int getType() { - return mType; - } - - /** Return source uri of the transcoding. */ - @NonNull - public Uri getSourceUri() { - return mSourceUri; - } - - /** - * Return source file descriptor of the transcoding. - * This will be null if client has not provided it. - */ - @Nullable - public ParcelFileDescriptor getSourceFileDescriptor() { - return mSourceFileDescriptor; - } - - /** Return the UID of the client that this request is for. -1 means not available. */ - public int getClientUid() { - return mClientUid; - } - - /** Return the PID of the client that this request is for. -1 means not available. */ - public int getClientPid() { - return mClientPid; - } - - /** Return destination uri of the transcoding. */ - @NonNull - public Uri getDestinationUri() { - return mDestinationUri; - } - - /** - * Return destination file descriptor of the transcoding. - * This will be null if client has not provided it. - */ - @Nullable - public ParcelFileDescriptor getDestinationFileDescriptor() { - return mDestinationFileDescriptor; - } - - /** - * Return priority of the transcoding. - * @hide - */ - @TranscodingPriority - public int getPriority() { - return mPriority; - } - - /** - * Return TestConfig of the transcoding. - * @hide - */ - @Nullable - public TranscodingTestConfig getTestConfig() { - return mTestConfig; - } - - abstract void writeFormatToParcel(TranscodingRequestParcel parcel); - - /* Writes the TranscodingRequest to a parcel. */ - private TranscodingRequestParcel writeToParcel(@NonNull Context context) { - TranscodingRequestParcel parcel = new TranscodingRequestParcel(); - switch (mPriority) { - case PRIORITY_OFFLINE: - parcel.priority = TranscodingSessionPriority.kUnspecified; - break; - case PRIORITY_REALTIME: - case PRIORITY_UNKNOWN: - default: - parcel.priority = TranscodingSessionPriority.kNormal; - break; - } - parcel.transcodingType = mType; - parcel.sourceFilePath = mSourceUri.toString(); - parcel.sourceFd = mSourceFileDescriptor; - parcel.destinationFilePath = mDestinationUri.toString(); - parcel.destinationFd = mDestinationFileDescriptor; - parcel.clientUid = mClientUid; - parcel.clientPid = mClientPid; - if (mClientUid < 0) { - parcel.clientPackageName = context.getPackageName(); - } else { - String packageName = context.getPackageManager().getNameForUid(mClientUid); - // PackageName is optional as some uid does not have package name. Set to - // "Unavailable" string in this case. - if (packageName == null) { - Log.w(TAG, "Failed to find package for uid: " + mClientUid); - packageName = "Unavailable"; - } - parcel.clientPackageName = packageName; - } - writeFormatToParcel(parcel); - if (mTestConfig != null) { - parcel.isForTesting = true; - parcel.testConfig = mTestConfig; - } - return parcel; - } - - /** - * Builder to build a {@link TranscodingRequest} object. - * - * @param <T> The subclass to be built. - */ - abstract static class Builder<T extends Builder<T>> { - private @NonNull Uri mSourceUri; - private @NonNull Uri mDestinationUri; - private @Nullable ParcelFileDescriptor mSourceFileDescriptor = null; - private @Nullable ParcelFileDescriptor mDestinationFileDescriptor = null; - private int mClientUid = -1; - private int mClientPid = -1; - private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN; - private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN; - private TranscodingTestConfig mTestConfig; - - abstract T self(); - - /** - * Creates a builder for building {@link TranscodingRequest}s. - * - * Client must set the source Uri. If client also provides the source fileDescriptor - * through is provided by {@link #setSourceFileDescriptor(ParcelFileDescriptor)}, - * TranscodingSession will use the fd instead of calling back to the client to open the - * sourceUri. - * - * - * @param type The transcoding type. - * @param sourceUri Content uri for the source media file. - * @param destinationUri Content uri for the destination media file. - * - */ - private Builder(@TranscodingType int type, @NonNull Uri sourceUri, - @NonNull Uri destinationUri) { - mType = type; - - if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) { - throw new IllegalArgumentException( - "You must specify a non-empty source Uri."); - } - mSourceUri = sourceUri; - - if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) { - throw new IllegalArgumentException( - "You must specify a non-empty destination Uri."); - } - mDestinationUri = destinationUri; - } - - /** - * Specifies the fileDescriptor opened from the source media file. - * - * This call is optional. If the source fileDescriptor is provided, TranscodingSession - * will use it directly instead of opening the uri from {@link #Builder(int, Uri, Uri)}. - * It is client's responsibility to make sure the fileDescriptor is opened from the - * source uri. - * @param fileDescriptor a {@link ParcelFileDescriptor} opened from source media file. - * @return The same builder instance. - * @throws IllegalArgumentException if fileDescriptor is invalid. - */ - @NonNull - public T setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) { - if (fileDescriptor == null || fileDescriptor.getFd() < 0) { - throw new IllegalArgumentException( - "Invalid source descriptor."); - } - mSourceFileDescriptor = fileDescriptor; - return self(); - } - - /** - * Specifies the fileDescriptor opened from the destination media file. - * - * This call is optional. If the destination fileDescriptor is provided, - * TranscodingSession will use it directly instead of opening the source uri from - * {@link #Builder(int, Uri, Uri)} upon transcoding starts. It is client's - * responsibility to make sure the fileDescriptor is opened from the destination uri. - * @param fileDescriptor a {@link ParcelFileDescriptor} opened from destination media - * file. - * @return The same builder instance. - * @throws IllegalArgumentException if fileDescriptor is invalid. - */ - @NonNull - public T setDestinationFileDescriptor( - @NonNull ParcelFileDescriptor fileDescriptor) { - if (fileDescriptor == null || fileDescriptor.getFd() < 0) { - throw new IllegalArgumentException( - "Invalid destination descriptor."); - } - mDestinationFileDescriptor = fileDescriptor; - return self(); - } - - /** - * Specify the UID of the client that this request is for. - * <p> - * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the - * pid. Note that the permission check happens on the service side upon starting the - * transcoding. If the client does not have the permission, the transcoding will fail. - * - * @param uid client Uid. - * @return The same builder instance. - * @throws IllegalArgumentException if uid is invalid. - */ - @NonNull - public T setClientUid(int uid) { - if (uid < 0) { - throw new IllegalArgumentException("Invalid Uid"); - } - mClientUid = uid; - return self(); - } - - /** - * Specify the pid of the client that this request is for. - * <p> - * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the - * pid. Note that the permission check happens on the service side upon starting the - * transcoding. If the client does not have the permission, the transcoding will fail. - * - * @param pid client Pid. - * @return The same builder instance. - * @throws IllegalArgumentException if pid is invalid. - */ - @NonNull - public T setClientPid(int pid) { - if (pid < 0) { - throw new IllegalArgumentException("Invalid pid"); - } - mClientPid = pid; - return self(); - } - - /** - * Specifies the priority of the transcoding. - * - * @param priority Must be one of the {@code PRIORITY_*} - * @return The same builder instance. - * @throws IllegalArgumentException if flags is invalid. - * @hide - */ - @NonNull - public T setPriority(@TranscodingPriority int priority) { - if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) { - throw new IllegalArgumentException("Invalid priority: " + priority); - } - mPriority = priority; - return self(); - } - - /** - * Sets the delay in processing this request. - * @param config test config. - * @return The same builder instance. - * @hide - */ - @VisibleForTesting - @NonNull - public T setTestConfig(@NonNull TranscodingTestConfig config) { - mTestConfig = config; - return self(); - } - } - - /** - * Abstract base class for all the format resolvers. - */ - abstract static class MediaFormatResolver { - private @NonNull ApplicationMediaCapabilities mClientCaps; - - /** - * Prevents public constructor access. - */ - /* package private */ MediaFormatResolver() { - } - - /** - * Constructs MediaFormatResolver object. - * - * @param clientCaps An ApplicationMediaCapabilities object containing the client's - * capabilities. - */ - MediaFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps) { - if (clientCaps == null) { - throw new IllegalArgumentException("Client capabilities must not be null"); - } - mClientCaps = clientCaps; - } - - /** - * Returns the client capabilities. - */ - @NonNull - /* package */ ApplicationMediaCapabilities getClientCapabilities() { - return mClientCaps; - } - - abstract boolean shouldTranscode(); - } - - /** - * VideoFormatResolver for deciding if video transcoding is needed, and if so, the track - * formats to use. - */ - public static class VideoFormatResolver extends MediaFormatResolver { - private static final int BIT_RATE = 20000000; // 20Mbps - - private MediaFormat mSrcVideoFormatHint; - private MediaFormat mSrcAudioFormatHint; - - /** - * Constructs a new VideoFormatResolver object. - * - * @param clientCaps An ApplicationMediaCapabilities object containing the client's - * capabilities. - * @param srcVideoFormatHint A MediaFormat object containing information about the - * source's video track format that could affect the - * transcoding decision. Such information could include video - * codec types, color spaces, whether special format info (eg. - * slow-motion markers) are present, etc.. If a particular - * information is not present, it will not be used to make the - * decision. - */ - public VideoFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps, - @NonNull MediaFormat srcVideoFormatHint) { - super(clientCaps); - mSrcVideoFormatHint = srcVideoFormatHint; - } - - /** - * Constructs a new VideoFormatResolver object. - * - * @param clientCaps An ApplicationMediaCapabilities object containing the client's - * capabilities. - * @param srcVideoFormatHint A MediaFormat object containing information about the - * source's video track format that could affect the - * transcoding decision. Such information could include video - * codec types, color spaces, whether special format info (eg. - * slow-motion markers) are present, etc.. If a particular - * information is not present, it will not be used to make the - * decision. - * @param srcAudioFormatHint A MediaFormat object containing information about the - * source's audio track format that could affect the - * transcoding decision. - * @hide - */ - VideoFormatResolver(@NonNull ApplicationMediaCapabilities clientCaps, - @NonNull MediaFormat srcVideoFormatHint, - @NonNull MediaFormat srcAudioFormatHint) { - super(clientCaps); - mSrcVideoFormatHint = srcVideoFormatHint; - mSrcAudioFormatHint = srcAudioFormatHint; - } - - /** - * Returns whether the source content should be transcoded. - * - * @return true if the source should be transcoded. - */ - public boolean shouldTranscode() { - boolean supportHevc = getClientCapabilities().isVideoMimeTypeSupported( - MediaFormat.MIMETYPE_VIDEO_HEVC); - if (!supportHevc && MediaFormat.MIMETYPE_VIDEO_HEVC.equals( - mSrcVideoFormatHint.getString(MediaFormat.KEY_MIME))) { - return true; - } - // TODO: add more checks as needed below. - return false; - } - - /** - * Retrieves the video track format to be used on - * {@link VideoTranscodingRequest.Builder#setVideoTrackFormat(MediaFormat)} for this - * configuration. - * - * @return the video track format to be used if transcoding should be performed, - * and null otherwise. - * @throws IllegalArgumentException if the hinted source video format contains invalid - * parameters. - */ - @Nullable - public MediaFormat resolveVideoFormat() { - if (!shouldTranscode()) { - return null; - } - - MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint); - videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC); - - int width = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_WIDTH, -1); - int height = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_HEIGHT, -1); - if (width <= 0 || height <= 0) { - throw new IllegalArgumentException( - "Source Width and height must be larger than 0"); - } - - float frameRate = - mSrcVideoFormatHint.getNumber(MediaFormat.KEY_FRAME_RATE, 30.0) - .floatValue(); - if (frameRate <= 0) { - throw new IllegalArgumentException( - "frameRate must be larger than 0"); - } - - int bitrate = getAVCBitrate(width, height, frameRate); - videoTrackFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); - return videoTrackFormat; - } - - /** - * Generate a default bitrate with the fixed bpp(bits-per-pixel) 0.25. - * This maps to: - * 1080P@30fps -> 16Mbps - * 1080P@60fps-> 32Mbps - * 4K@30fps -> 62Mbps - */ - private static int getDefaultBitrate(int width, int height, float frameRate) { - return (int) (width * height * frameRate * BPP); - } - - /** - * Query the bitrate from CamcorderProfile. If there are two profiles that match the - * width/height/framerate, we will use the higher one to get better quality. - * Return default bitrate if could not find any match profile. - */ - private static int getAVCBitrate(int width, int height, float frameRate) { - int bitrate = -1; - int[] cameraIds = {0, 1}; - - // Profiles ordered in decreasing order of preference. - int[] preferQualities = { - CamcorderProfile.QUALITY_2160P, - CamcorderProfile.QUALITY_1080P, - CamcorderProfile.QUALITY_720P, - CamcorderProfile.QUALITY_480P, - CamcorderProfile.QUALITY_LOW, - }; - - for (int cameraId : cameraIds) { - for (int quality : preferQualities) { - // Check if camera id has profile for the quality level. - if (!CamcorderProfile.hasProfile(cameraId, quality)) { - continue; - } - CamcorderProfile profile = CamcorderProfile.get(cameraId, quality); - // Check the width/height/framerate/codec, also consider portrait case. - if (((width == profile.videoFrameWidth - && height == profile.videoFrameHeight) - || (height == profile.videoFrameWidth - && width == profile.videoFrameHeight)) - && (int) frameRate == profile.videoFrameRate - && profile.videoCodec == MediaRecorder.VideoEncoder.H264) { - if (bitrate < profile.videoBitRate) { - bitrate = profile.videoBitRate; - } - break; - } - } - } - - if (bitrate == -1) { - Log.w(TAG, "Failed to find CamcorderProfile for w: " + width + "h: " + height - + " fps: " - + frameRate); - bitrate = getDefaultBitrate(width, height, frameRate); - } - Log.d(TAG, "Using bitrate " + bitrate + " for " + width + " " + height + " " - + frameRate); - return bitrate; - } - - /** - * Retrieves the audio track format to be used for transcoding. - * - * @return the audio track format to be used if transcoding should be performed, and - * null otherwise. - * @hide - */ - @Nullable - public MediaFormat resolveAudioFormat() { - if (!shouldTranscode()) { - return null; - } - // Audio transcoding is not supported yet, always return null. - return null; - } - } - } - - /** - * VideoTranscodingRequest encapsulates the configuration for transcoding a video. - */ - public static final class VideoTranscodingRequest extends TranscodingRequest { - /** - * Desired output video format of the destination file. - * <p> If this is null, source file's video track will be passed through and copied to the - * destination file. - */ - private @Nullable MediaFormat mVideoTrackFormat = null; - - /** - * Desired output audio format of the destination file. - * <p> If this is null, source file's audio track will be passed through and copied to the - * destination file. - */ - private @Nullable MediaFormat mAudioTrackFormat = null; - - private VideoTranscodingRequest(VideoTranscodingRequest.Builder builder) { - super(builder); - mVideoTrackFormat = builder.mVideoTrackFormat; - mAudioTrackFormat = builder.mAudioTrackFormat; - } - - /** - * Return the video track format of the transcoding. - * This will be null if client has not specified the video track format. - */ - @NonNull - public MediaFormat getVideoTrackFormat() { - return mVideoTrackFormat; - } - - @Override - void writeFormatToParcel(TranscodingRequestParcel parcel) { - parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat); - } - - /* Converts the MediaFormat to TranscodingVideoTrackFormat. */ - private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) { - if (format == null) { - throw new IllegalArgumentException("Invalid MediaFormat"); - } - - TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat(); - - if (format.containsKey(MediaFormat.KEY_MIME)) { - String mime = format.getString(MediaFormat.KEY_MIME); - if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) { - trackFormat.codecType = TranscodingVideoCodecType.kAvc; - } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) { - trackFormat.codecType = TranscodingVideoCodecType.kHevc; - } else { - throw new UnsupportedOperationException("Only support transcode to avc/hevc"); - } - } - - if (format.containsKey(MediaFormat.KEY_BIT_RATE)) { - int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE); - if (bitrateBps <= 0) { - throw new IllegalArgumentException("Bitrate must be larger than 0"); - } - trackFormat.bitrateBps = bitrateBps; - } - - if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey( - MediaFormat.KEY_HEIGHT)) { - int width = format.getInteger(MediaFormat.KEY_WIDTH); - int height = format.getInteger(MediaFormat.KEY_HEIGHT); - if (width <= 0 || height <= 0) { - throw new IllegalArgumentException("Width and height must be larger than 0"); - } - // TODO: Validate the aspect ratio after adding scaling. - trackFormat.width = width; - trackFormat.height = height; - } - - if (format.containsKey(MediaFormat.KEY_PROFILE)) { - int profile = format.getInteger(MediaFormat.KEY_PROFILE); - if (profile <= 0) { - throw new IllegalArgumentException("Invalid codec profile"); - } - // TODO: Validate the profile according to codec type. - trackFormat.profile = profile; - } - - if (format.containsKey(MediaFormat.KEY_LEVEL)) { - int level = format.getInteger(MediaFormat.KEY_LEVEL); - if (level <= 0) { - throw new IllegalArgumentException("Invalid codec level"); - } - // TODO: Validate the level according to codec type. - trackFormat.level = level; - } - - return trackFormat; - } - - /** - * Builder class for {@link VideoTranscodingRequest}. - */ - public static final class Builder extends - TranscodingRequest.Builder<VideoTranscodingRequest.Builder> { - /** - * Desired output video format of the destination file. - * <p> If this is null, source file's video track will be passed through and - * copied to the destination file. - */ - private @Nullable MediaFormat mVideoTrackFormat = null; - - /** - * Desired output audio format of the destination file. - * <p> If this is null, source file's audio track will be passed through and copied - * to the destination file. - */ - private @Nullable MediaFormat mAudioTrackFormat = null; - - /** - * Creates a builder for building {@link VideoTranscodingRequest}s. - * - * <p> Client could only specify the settings that matters to them, e.g. codec format or - * bitrate. And by default, transcoding will preserve the original video's settings - * (bitrate, framerate, resolution) if not provided. - * <p>Note that some settings may silently fail to apply if the device does not support - * them. - * @param sourceUri Content uri for the source media file. - * @param destinationUri Content uri for the destination media file. - * @param videoFormat MediaFormat containing the settings that client wants override in - * the original video's video track. - * @throws IllegalArgumentException if videoFormat is invalid. - */ - public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri, - @NonNull MediaFormat videoFormat) { - super(TRANSCODING_TYPE_VIDEO, sourceUri, destinationUri); - setVideoTrackFormat(videoFormat); - } - - @Override - @NonNull - public Builder setClientUid(int uid) { - super.setClientUid(uid); - return self(); - } - - @Override - @NonNull - public Builder setClientPid(int pid) { - super.setClientPid(pid); - return self(); - } - - @Override - @NonNull - public Builder setSourceFileDescriptor(@NonNull ParcelFileDescriptor fd) { - super.setSourceFileDescriptor(fd); - return self(); - } - - @Override - @NonNull - public Builder setDestinationFileDescriptor(@NonNull ParcelFileDescriptor fd) { - super.setDestinationFileDescriptor(fd); - return self(); - } - - private void setVideoTrackFormat(@NonNull MediaFormat videoFormat) { - if (videoFormat == null) { - throw new IllegalArgumentException("videoFormat must not be null"); - } - - // Check if the MediaFormat is for video by looking at the MIME type. - String mime = videoFormat.containsKey(MediaFormat.KEY_MIME) - ? videoFormat.getString(MediaFormat.KEY_MIME) : null; - if (mime == null || !mime.startsWith("video/")) { - throw new IllegalArgumentException("Invalid video format: wrong mime type"); - } - - mVideoTrackFormat = videoFormat; - } - - /** - * @return a new {@link TranscodingRequest} instance successfully initialized - * with all the parameters set on this <code>Builder</code>. - * @throws UnsupportedOperationException if the parameters set on the - * <code>Builder</code> were incompatible, or - * if they are not supported by the - * device. - */ - @NonNull - public VideoTranscodingRequest build() { - return new VideoTranscodingRequest(this); - } - - @Override - VideoTranscodingRequest.Builder self() { - return this; - } - } - } - - /** - * Handle to an enqueued transcoding operation. An instance of this class represents a single - * enqueued transcoding operation. The caller can use that instance to query the status or - * progress, and to get the result once the operation has completed. - */ - public static final class TranscodingSession { - /** The session is enqueued but not yet running. */ - public static final int STATUS_PENDING = 1; - /** The session is currently running. */ - public static final int STATUS_RUNNING = 2; - /** The session is finished. */ - public static final int STATUS_FINISHED = 3; - /** The session is paused. */ - public static final int STATUS_PAUSED = 4; - - /** @hide */ - @IntDef(prefix = { "STATUS_" }, value = { - STATUS_PENDING, - STATUS_RUNNING, - STATUS_FINISHED, - STATUS_PAUSED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Status {} - - /** The session does not have a result yet. */ - public static final int RESULT_NONE = 1; - /** The session completed successfully. */ - public static final int RESULT_SUCCESS = 2; - /** The session encountered an error while running. */ - public static final int RESULT_ERROR = 3; - /** The session was canceled by the caller. */ - public static final int RESULT_CANCELED = 4; - - /** @hide */ - @IntDef(prefix = { "RESULT_" }, value = { - RESULT_NONE, - RESULT_SUCCESS, - RESULT_ERROR, - RESULT_CANCELED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Result {} - - - // The error code exposed here should be in sync with: - // frameworks/av/media/libmediatranscoding/aidl/android/media/TranscodingErrorCode.aidl - /** @hide */ - @IntDef(prefix = { "TRANSCODING_SESSION_ERROR_" }, value = { - ERROR_NONE, - ERROR_DROPPED_BY_SERVICE, - ERROR_SERVICE_DIED}) - @Retention(RetentionPolicy.SOURCE) - public @interface TranscodingSessionErrorCode{} - /** - * Constant indicating that no error occurred. - */ - public static final int ERROR_NONE = 0; - - /** - * Constant indicating that the session is dropped by Transcoding service due to hitting - * the limit, e.g. too many back to back transcoding happen in a short time frame. - */ - public static final int ERROR_DROPPED_BY_SERVICE = 1; - - /** - * Constant indicating the backing transcoding service is died. Client should enqueue the - * the request again. - */ - public static final int ERROR_SERVICE_DIED = 2; - - /** Listener that gets notified when the progress changes. */ - @FunctionalInterface - public interface OnProgressUpdateListener { - /** - * Called when the progress changes. The progress is in percentage between 0 and 1, - * where 0 means the session has not yet started and 100 means that it has finished. - * - * @param session The session associated with the progress. - * @param progress The new progress ranging from 0 ~ 100 inclusive. - */ - void onProgressUpdate(@NonNull TranscodingSession session, - @IntRange(from = 0, to = 100) int progress); - } - - private final MediaTranscodingManager mManager; - private Executor mListenerExecutor; - private OnTranscodingFinishedListener mListener; - private int mSessionId = -1; - // Lock for internal state. - private final Object mLock = new Object(); - @GuardedBy("mLock") - private Executor mProgressUpdateExecutor = null; - @GuardedBy("mLock") - private OnProgressUpdateListener mProgressUpdateListener = null; - @GuardedBy("mLock") - private int mProgress = 0; - @GuardedBy("mLock") - private int mProgressUpdateInterval = 0; - @GuardedBy("mLock") - private @Status int mStatus = STATUS_PENDING; - @GuardedBy("mLock") - private @Result int mResult = RESULT_NONE; - @GuardedBy("mLock") - private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE; - @GuardedBy("mLock") - private boolean mHasRetried = false; - // The original request that associated with this session. - private final TranscodingRequest mRequest; - - private TranscodingSession( - @NonNull MediaTranscodingManager manager, - @NonNull TranscodingRequest request, - @NonNull TranscodingSessionParcel parcel, - @NonNull @CallbackExecutor Executor executor, - @NonNull OnTranscodingFinishedListener listener) { - Objects.requireNonNull(manager, "manager must not be null"); - Objects.requireNonNull(parcel, "parcel must not be null"); - Objects.requireNonNull(executor, "listenerExecutor must not be null"); - Objects.requireNonNull(listener, "listener must not be null"); - mManager = manager; - mSessionId = parcel.sessionId; - mListenerExecutor = executor; - mListener = listener; - mRequest = request; - } - - /** - * Set a progress listener. - * @param executor The executor on which listener will be invoked. - * @param listener The progress listener. - */ - public void setOnProgressUpdateListener( - @NonNull @CallbackExecutor Executor executor, - @Nullable OnProgressUpdateListener listener) { - synchronized (mLock) { - Objects.requireNonNull(executor, "listenerExecutor must not be null"); - Objects.requireNonNull(listener, "listener must not be null"); - mProgressUpdateExecutor = executor; - mProgressUpdateListener = listener; - } - } - - private void updateStatusAndResult(@Status int sessionStatus, - @Result int sessionResult, @TranscodingSessionErrorCode int errorCode) { - synchronized (mLock) { - mStatus = sessionStatus; - mResult = sessionResult; - mErrorCode = errorCode; - } - } - - /** - * Retrieve the error code associated with the RESULT_ERROR. - */ - public @TranscodingSessionErrorCode int getErrorCode() { - synchronized (mLock) { - return mErrorCode; - } - } - - /** - * Resubmit the transcoding session to the service. - * Note that only the session that fails or gets cancelled could be retried and each session - * could be retried only once. After that, Client need to enqueue a new request if they want - * to try again. - * - * @return true if successfully resubmit the job to service. False otherwise. - * @throws UnsupportedOperationException if the retry could not be fulfilled. - * @hide - */ - public boolean retry() { - return retryInternal(true /*setHasRetried*/); - } - - // TODO(hkuang): Add more test for it. - private boolean retryInternal(boolean setHasRetried) { - synchronized (mLock) { - if (mStatus == STATUS_PENDING || mStatus == STATUS_RUNNING) { - throw new UnsupportedOperationException( - "Failed to retry as session is in processing"); - } - - if (mHasRetried) { - throw new UnsupportedOperationException("Session has been retried already"); - } - - // Get the client interface. - ITranscodingClient client = mManager.getTranscodingClient(); - if (client == null) { - Log.e(TAG, "Service rebooting. Try again later"); - return false; - } - - synchronized (mManager.mPendingTranscodingSessions) { - try { - // Submits the request to MediaTranscoding service. - TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel(); - if (!client.submitRequest(mRequest.writeToParcel(mManager.mContext), - sessionParcel)) { - mHasRetried = true; - throw new UnsupportedOperationException("Failed to enqueue request"); - } - - // Replace the old session id wit the new one. - mSessionId = sessionParcel.sessionId; - // Adds the new session back into pending sessions. - mManager.mPendingTranscodingSessions.put(mSessionId, this); - } catch (RemoteException re) { - return false; - } - mStatus = STATUS_PENDING; - mHasRetried = setHasRetried ? true : false; - } - } - return true; - } - - /** - * Cancels the transcoding session and notify the listener. - * If the session happened to finish before being canceled this call is effectively a no-op - * and will not update the result in that case. - */ - public void cancel() { - synchronized (mLock) { - // Check if the session is finished already. - if (mStatus != STATUS_FINISHED) { - try { - ITranscodingClient client = mManager.getTranscodingClient(); - // The client may be gone. - if (client != null) { - client.cancelSession(mSessionId); - } - } catch (RemoteException re) { - //TODO(hkuang): Find out what to do if failing to cancel the session. - Log.e(TAG, "Failed to cancel the session due to exception: " + re); - } - mStatus = STATUS_FINISHED; - mResult = RESULT_CANCELED; - - // Notifies client the session is canceled. - mListenerExecutor.execute(() -> mListener.onTranscodingFinished(this)); - } - } - } - - /** - * Gets the progress of the transcoding session. The progress is between 0 and 100, where 0 - * means that the session has not yet started and 100 means that it is finished. For the - * cancelled session, the progress will be the last updated progress before it is cancelled. - * @return The progress. - */ - @IntRange(from = 0, to = 100) - public int getProgress() { - synchronized (mLock) { - return mProgress; - } - } - - /** - * Gets the status of the transcoding session. - * @return The status. - */ - public @Status int getStatus() { - synchronized (mLock) { - return mStatus; - } - } - - /** - * Adds a client uid that is also waiting for this transcoding session. - * <p> - * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the - * uid. Note that the permission check happens on the service side upon starting the - * transcoding. If the client does not have the permission, the transcoding will fail. - * @param uid the additional client uid to be added. - * @return true if successfully added, false otherwise. - */ - public boolean addClientUid(int uid) { - if (uid < 0) { - throw new IllegalArgumentException("Invalid Uid"); - } - - // Get the client interface. - ITranscodingClient client = mManager.getTranscodingClient(); - if (client == null) { - Log.e(TAG, "Service is dead..."); - return false; - } - - try { - if (!client.addClientUid(mSessionId, uid)) { - Log.e(TAG, "Failed to add client uid"); - return false; - } - } catch (Exception ex) { - Log.e(TAG, "Failed to get client uids due to " + ex); - return false; - } - return true; - } - - /** - * Query all the client that waiting for this transcoding session - * @return a list containing all the client uids. - */ - @NonNull - public List<Integer> getClientUids() { - List<Integer> uidList = new ArrayList<Integer>(); - - // Get the client interface. - ITranscodingClient client = mManager.getTranscodingClient(); - if (client == null) { - Log.e(TAG, "Service is dead..."); - return uidList; - } - - try { - int[] clientUids = client.getClientUids(mSessionId); - for (int i : clientUids) { - uidList.add(i); - } - } catch (Exception ex) { - Log.e(TAG, "Failed to get client uids due to " + ex); - } - - return uidList; - } - - /** - * Gets sessionId of the transcoding session. - * @return session id. - */ - public int getSessionId() { - return mSessionId; - } - - /** - * Gets the result of the transcoding session. - * @return The result. - */ - public @Result int getResult() { - synchronized (mLock) { - return mResult; - } - } - - @Override - public String toString() { - String result; - String status; - - switch (mResult) { - case RESULT_NONE: - result = "RESULT_NONE"; - break; - case RESULT_SUCCESS: - result = "RESULT_SUCCESS"; - break; - case RESULT_ERROR: - result = "RESULT_ERROR(" + mErrorCode + ")"; - break; - case RESULT_CANCELED: - result = "RESULT_CANCELED"; - break; - default: - result = String.valueOf(mResult); - break; - } - - switch (mStatus) { - case STATUS_PENDING: - status = "STATUS_PENDING"; - break; - case STATUS_PAUSED: - status = "STATUS_PAUSED"; - break; - case STATUS_RUNNING: - status = "STATUS_RUNNING"; - break; - case STATUS_FINISHED: - status = "STATUS_FINISHED"; - break; - default: - status = String.valueOf(mStatus); - break; - } - return String.format(" session: {id: %d, status: %s, result: %s, progress: %d}", - mSessionId, status, result, mProgress); - } - - private void updateProgress(int newProgress) { - synchronized (mLock) { - mProgress = newProgress; - } - } - - private void updateStatus(int newStatus) { - synchronized (mLock) { - mStatus = newStatus; - } - } - } - - private ITranscodingClient getTranscodingClient() { - synchronized (mLock) { - return mTranscodingClient; - } - } - - /** - * Enqueues a TranscodingRequest for execution. - * <p> Upon successfully accepting the request, MediaTranscodingManager will return a - * {@link TranscodingSession} to the client. Client should use {@link TranscodingSession} to - * track the progress and get the result. - * <p> MediaTranscodingManager will return null if fails to accept the request due to service - * rebooting. Client could retry again after receiving null. - * - * @param transcodingRequest The TranscodingRequest to enqueue. - * @param listenerExecutor Executor on which the listener is notified. - * @param listener Listener to get notified when the transcoding session is finished. - * @return A TranscodingSession for this operation. - * @throws UnsupportedOperationException if the request could not be fulfilled. - */ - @Nullable - public TranscodingSession enqueueRequest( - @NonNull TranscodingRequest transcodingRequest, - @NonNull @CallbackExecutor Executor listenerExecutor, - @NonNull OnTranscodingFinishedListener listener) { - Log.i(TAG, "enqueueRequest called."); - Objects.requireNonNull(transcodingRequest, "transcodingRequest must not be null"); - Objects.requireNonNull(listenerExecutor, "listenerExecutor must not be null"); - Objects.requireNonNull(listener, "listener must not be null"); - - // Converts the request to TranscodingRequestParcel. - TranscodingRequestParcel requestParcel = transcodingRequest.writeToParcel(mContext); - - Log.i(TAG, "Getting transcoding request " + transcodingRequest.getSourceUri()); - - // Submits the request to MediaTranscoding service. - try { - TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel(); - // Synchronizes the access to mPendingTranscodingSessions to make sure the session Id is - // inserted in the mPendingTranscodingSessions in the callback handler. - synchronized (mPendingTranscodingSessions) { - synchronized (mLock) { - if (mTranscodingClient == null) { - // Try to register with the service again. - IMediaTranscodingService service = getService(false /*retry*/); - if (service == null) { - Log.w(TAG, "Service rebooting. Try again later"); - return null; - } - mTranscodingClient = registerClient(service); - // If still fails, throws an exception to tell client to try later. - if (mTranscodingClient == null) { - Log.w(TAG, "Service rebooting. Try again later"); - return null; - } - } - - if (!mTranscodingClient.submitRequest(requestParcel, sessionParcel)) { - throw new UnsupportedOperationException("Failed to enqueue request"); - } - } - - // Wraps the TranscodingSessionParcel into a TranscodingSession and returns it to - // client for tracking. - TranscodingSession session = new TranscodingSession(this, transcodingRequest, - sessionParcel, - listenerExecutor, - listener); - - // Adds the new session into pending sessions. - mPendingTranscodingSessions.put(session.getSessionId(), session); - return session; - } - } catch (RemoteException ex) { - Log.w(TAG, "Service rebooting. Try again later"); - return null; - } catch (ServiceSpecificException ex) { - throw new UnsupportedOperationException( - "Failed to submit request to Transcoding service. Error: " + ex); - } - } -} diff --git a/apex/media/framework/java/android/media/ProxyDataSourceCallback.java b/apex/media/framework/java/android/media/ProxyDataSourceCallback.java deleted file mode 100644 index 14d3ce87f03d..000000000000 --- a/apex/media/framework/java/android/media/ProxyDataSourceCallback.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2019 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.media; - -import android.os.ParcelFileDescriptor; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.IOException; - -/** - * A DataSourceCallback that is backed by a ParcelFileDescriptor. - */ -class ProxyDataSourceCallback extends DataSourceCallback { - private static final String TAG = "TestDataSourceCallback"; - - ParcelFileDescriptor mPFD; - FileDescriptor mFD; - - ProxyDataSourceCallback(ParcelFileDescriptor pfd) throws IOException { - mPFD = pfd.dup(); - mFD = mPFD.getFileDescriptor(); - } - - @Override - public synchronized int readAt(long position, byte[] buffer, int offset, int size) - throws IOException { - try { - Os.lseek(mFD, position, OsConstants.SEEK_SET); - int ret = Os.read(mFD, buffer, offset, size); - return (ret == 0) ? END_OF_STREAM : ret; - } catch (ErrnoException e) { - throw new IOException(e); - } - } - - @Override - public synchronized long getSize() throws IOException { - return mPFD.getStatSize(); - } - - @Override - public synchronized void close() { - try { - mPFD.close(); - } catch (IOException e) { - Log.e(TAG, "Failed to close the PFD.", e); - } - } -} - diff --git a/apex/media/framework/java/android/media/RoutingDelegate.java b/apex/media/framework/java/android/media/RoutingDelegate.java deleted file mode 100644 index 23598130f391..000000000000 --- a/apex/media/framework/java/android/media/RoutingDelegate.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018 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.media; - -import android.os.Handler; - -class RoutingDelegate implements AudioRouting.OnRoutingChangedListener { - private AudioRouting mAudioRouting; - private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener; - private Handler mHandler; - - RoutingDelegate(final AudioRouting audioRouting, - final AudioRouting.OnRoutingChangedListener listener, - Handler handler) { - mAudioRouting = audioRouting; - mOnRoutingChangedListener = listener; - mHandler = handler; - } - - public AudioRouting.OnRoutingChangedListener getListener() { - return mOnRoutingChangedListener; - } - - public Handler getHandler() { - return mHandler; - } - - @Override - public void onRoutingChanged(AudioRouting router) { - if (mOnRoutingChangedListener != null) { - mOnRoutingChangedListener.onRoutingChanged(mAudioRouting); - } - } -} diff --git a/apex/media/framework/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java deleted file mode 100644 index 26f4568fa7e5..000000000000 --- a/apex/media/framework/java/android/media/Session2Command.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2018 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.media; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; - -import java.util.Objects; - -/** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}. - * <p> - * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command. - * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and - * {@link #getCustomAction()} shouldn't be {@code null}. - * <p> - * Refer to the - * <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a> - * class for the list of valid commands. - */ -public final class Session2Command implements Parcelable { - /** - * Command code for the custom command which can be defined by string action in the - * {@link Session2Command}. - */ - public static final int COMMAND_CODE_CUSTOM = 0; - - public static final @android.annotation.NonNull Parcelable.Creator<Session2Command> CREATOR = - new Parcelable.Creator<Session2Command>() { - @Override - public Session2Command createFromParcel(Parcel in) { - return new Session2Command(in); - } - - @Override - public Session2Command[] newArray(int size) { - return new Session2Command[size]; - } - }; - - private final int mCommandCode; - // Nonnull if it's custom command - private final String mCustomAction; - private final Bundle mCustomExtras; - - /** - * Constructor for creating a command predefined in AndroidX media2. - * - * @param commandCode A command code for a command predefined in AndroidX media2. - */ - public Session2Command(int commandCode) { - if (commandCode == COMMAND_CODE_CUSTOM) { - throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM"); - } - mCommandCode = commandCode; - mCustomAction = null; - mCustomExtras = null; - } - - /** - * Constructor for creating a custom command. - * - * @param action The action of this custom command. - * @param extras An extra bundle for this custom command. - */ - public Session2Command(@NonNull String action, @Nullable Bundle extras) { - if (action == null) { - throw new IllegalArgumentException("action shouldn't be null"); - } - mCommandCode = COMMAND_CODE_CUSTOM; - mCustomAction = action; - mCustomExtras = extras; - } - - /** - * Used by parcelable creator. - */ - @SuppressWarnings("WeakerAccess") /* synthetic access */ - Session2Command(Parcel in) { - mCommandCode = in.readInt(); - mCustomAction = in.readString(); - mCustomExtras = in.readBundle(); - } - - /** - * Gets the command code of a predefined command. - * This will return {@link #COMMAND_CODE_CUSTOM} for a custom command. - */ - public int getCommandCode() { - return mCommandCode; - } - - /** - * Gets the action of a custom command. - * This will return {@code null} for a predefined command. - */ - @Nullable - public String getCustomAction() { - return mCustomAction; - } - - /** - * Gets the extra bundle of a custom command. - * This will return {@code null} for a predefined command. - */ - @Nullable - public Bundle getCustomExtras() { - return mCustomExtras; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - if (dest == null) { - throw new IllegalArgumentException("parcel shouldn't be null"); - } - dest.writeInt(mCommandCode); - dest.writeString(mCustomAction); - dest.writeBundle(mCustomExtras); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof Session2Command)) { - return false; - } - Session2Command other = (Session2Command) obj; - return mCommandCode == other.mCommandCode - && TextUtils.equals(mCustomAction, other.mCustomAction); - } - - @Override - public int hashCode() { - return Objects.hash(mCustomAction, mCommandCode); - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Contains the result of {@link Session2Command}. - */ - public static final class Result { - private final int mResultCode; - private final Bundle mResultData; - - /** - * Result code representing that the command is skipped or canceled. For an example, a seek - * command can be skipped if it is followed by another seek command. - */ - public static final int RESULT_INFO_SKIPPED = 1; - - /** - * Result code representing that the command is successfully completed. - */ - public static final int RESULT_SUCCESS = 0; - - /** - * Result code represents that call is ended with an unknown error. - */ - public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; - - /** - * Constructor of {@link Result}. - * - * @param resultCode result code - * @param resultData result data - */ - public Result(int resultCode, @Nullable Bundle resultData) { - mResultCode = resultCode; - mResultData = resultData; - } - - /** - * Returns the result code. - */ - public int getResultCode() { - return mResultCode; - } - - /** - * Returns the result data. - */ - @Nullable - public Bundle getResultData() { - return mResultData; - } - } -} diff --git a/apex/media/framework/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java deleted file mode 100644 index 13aabfc45ab7..000000000000 --- a/apex/media/framework/java/android/media/Session2CommandGroup.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2018 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.media; - -import static android.media.Session2Command.COMMAND_CODE_CUSTOM; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -/** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * A set of {@link Session2Command} which represents a command group. - */ -public final class Session2CommandGroup implements Parcelable { - private static final String TAG = "Session2CommandGroup"; - - public static final @android.annotation.NonNull Parcelable.Creator<Session2CommandGroup> - CREATOR = new Parcelable.Creator<Session2CommandGroup>() { - @Override - public Session2CommandGroup createFromParcel(Parcel in) { - return new Session2CommandGroup(in); - } - - @Override - public Session2CommandGroup[] newArray(int size) { - return new Session2CommandGroup[size]; - } - }; - - Set<Session2Command> mCommands = new HashSet<>(); - - /** - * Creates a new Session2CommandGroup with commands copied from another object. - * - * @param commands The collection of commands to copy. - */ - @SuppressWarnings("WeakerAccess") /* synthetic access */ - Session2CommandGroup(@Nullable Collection<Session2Command> commands) { - if (commands != null) { - mCommands.addAll(commands); - } - } - - /** - * Used by parcelable creator. - */ - @SuppressWarnings("WeakerAccess") /* synthetic access */ - Session2CommandGroup(Parcel in) { - Parcelable[] commands = in.readParcelableArray(Session2Command.class.getClassLoader()); - if (commands != null) { - for (Parcelable command : commands) { - mCommands.add((Session2Command) command); - } - } - } - - /** - * Checks whether this command group has a command that matches given {@code command}. - * - * @param command A command to find. Shouldn't be {@code null}. - */ - public boolean hasCommand(@NonNull Session2Command command) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - return mCommands.contains(command); - } - - /** - * Checks whether this command group has a command that matches given {@code commandCode}. - * - * @param commandCode A command code to find. - * Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}. - */ - public boolean hasCommand(int commandCode) { - if (commandCode == COMMAND_CODE_CUSTOM) { - throw new IllegalArgumentException("Use hasCommand(Command) for custom command"); - } - for (Session2Command command : mCommands) { - if (command.getCommandCode() == commandCode) { - return true; - } - } - return false; - } - - /** - * Gets all commands of this command group. - */ - @NonNull - public Set<Session2Command> getCommands() { - return new HashSet<>(mCommands); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - if (dest == null) { - throw new IllegalArgumentException("parcel shouldn't be null"); - } - dest.writeParcelableArray(mCommands.toArray(new Session2Command[0]), 0); - } - - /** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Builds a {@link Session2CommandGroup} object. - */ - public static final class Builder { - private Set<Session2Command> mCommands; - - public Builder() { - mCommands = new HashSet<>(); - } - - /** - * Creates a new builder for {@link Session2CommandGroup} with commands copied from another - * {@link Session2CommandGroup} object. - * @param commandGroup - */ - public Builder(@NonNull Session2CommandGroup commandGroup) { - if (commandGroup == null) { - throw new IllegalArgumentException("command group shouldn't be null"); - } - mCommands = commandGroup.getCommands(); - } - - /** - * Adds a command to this command group. - * - * @param command A command to add. Shouldn't be {@code null}. - */ - @NonNull - public Builder addCommand(@NonNull Session2Command command) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - mCommands.add(command); - return this; - } - - /** - * Removes a command from this group which matches given {@code command}. - * - * @param command A command to find. Shouldn't be {@code null}. - */ - @NonNull - public Builder removeCommand(@NonNull Session2Command command) { - if (command == null) { - throw new IllegalArgumentException("command shouldn't be null"); - } - mCommands.remove(command); - return this; - } - - /** - * Builds {@link Session2CommandGroup}. - * - * @return a new {@link Session2CommandGroup}. - */ - @NonNull - public Session2CommandGroup build() { - return new Session2CommandGroup(mCommands); - } - } -} diff --git a/apex/media/framework/java/android/media/Session2Link.java b/apex/media/framework/java/android/media/Session2Link.java deleted file mode 100644 index 6e550e86a9fe..000000000000 --- a/apex/media/framework/java/android/media/Session2Link.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2018 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.media; - -import android.annotation.NonNull; -import android.os.Binder; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.util.Log; - -import java.util.Objects; - -/** - * Handles incoming commands from {@link MediaController2} to {@link MediaSession2}. - * @hide - */ -// @SystemApi -public final class Session2Link implements Parcelable { - private static final String TAG = "Session2Link"; - private static final boolean DEBUG = MediaSession2.DEBUG; - - public static final @android.annotation.NonNull Parcelable.Creator<Session2Link> CREATOR = - new Parcelable.Creator<Session2Link>() { - @Override - public Session2Link createFromParcel(Parcel in) { - return new Session2Link(in); - } - - @Override - public Session2Link[] newArray(int size) { - return new Session2Link[size]; - } - }; - - private final MediaSession2 mSession; - private final IMediaSession2 mISession; - - public Session2Link(MediaSession2 session) { - mSession = session; - mISession = new Session2Stub(); - } - - Session2Link(Parcel in) { - mSession = null; - mISession = IMediaSession2.Stub.asInterface(in.readStrongBinder()); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongBinder(mISession.asBinder()); - } - - @Override - public int hashCode() { - return mISession.asBinder().hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Session2Link)) { - return false; - } - Session2Link other = (Session2Link) obj; - return Objects.equals(mISession.asBinder(), other.mISession.asBinder()); - } - - /** Link to death with mISession */ - public void linkToDeath(@NonNull IBinder.DeathRecipient recipient, int flags) { - if (mISession != null) { - try { - mISession.asBinder().linkToDeath(recipient, flags); - } catch (RemoteException e) { - if (DEBUG) { - Log.d(TAG, "Session died too early.", e); - } - } - } - } - - /** Unlink to death with mISession */ - public boolean unlinkToDeath(@NonNull IBinder.DeathRecipient recipient, int flags) { - if (mISession != null) { - return mISession.asBinder().unlinkToDeath(recipient, flags); - } - return true; - } - - /** Interface method for IMediaSession2.connect */ - public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) { - try { - mISession.connect(caller, seq, connectionRequest); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Interface method for IMediaSession2.disconnect */ - public void disconnect(final Controller2Link caller, int seq) { - try { - mISession.disconnect(caller, seq); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Interface method for IMediaSession2.sendSessionCommand */ - public void sendSessionCommand(final Controller2Link caller, final int seq, - final Session2Command command, final Bundle args, ResultReceiver resultReceiver) { - try { - mISession.sendSessionCommand(caller, seq, command, args, resultReceiver); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Interface method for IMediaSession2.sendSessionCommand */ - public void cancelSessionCommand(final Controller2Link caller, final int seq) { - try { - mISession.cancelSessionCommand(caller, seq); - } catch (RemoteException e) { - throw new RuntimeException(e); - } - } - - /** Stub implementation for IMediaSession2.connect */ - public void onConnect(final Controller2Link caller, int pid, int uid, int seq, - Bundle connectionRequest) { - mSession.onConnect(caller, pid, uid, seq, connectionRequest); - } - - /** Stub implementation for IMediaSession2.disconnect */ - public void onDisconnect(final Controller2Link caller, int seq) { - mSession.onDisconnect(caller, seq); - } - - /** Stub implementation for IMediaSession2.sendSessionCommand */ - public void onSessionCommand(final Controller2Link caller, final int seq, - final Session2Command command, final Bundle args, ResultReceiver resultReceiver) { - mSession.onSessionCommand(caller, seq, command, args, resultReceiver); - } - - /** Stub implementation for IMediaSession2.cancelSessionCommand */ - public void onCancelCommand(final Controller2Link caller, final int seq) { - mSession.onCancelCommand(caller, seq); - } - - private class Session2Stub extends IMediaSession2.Stub { - @Override - public void connect(final Controller2Link caller, int seq, Bundle connectionRequest) { - if (caller == null || connectionRequest == null) { - return; - } - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - Session2Link.this.onConnect(caller, pid, uid, seq, connectionRequest); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void disconnect(final Controller2Link caller, int seq) { - if (caller == null) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - Session2Link.this.onDisconnect(caller, seq); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void sendSessionCommand(final Controller2Link caller, final int seq, - final Session2Command command, final Bundle args, ResultReceiver resultReceiver) { - if (caller == null) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - Session2Link.this.onSessionCommand(caller, seq, command, args, resultReceiver); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void cancelSessionCommand(final Controller2Link caller, final int seq) { - if (caller == null) { - return; - } - final long token = Binder.clearCallingIdentity(); - try { - Session2Link.this.onCancelCommand(caller, seq); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } -} diff --git a/apex/media/framework/java/android/media/Session2Token.java b/apex/media/framework/java/android/media/Session2Token.java deleted file mode 100644 index aae2e1bcb6df..000000000000 --- a/apex/media/framework/java/android/media/Session2Token.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright 2018 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.media; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.List; -import java.util.Objects; - -/** - * This API is not generally intended for third party application developers. - * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> - * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session - * Library</a> for consistent behavior across all devices. - * <p> - * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}. - * If it's representing a session service, it may not be ongoing. - * <p> - * This may be passed to apps by the session owner to allow them to create a - * {@link MediaController2} to communicate with the session. - * <p> - * It can be also obtained by {@link android.media.session.MediaSessionManager}. - */ -public final class Session2Token implements Parcelable { - private static final String TAG = "Session2Token"; - - public static final @android.annotation.NonNull Creator<Session2Token> CREATOR = - new Creator<Session2Token>() { - @Override - public Session2Token createFromParcel(Parcel p) { - return new Session2Token(p); - } - - @Override - public Session2Token[] newArray(int size) { - return new Session2Token[size]; - } - }; - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "TYPE_", value = {TYPE_SESSION, TYPE_SESSION_SERVICE}) - public @interface TokenType { - } - - /** - * Type for {@link MediaSession2}. - */ - public static final int TYPE_SESSION = 0; - - /** - * Type for {@link MediaSession2Service}. - */ - public static final int TYPE_SESSION_SERVICE = 1; - - private final int mUid; - @TokenType - private final int mType; - private final String mPackageName; - private final String mServiceName; - private final Session2Link mSessionLink; - private final ComponentName mComponentName; - private final Bundle mExtras; - - /** - * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}. - * - * @param context The context. - * @param serviceComponent The component name of the service. - */ - public Session2Token(@NonNull Context context, @NonNull ComponentName serviceComponent) { - if (context == null) { - throw new IllegalArgumentException("context shouldn't be null"); - } - if (serviceComponent == null) { - throw new IllegalArgumentException("serviceComponent shouldn't be null"); - } - - final PackageManager manager = context.getPackageManager(); - final int uid = getUid(manager, serviceComponent.getPackageName()); - - if (!isInterfaceDeclared(manager, MediaSession2Service.SERVICE_INTERFACE, - serviceComponent)) { - Log.w(TAG, serviceComponent + " doesn't implement MediaSession2Service."); - } - mComponentName = serviceComponent; - mPackageName = serviceComponent.getPackageName(); - mServiceName = serviceComponent.getClassName(); - mUid = uid; - mType = TYPE_SESSION_SERVICE; - mSessionLink = null; - mExtras = Bundle.EMPTY; - } - - Session2Token(int uid, int type, String packageName, Session2Link sessionLink, - @NonNull Bundle tokenExtras) { - mUid = uid; - mType = type; - mPackageName = packageName; - mServiceName = null; - mComponentName = null; - mSessionLink = sessionLink; - mExtras = tokenExtras; - } - - Session2Token(Parcel in) { - mUid = in.readInt(); - mType = in.readInt(); - mPackageName = in.readString(); - mServiceName = in.readString(); - mSessionLink = in.readParcelable(null); - mComponentName = ComponentName.unflattenFromString(in.readString()); - - Bundle extras = in.readBundle(); - if (extras == null) { - Log.w(TAG, "extras shouldn't be null."); - extras = Bundle.EMPTY; - } else if (MediaSession2.hasCustomParcelable(extras)) { - Log.w(TAG, "extras contain custom parcelable. Ignoring."); - extras = Bundle.EMPTY; - } - mExtras = extras; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mUid); - dest.writeInt(mType); - dest.writeString(mPackageName); - dest.writeString(mServiceName); - dest.writeParcelable(mSessionLink, flags); - dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString()); - dest.writeBundle(mExtras); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public int hashCode() { - return Objects.hash(mType, mUid, mPackageName, mServiceName, mSessionLink); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Session2Token)) { - return false; - } - Session2Token other = (Session2Token) obj; - return mUid == other.mUid - && TextUtils.equals(mPackageName, other.mPackageName) - && TextUtils.equals(mServiceName, other.mServiceName) - && mType == other.mType - && Objects.equals(mSessionLink, other.mSessionLink); - } - - @Override - public String toString() { - return "Session2Token {pkg=" + mPackageName + " type=" + mType - + " service=" + mServiceName + " Session2Link=" + mSessionLink + "}"; - } - - /** - * @return uid of the session - */ - public int getUid() { - return mUid; - } - - /** - * @return package name of the session - */ - @NonNull - public String getPackageName() { - return mPackageName; - } - - /** - * @return service name of the session. Can be {@code null} for {@link #TYPE_SESSION}. - */ - @Nullable - public String getServiceName() { - return mServiceName; - } - - /** - * @return type of the token - * @see #TYPE_SESSION - * @see #TYPE_SESSION_SERVICE - */ - public @TokenType int getType() { - return mType; - } - - /** - * @return extras of the token - * @see MediaSession2.Builder#setExtras(Bundle) - */ - @NonNull - public Bundle getExtras() { - return new Bundle(mExtras); - } - - Session2Link getSessionLink() { - return mSessionLink; - } - - private static boolean isInterfaceDeclared(PackageManager manager, String serviceInterface, - ComponentName serviceComponent) { - Intent serviceIntent = new Intent(serviceInterface); - // Use queryIntentServices to find services with MediaSession2Service.SERVICE_INTERFACE. - // We cannot use resolveService with intent specified class name, because resolveService - // ignores actions if Intent.setClassName() is specified. - serviceIntent.setPackage(serviceComponent.getPackageName()); - - List<ResolveInfo> list = manager.queryIntentServices( - serviceIntent, PackageManager.GET_META_DATA); - if (list != null) { - for (int i = 0; i < list.size(); i++) { - ResolveInfo resolveInfo = list.get(i); - if (resolveInfo == null || resolveInfo.serviceInfo == null) { - continue; - } - if (TextUtils.equals( - resolveInfo.serviceInfo.name, serviceComponent.getClassName())) { - return true; - } - } - } - return false; - } - - private static int getUid(PackageManager manager, String packageName) { - try { - return manager.getApplicationInfo(packageName, 0).uid; - } catch (PackageManager.NameNotFoundException e) { - throw new IllegalArgumentException("Cannot find package " + packageName); - } - } -} diff --git a/apex/media/framework/jni/android_media_MediaParserJNI.cpp b/apex/media/framework/jni/android_media_MediaParserJNI.cpp deleted file mode 100644 index c81152c0954c..000000000000 --- a/apex/media/framework/jni/android_media_MediaParserJNI.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 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. - */ - -#include <jni.h> -#include <media/MediaMetrics.h> - -#define JNI_FUNCTION(RETURN_TYPE, NAME, ...) \ - extern "C" { \ - JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \ - ##__VA_ARGS__); \ - } \ - JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \ - ##__VA_ARGS__) - -namespace { - -constexpr char kMediaMetricsKey[] = "mediaparser"; - -constexpr char kAttributeLogSessionId[] = "android.media.mediaparser.logSessionId"; -constexpr char kAttributeParserName[] = "android.media.mediaparser.parserName"; -constexpr char kAttributeCreatedByName[] = "android.media.mediaparser.createdByName"; -constexpr char kAttributeParserPool[] = "android.media.mediaparser.parserPool"; -constexpr char kAttributeLastException[] = "android.media.mediaparser.lastException"; -constexpr char kAttributeResourceByteCount[] = "android.media.mediaparser.resourceByteCount"; -constexpr char kAttributeDurationMillis[] = "android.media.mediaparser.durationMillis"; -constexpr char kAttributeTrackMimeTypes[] = "android.media.mediaparser.trackMimeTypes"; -constexpr char kAttributeTrackCodecs[] = "android.media.mediaparser.trackCodecs"; -constexpr char kAttributeAlteredParameters[] = "android.media.mediaparser.alteredParameters"; -constexpr char kAttributeVideoWidth[] = "android.media.mediaparser.videoWidth"; -constexpr char kAttributeVideoHeight[] = "android.media.mediaparser.videoHeight"; - -// Util class to handle string resource management. -class JstringHandle { -public: - JstringHandle(JNIEnv* env, jstring value) : mEnv(env), mJstringValue(value) { - mCstringValue = env->GetStringUTFChars(value, /* isCopy= */ nullptr); - } - - ~JstringHandle() { - if (mCstringValue != nullptr) { - mEnv->ReleaseStringUTFChars(mJstringValue, mCstringValue); - } - } - - [[nodiscard]] const char* value() const { - return mCstringValue != nullptr ? mCstringValue : ""; - } - - JNIEnv* mEnv; - jstring mJstringValue; - const char* mCstringValue; -}; - -} // namespace - -JNI_FUNCTION(void, nativeSubmitMetrics, jstring logSessionIdJstring, jstring parserNameJstring, - jboolean createdByName, jstring parserPoolJstring, jstring lastExceptionJstring, - jlong resourceByteCount, jlong durationMillis, jstring trackMimeTypesJstring, - jstring trackCodecsJstring, jstring alteredParameters, jint videoWidth, - jint videoHeight) { - mediametrics_handle_t item(mediametrics_create(kMediaMetricsKey)); - mediametrics_setCString(item, kAttributeLogSessionId, - JstringHandle(env, logSessionIdJstring).value()); - mediametrics_setCString(item, kAttributeParserName, - JstringHandle(env, parserNameJstring).value()); - mediametrics_setInt32(item, kAttributeCreatedByName, createdByName ? 1 : 0); - mediametrics_setCString(item, kAttributeParserPool, - JstringHandle(env, parserPoolJstring).value()); - mediametrics_setCString(item, kAttributeLastException, - JstringHandle(env, lastExceptionJstring).value()); - mediametrics_setInt64(item, kAttributeResourceByteCount, resourceByteCount); - mediametrics_setInt64(item, kAttributeDurationMillis, durationMillis); - mediametrics_setCString(item, kAttributeTrackMimeTypes, - JstringHandle(env, trackMimeTypesJstring).value()); - mediametrics_setCString(item, kAttributeTrackCodecs, - JstringHandle(env, trackCodecsJstring).value()); - mediametrics_setCString(item, kAttributeAlteredParameters, - JstringHandle(env, alteredParameters).value()); - mediametrics_setInt32(item, kAttributeVideoWidth, videoWidth); - mediametrics_setInt32(item, kAttributeVideoHeight, videoHeight); - mediametrics_selfRecord(item); - mediametrics_delete(item); -} diff --git a/apex/media/framework/lint-baseline.xml b/apex/media/framework/lint-baseline.xml deleted file mode 100644 index e1b145083f80..000000000000 --- a/apex/media/framework/lint-baseline.xml +++ /dev/null @@ -1,312 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 29): `new android.media.ApplicationMediaCapabilities.Builder`" - errorLine1=" new ApplicationMediaCapabilities.Builder();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java" - line="208" - column="29"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level 31 (current min is 29): `new android.media.ApplicationMediaCapabilities.Builder`" - errorLine1=" ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java" - line="314" - column="56"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 29): `android.os.RemoteException#rethrowFromSystemServer`" - errorLine1=" e.rethrowFromSystemServer();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaCommunicationManager.java" - line="110" - column="15"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 29): `android.os.Parcel#writeParcelableCreator`" - errorLine1=" dest.writeParcelableCreator((Parcelable) parcelable);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParceledListSlice.java" - line="77" - column="14"/> - </issue> - - <issue - id="NewApi" - message="Call requires API level R (current min is 29): `android.os.Parcel#readParcelableCreator`" - errorLine1=" return from.readParcelableCreator(loader);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParceledListSlice.java" - line="82" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.TrackData#mediaFormat`" - errorLine1=" this.mediaFormat = mediaFormat;" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="273" - column="13"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.TrackData#drmInitData`" - errorLine1=" this.drmInitData = drmInitData;" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="274" - column="13"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`" - errorLine1=" this.timeMicros = timeMicros;" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="295" - column="13"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`" - errorLine1=" this.position = position;" - errorLine2=" ~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="296" - column="13"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`" - errorLine1=" return "[timeMicros=" + timeMicros + ", position=" + position + "]";" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="302" - column="66"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`" - errorLine1=" return "[timeMicros=" + timeMicros + ", position=" + position + "]";" - errorLine2=" ~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="302" - column="37"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level R (current min is 29): `android.media.MediaParser.SeekPoint`" - errorLine1=" SeekPoint other = (SeekPoint) obj;" - errorLine2=" ~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="313" - column="32"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`" - errorLine1=" return timeMicros == other.timeMicros && position == other.position;" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="314" - column="54"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`" - errorLine1=" return timeMicros == other.timeMicros && position == other.position;" - errorLine2=" ~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="314" - column="66"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`" - errorLine1=" return timeMicros == other.timeMicros && position == other.position;" - errorLine2=" ~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="314" - column="20"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`" - errorLine1=" return timeMicros == other.timeMicros && position == other.position;" - errorLine2=" ~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="314" - column="34"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`" - errorLine1=" int result = (int) timeMicros;" - errorLine2=" ~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="319" - column="32"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`" - errorLine1=" result = 31 * result + (int) position;" - errorLine2=" ~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="320" - column="42"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level R (current min is 29): `android.media.MediaParser.InputReader`" - errorLine1=" public interface SeekableInputReader extends InputReader {" - errorLine2=" ~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="352" - column="50"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level 31 (current min is 29): `android.media.metrics.LogSessionId#LOG_SESSION_ID_NONE`" - errorLine1=" @NonNull private LogSessionId mLogSessionId = LogSessionId.LOG_SESSION_ID_NONE;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1071" - column="51"/> - </issue> - - <issue - id="NewApi" - message="Cast from `SeekableInputReader` to `InputReader` requires API level 30 (current min is 29)" - errorLine1=" mExoDataReader.mInputReader = seekableInputReader;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1201" - column="39"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`" - errorLine1=" mPendingSeekPosition = seekPoint.position;" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1287" - column="36"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`" - errorLine1=" mPendingSeekTimeMicros = seekPoint.timeMicros;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1288" - column="38"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#position`" - errorLine1=" mExtractor.seek(seekPoint.position, seekPoint.timeMicros);" - errorLine2=" ~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1290" - column="29"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.MediaParser.SeekPoint#timeMicros`" - errorLine1=" mExtractor.seek(seekPoint.position, seekPoint.timeMicros);" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1290" - column="49"/> - </issue> - - <issue - id="NewApi" - message="Field requires API level R (current min is 29): `android.media.DrmInitData.SchemeInitData#uuid`" - errorLine1=" if (schemeInitData.uuid.equals(schemeUuid)) {" - errorLine2=" ~~~~~~~~~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1579" - column="21"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level R (current min is 29): `android.media.MediaParser.InputReader`" - errorLine1=" private static final class DataReaderAdapter implements InputReader {" - errorLine2=" ~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1872" - column="61"/> - </issue> - - <issue - id="NewApi" - message="Class requires API level R (current min is 29): `android.media.MediaParser.InputReader`" - errorLine1=" private static final class ParsableByteArrayAdapter implements InputReader {" - errorLine2=" ~~~~~~~~~~~"> - <location - file="frameworks/base/apex/media/framework/java/android/media/MediaParser.java" - line="1905" - column="68"/> - </issue> - -</issues> diff --git a/apex/media/framework/updatable-media-proguard.flags b/apex/media/framework/updatable-media-proguard.flags deleted file mode 100644 index 4e7d8422bf44..000000000000 --- a/apex/media/framework/updatable-media-proguard.flags +++ /dev/null @@ -1,2 +0,0 @@ -# Keep all symbols in android.media. --keep class android.media.* {*;} diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp deleted file mode 100644 index 271fc5312f8f..000000000000 --- a/apex/media/service/Android.bp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -filegroup { - name: "service-media-s-sources", - srcs: [ - "java/**/*.java", - ], - path: "java", - visibility: ["//visibility:private"], -} - -java_sdk_library { - name: "service-media-s", - permitted_packages: [ - "com.android.server.media", - ], - defaults: ["framework-system-server-module-defaults"], - srcs: [ - ":service-media-s-sources", - ], - libs: [ - "updatable-media", - ], - sdk_version: "system_server_current", - min_sdk_version: "29", // TODO: We may need to bump this at some point. - apex_available: [ - "com.android.media", - ], -} diff --git a/apex/media/service/api/current.txt b/apex/media/service/api/current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/media/service/api/current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/media/service/api/removed.txt b/apex/media/service/api/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/media/service/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/media/service/api/system-server-current.txt b/apex/media/service/api/system-server-current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/media/service/api/system-server-current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/media/service/api/system-server-removed.txt b/apex/media/service/api/system-server-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/media/service/api/system-server-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java deleted file mode 100644 index ed31aa3d2a39..000000000000 --- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java +++ /dev/null @@ -1,620 +0,0 @@ -/* - * Copyright 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 com.android.server.media; - -import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.os.UserHandle.ALL; -import static android.os.UserHandle.getUserHandleForUid; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.NotificationManager; -import android.content.Context; -import android.content.pm.PackageManager; -import android.media.IMediaCommunicationService; -import android.media.IMediaCommunicationServiceCallback; -import android.media.MediaController2; -import android.media.MediaParceledListSlice; -import android.media.Session2CommandGroup; -import android.media.Session2Token; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Process; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; -import android.util.SparseArray; -import android.util.SparseIntArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.SystemService; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; - -/** - * A system service that manages {@link android.media.MediaSession2} creations - * and their ongoing media playback state. - * @hide - */ -public class MediaCommunicationService extends SystemService { - private static final String TAG = "MediaCommunicationService"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - final Context mContext; - - final Object mLock = new Object(); - final Handler mHandler = new Handler(Looper.getMainLooper()); - - @GuardedBy("mLock") - private final SparseIntArray mFullUserIds = new SparseIntArray(); - @GuardedBy("mLock") - private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>(); - - final Executor mRecordExecutor = Executors.newSingleThreadExecutor(); - @GuardedBy("mLock") - final List<CallbackRecord> mCallbackRecords = new ArrayList<>(); - final NotificationManager mNotificationManager; - - public MediaCommunicationService(Context context) { - super(context); - mContext = context; - mNotificationManager = context.getSystemService(NotificationManager.class); - } - - @Override - public void onStart() { - publishBinderService(Context.MEDIA_COMMUNICATION_SERVICE, new Stub()); - updateUser(); - } - - @Override - public void onUserStarting(@NonNull TargetUser user) { - if (DEBUG) Log.d(TAG, "onUserStarting: " + user); - updateUser(); - } - - @Override - public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { - if (DEBUG) Log.d(TAG, "onUserSwitching: " + to); - updateUser(); - } - - @Override - public void onUserStopped(@NonNull TargetUser targetUser) { - int userId = targetUser.getUserHandle().getIdentifier(); - - if (DEBUG) Log.d(TAG, "onUserStopped: " + userId); - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(userId); - if (user != null) { - if (user.getFullUserId() == userId) { - user.destroyAllSessions(); - mUserRecords.remove(userId); - } else { - user.destroySessionsForUser(userId); - } - } - } - updateUser(); - } - - @Nullable - CallbackRecord findCallbackRecordLocked(@Nullable IMediaCommunicationServiceCallback callback) { - if (callback == null) { - return null; - } - for (CallbackRecord record : mCallbackRecords) { - if (Objects.equals(callback.asBinder(), record.mCallback.asBinder())) { - return record; - } - } - return null; - } - - List<Session2Token> getSession2TokensLocked(int userId) { - List<Session2Token> list = new ArrayList<>(); - if (userId == ALL.getIdentifier()) { - int size = mUserRecords.size(); - for (int i = 0; i < size; i++) { - list.addAll(mUserRecords.valueAt(i).getAllSession2Tokens()); - } - } else { - FullUserRecord user = getFullUserRecordLocked(userId); - if (user != null) { - list.addAll(user.getSession2Tokens(userId)); - } - } - return list; - } - - private FullUserRecord getFullUserRecordLocked(int userId) { - int fullUserId = mFullUserIds.get(userId, -1); - if (fullUserId < 0) { - return null; - } - return mUserRecords.get(fullUserId); - } - - private boolean hasMediaControlPermission(int pid, int uid) { - // Check if it's system server or has MEDIA_CONTENT_CONTROL. - // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra - // check here. - if (uid == Process.SYSTEM_UID || mContext.checkPermission( - android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) - == PackageManager.PERMISSION_GRANTED) { - return true; - } else if (DEBUG) { - Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); - } - return false; - } - - private void updateUser() { - UserManager manager = mContext.getSystemService(UserManager.class); - List<UserHandle> allUsers = manager.getUserHandles(/*excludeDying=*/false); - - synchronized (mLock) { - mFullUserIds.clear(); - if (allUsers != null) { - for (UserHandle user : allUsers) { - UserHandle parent = manager.getProfileParent(user); - if (parent != null) { - mFullUserIds.put(user.getIdentifier(), parent.getIdentifier()); - } else { - mFullUserIds.put(user.getIdentifier(), user.getIdentifier()); - if (mUserRecords.get(user.getIdentifier()) == null) { - mUserRecords.put(user.getIdentifier(), - new FullUserRecord(user.getIdentifier())); - } - } - } - } - // Ensure that the current full user exists. - int currentFullUserId = ActivityManager.getCurrentUser(); - FullUserRecord currentFullUserRecord = mUserRecords.get(currentFullUserId); - if (currentFullUserRecord == null) { - Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); - currentFullUserRecord = new FullUserRecord(currentFullUserId); - mUserRecords.put(currentFullUserId, currentFullUserRecord); - } - mFullUserIds.put(currentFullUserId, currentFullUserId); - } - } - - void dispatchSession2Created(Session2Token token) { - synchronized (mLock) { - for (CallbackRecord record : mCallbackRecords) { - if (record.mUserId != ALL.getIdentifier() - && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) { - continue; - } - try { - record.mCallback.onSession2Created(token); - } catch (RemoteException e) { - Log.w(TAG, "Failed to notify session2 token created " + record); - } - } - } - } - - void dispatchSession2Changed(int userId) { - MediaParceledListSlice<Session2Token> allSession2Tokens; - MediaParceledListSlice<Session2Token> userSession2Tokens; - - synchronized (mLock) { - allSession2Tokens = - new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier())); - userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId)); - } - allSession2Tokens.setInlineCountLimit(1); - userSession2Tokens.setInlineCountLimit(1); - - synchronized (mLock) { - for (CallbackRecord record : mCallbackRecords) { - if (record.mUserId == ALL.getIdentifier()) { - try { - record.mCallback.onSession2Changed(allSession2Tokens); - } catch (RemoteException e) { - Log.w(TAG, "Failed to notify session2 tokens changed " + record); - } - } else if (record.mUserId == userId) { - try { - record.mCallback.onSession2Changed(userSession2Tokens); - } catch (RemoteException e) { - Log.w(TAG, "Failed to notify session2 tokens changed " + record); - } - } - } - } - } - - void onSessionDied(Session2Record session) { - if (DEBUG) { - Log.d(TAG, "Destroying " + session); - } - if (session.isClosed()) { - Log.w(TAG, "Destroying already destroyed session. Ignoring."); - return; - } - - FullUserRecord user = session.getFullUser(); - if (user != null) { - user.removeSession(session); - } - session.close(); - } - - private class Stub extends IMediaCommunicationService.Stub { - @Override - public void notifySession2Created(Session2Token sessionToken) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - try { - if (DEBUG) { - Log.d(TAG, "Session2 is created " + sessionToken); - } - if (uid != sessionToken.getUid()) { - throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid - + " but actually=" + sessionToken.getUid()); - } - FullUserRecord user; - int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier(); - synchronized (mLock) { - user = getFullUserRecordLocked(userId); - } - if (user == null) { - Log.w(TAG, "notifySession2Created: Ignore session of an unknown user"); - return; - } - user.addSession(new Session2Record(MediaCommunicationService.this, - user, sessionToken, mRecordExecutor)); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL - * permission or an enabled notification listener) - * - * @param controllerPackageName package name of the controller app - * @param controllerPid pid of the controller app - * @param controllerUid uid of the controller app - */ - @Override - public boolean isTrusted(String controllerPackageName, int controllerPid, - int controllerUid) { - final int uid = Binder.getCallingUid(); - final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); - final long token = Binder.clearCallingIdentity(); - try { - // Don't perform check between controllerPackageName and controllerUid. - // When an (activity|service) runs on the another apps process by specifying - // android:process in the AndroidManifest.xml, then PID and UID would have the - // running process' information instead of the (activity|service) that has created - // MediaController. - // Note that we can use Context#getOpPackageName() instead of - // Context#getPackageName() for getting package name that matches with the PID/UID, - // but it doesn't tell which package has created the MediaController, so useless. - return hasMediaControlPermission(controllerPid, controllerUid) - || hasEnabledNotificationListener( - userId, controllerPackageName, controllerUid); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public MediaParceledListSlice getSession2Tokens(int userId) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - try { - // Check that they can make calls on behalf of the user and get the final user id - int resolvedUserId = handleIncomingUser(pid, uid, userId, null); - List<Session2Token> result; - synchronized (mLock) { - result = getSession2TokensLocked(resolvedUserId); - } - MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result); - parceledListSlice.setInlineCountLimit(1); - return parceledListSlice; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void registerCallback(IMediaCommunicationServiceCallback callback, - String packageName) throws RemoteException { - Objects.requireNonNull(callback, "callback should not be null"); - Objects.requireNonNull(packageName, "packageName should not be null"); - - synchronized (mLock) { - if (findCallbackRecordLocked(callback) == null) { - - CallbackRecord record = new CallbackRecord(callback, packageName, - Binder.getCallingUid(), Binder.getCallingPid()); - mCallbackRecords.add(record); - try { - callback.asBinder().linkToDeath(record, 0); - } catch (RemoteException e) { - Log.w(TAG, "Failed to register callback", e); - mCallbackRecords.remove(record); - } - } else { - Log.e(TAG, "registerCallback is called with already registered callback. " - + "packageName=" + packageName); - } - } - } - - @Override - public void unregisterCallback(IMediaCommunicationServiceCallback callback) - throws RemoteException { - synchronized (mLock) { - CallbackRecord existingRecord = findCallbackRecordLocked(callback); - if (existingRecord != null) { - mCallbackRecords.remove(existingRecord); - callback.asBinder().unlinkToDeath(existingRecord, 0); - } else { - Log.e(TAG, "unregisterCallback is called with unregistered callback."); - } - } - } - - private boolean hasEnabledNotificationListener(int callingUserId, - String controllerPackageName, int controllerUid) { - int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier(); - if (callingUserId != controllerUserId) { - // Enabled notification listener only works within the same user. - return false; - } - - if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName, - UserHandle.getUserHandleForUid(controllerUid))) { - return true; - } - if (DEBUG) { - Log.d(TAG, controllerPackageName + " (uid=" + controllerUid - + ") doesn't have an enabled notification listener"); - } - return false; - } - - // Handles incoming user by checking whether the caller has permission to access the - // given user id's information or not. Permission is not necessary if the given user id is - // equal to the caller's user id, but if not, the caller needs to have the - // INTERACT_ACROSS_USERS_FULL permission. Otherwise, a security exception will be thrown. - // The return value will be the given user id, unless the given user id is - // UserHandle.CURRENT, which will return the ActivityManager.getCurrentUser() value instead. - private int handleIncomingUser(int pid, int uid, int userId, String packageName) { - int callingUserId = UserHandle.getUserHandleForUid(uid).getIdentifier(); - if (userId == callingUserId) { - return userId; - } - - boolean canInteractAcrossUsersFull = mContext.checkPermission( - INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED; - if (canInteractAcrossUsersFull) { - if (userId == UserHandle.CURRENT.getIdentifier()) { - return ActivityManager.getCurrentUser(); - } - return userId; - } - - throw new SecurityException("Permission denied while calling from " + packageName - + " with user id: " + userId + "; Need to run as either the calling user id (" - + callingUserId + "), or with " + INTERACT_ACROSS_USERS_FULL + " permission"); - } - } - - final class CallbackRecord implements IBinder.DeathRecipient { - private final IMediaCommunicationServiceCallback mCallback; - private final String mPackageName; - private final int mUid; - private int mPid; - private final int mUserId; - - CallbackRecord(IMediaCommunicationServiceCallback callback, - String packageName, int uid, int pid) { - mCallback = callback; - mPackageName = packageName; - mUid = uid; - mPid = pid; - mUserId = (mContext.checkPermission( - INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED) - ? ALL.getIdentifier() : UserHandle.getUserHandleForUid(mUid).getIdentifier(); - } - - @Override - public String toString() { - return "CallbackRecord[callback=" + mCallback + ", pkg=" + mPackageName - + ", uid=" + mUid + ", pid=" + mPid + "]"; - } - - @Override - public void binderDied() { - synchronized (mLock) { - mCallbackRecords.remove(this); - } - } - } - - final class FullUserRecord { - private final int mFullUserId; - private final Object mUserLock = new Object(); - @GuardedBy("mUserLock") - private final List<Session2Record> mSessionRecords = new ArrayList<>(); - - FullUserRecord(int fullUserId) { - mFullUserId = fullUserId; - } - - public void addSession(Session2Record record) { - synchronized (mUserLock) { - mSessionRecords.add(record); - } - mHandler.post(() -> dispatchSession2Created(record.mSessionToken)); - mHandler.post(() -> dispatchSession2Changed(mFullUserId)); - } - - private void removeSession(Session2Record record) { - synchronized (mUserLock) { - mSessionRecords.remove(record); - } - mHandler.post(() -> dispatchSession2Changed(mFullUserId)); - //TODO: Handle if the removed session was the media button session. - } - - public int getFullUserId() { - return mFullUserId; - } - - public List<Session2Token> getAllSession2Tokens() { - synchronized (mUserLock) { - return mSessionRecords.stream() - .map(Session2Record::getSessionToken) - .collect(Collectors.toList()); - } - } - - public List<Session2Token> getSession2Tokens(int userId) { - synchronized (mUserLock) { - return mSessionRecords.stream() - .filter(record -> record.getUserId() == userId) - .map(Session2Record::getSessionToken) - .collect(Collectors.toList()); - } - } - - public void destroyAllSessions() { - synchronized (mUserLock) { - for (Session2Record session : mSessionRecords) { - session.close(); - } - mSessionRecords.clear(); - } - mHandler.post(() -> dispatchSession2Changed(mFullUserId)); - } - - public void destroySessionsForUser(int userId) { - boolean changed = false; - synchronized (mUserLock) { - for (int i = mSessionRecords.size() - 1; i >= 0; i--) { - Session2Record session = mSessionRecords.get(i); - if (session.getUserId() == userId) { - mSessionRecords.remove(i); - session.close(); - changed = true; - } - } - } - if (changed) { - mHandler.post(() -> dispatchSession2Changed(mFullUserId)); - } - } - } - - static final class Session2Record { - final Session2Token mSessionToken; - final Object mSession2RecordLock = new Object(); - final WeakReference<MediaCommunicationService> mServiceRef; - final WeakReference<FullUserRecord> mFullUserRef; - @GuardedBy("mSession2RecordLock") - private final MediaController2 mController; - - @GuardedBy("mSession2RecordLock") - boolean mIsConnected; - @GuardedBy("mSession2RecordLock") - private boolean mIsClosed; - - Session2Record(MediaCommunicationService service, FullUserRecord fullUser, - Session2Token token, Executor controllerExecutor) { - mServiceRef = new WeakReference<>(service); - mFullUserRef = new WeakReference<>(fullUser); - mSessionToken = token; - mController = new MediaController2.Builder(service.getContext(), token) - .setControllerCallback(controllerExecutor, new Controller2Callback()) - .build(); - } - - public int getUserId() { - return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier(); - } - - public FullUserRecord getFullUser() { - return mFullUserRef.get(); - } - - public boolean isClosed() { - synchronized (mSession2RecordLock) { - return mIsClosed; - } - } - - public void close() { - synchronized (mSession2RecordLock) { - mIsClosed = true; - mController.close(); - } - } - - public Session2Token getSessionToken() { - return mSessionToken; - } - - private class Controller2Callback extends MediaController2.ControllerCallback { - @Override - public void onConnected(MediaController2 controller, - Session2CommandGroup allowedCommands) { - if (DEBUG) { - Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands); - } - synchronized (mSession2RecordLock) { - mIsConnected = true; - } - } - - @Override - public void onDisconnected(MediaController2 controller) { - if (DEBUG) { - Log.d(TAG, "disconnected from " + mSessionToken); - } - synchronized (mSession2RecordLock) { - mIsConnected = false; - } - MediaCommunicationService service = mServiceRef.get(); - if (service != null) { - service.onSessionDied(Session2Record.this); - } - } - } - } -} diff --git a/api/Android.bp b/api/Android.bp index 15356bdc0797..69d602a34380 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -112,7 +112,7 @@ combined_apis { "framework-appsearch", "framework-bluetooth", "framework-connectivity", - "framework-connectivity-tiramisu", + "framework-connectivity-t", "framework-graphics", "framework-media", "framework-mediaprovider", @@ -178,8 +178,10 @@ genrule { cmd: metalava_cmd + "--check-compatibility:api:released $(location :android.api.module-lib.latest) " + // Note: having "public" be the base of module-lib is not perfect -- it should - // ideally be a merged public+system), but this will help when migrating from - // MODULE_LIBS -> public. + // ideally be a merged public+system (which metalava is not currently able to generate). + // This "base" will help when migrating from MODULE_LIBS -> public, but not when + // migrating from MODULE_LIBS -> system (where it needs to instead be listed as + // an incompatibility). "--check-compatibility:base $(location :frameworks-base-api-current.txt) " + "--baseline:compatibility:released $(location :android-incompatibilities.api.module-lib.latest) " + "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " + diff --git a/boot/hiddenapi/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt index 50e0a1b45c9d..e346ebf827ae 100644 --- a/boot/hiddenapi/hiddenapi-max-target-o.txt +++ b/boot/hiddenapi/hiddenapi-max-target-o.txt @@ -9315,78 +9315,6 @@ Landroid/app/usage/IUsageStatsManager;->setAppStandbyBucket(Ljava/lang/String;II Landroid/app/usage/IUsageStatsManager;->setAppStandbyBuckets(Landroid/content/pm/ParceledListSlice;I)V Landroid/app/usage/IUsageStatsManager;->unregisterAppUsageObserver(ILjava/lang/String;)V Landroid/app/usage/IUsageStatsManager;->whitelistAppTemporarily(Ljava/lang/String;JI)V -Landroid/app/usage/NetworkStats$Bucket;->convertDefaultNetworkStatus(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertMetered(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertRoaming(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertSet(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertState(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertTag(I)I -Landroid/app/usage/NetworkStats$Bucket;->convertUid(I)I -Landroid/app/usage/NetworkStats$Bucket;->mBeginTimeStamp:J -Landroid/app/usage/NetworkStats$Bucket;->mDefaultNetworkStatus:I -Landroid/app/usage/NetworkStats$Bucket;->mEndTimeStamp:J -Landroid/app/usage/NetworkStats$Bucket;->mMetered:I -Landroid/app/usage/NetworkStats$Bucket;->mRoaming:I -Landroid/app/usage/NetworkStats$Bucket;->mRxBytes:J -Landroid/app/usage/NetworkStats$Bucket;->mRxPackets:J -Landroid/app/usage/NetworkStats$Bucket;->mState:I -Landroid/app/usage/NetworkStats$Bucket;->mTag:I -Landroid/app/usage/NetworkStats$Bucket;->mTxBytes:J -Landroid/app/usage/NetworkStats$Bucket;->mTxPackets:J -Landroid/app/usage/NetworkStats$Bucket;->mUid:I -Landroid/app/usage/NetworkStats;-><init>(Landroid/content/Context;Landroid/net/NetworkTemplate;IJJLandroid/net/INetworkStatsService;)V -Landroid/app/usage/NetworkStats;->fillBucketFromSummaryEntry(Landroid/app/usage/NetworkStats$Bucket;)V -Landroid/app/usage/NetworkStats;->getDeviceSummaryForNetwork()Landroid/app/usage/NetworkStats$Bucket; -Landroid/app/usage/NetworkStats;->getNextHistoryBucket(Landroid/app/usage/NetworkStats$Bucket;)Z -Landroid/app/usage/NetworkStats;->getNextSummaryBucket(Landroid/app/usage/NetworkStats$Bucket;)Z -Landroid/app/usage/NetworkStats;->getSummaryAggregate()Landroid/app/usage/NetworkStats$Bucket; -Landroid/app/usage/NetworkStats;->getUid()I -Landroid/app/usage/NetworkStats;->hasNextUid()Z -Landroid/app/usage/NetworkStats;->isUidEnumeration()Z -Landroid/app/usage/NetworkStats;->mCloseGuard:Ldalvik/system/CloseGuard; -Landroid/app/usage/NetworkStats;->mEndTimeStamp:J -Landroid/app/usage/NetworkStats;->mEnumerationIndex:I -Landroid/app/usage/NetworkStats;->mHistory:Landroid/net/NetworkStatsHistory; -Landroid/app/usage/NetworkStats;->mRecycledHistoryEntry:Landroid/net/NetworkStatsHistory$Entry; -Landroid/app/usage/NetworkStats;->mRecycledSummaryEntry:Landroid/net/NetworkStats$Entry; -Landroid/app/usage/NetworkStats;->mSession:Landroid/net/INetworkStatsSession; -Landroid/app/usage/NetworkStats;->mStartTimeStamp:J -Landroid/app/usage/NetworkStats;->mState:I -Landroid/app/usage/NetworkStats;->mSummary:Landroid/net/NetworkStats; -Landroid/app/usage/NetworkStats;->mTag:I -Landroid/app/usage/NetworkStats;->mTemplate:Landroid/net/NetworkTemplate; -Landroid/app/usage/NetworkStats;->mUidOrUidIndex:I -Landroid/app/usage/NetworkStats;->mUids:[I -Landroid/app/usage/NetworkStats;->setSingleUidTagState(III)V -Landroid/app/usage/NetworkStats;->startHistoryEnumeration(III)V -Landroid/app/usage/NetworkStats;->startSummaryEnumeration()V -Landroid/app/usage/NetworkStats;->startUserUidEnumeration()V -Landroid/app/usage/NetworkStats;->stepHistory()V -Landroid/app/usage/NetworkStats;->stepUid()V -Landroid/app/usage/NetworkStats;->TAG:Ljava/lang/String; -Landroid/app/usage/NetworkStatsManager$CallbackHandler;-><init>(Landroid/os/Looper;ILjava/lang/String;Landroid/app/usage/NetworkStatsManager$UsageCallback;)V -Landroid/app/usage/NetworkStatsManager$CallbackHandler;->getObject(Landroid/os/Message;Ljava/lang/String;)Ljava/lang/Object; -Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mCallback:Landroid/app/usage/NetworkStatsManager$UsageCallback; -Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mNetworkType:I -Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mSubscriberId:Ljava/lang/String; -Landroid/app/usage/NetworkStatsManager$UsageCallback;->request:Landroid/net/DataUsageRequest; -Landroid/app/usage/NetworkStatsManager;-><init>(Landroid/content/Context;Landroid/net/INetworkStatsService;)V -Landroid/app/usage/NetworkStatsManager;->CALLBACK_LIMIT_REACHED:I -Landroid/app/usage/NetworkStatsManager;->CALLBACK_RELEASED:I -Landroid/app/usage/NetworkStatsManager;->createTemplate(ILjava/lang/String;)Landroid/net/NetworkTemplate; -Landroid/app/usage/NetworkStatsManager;->DBG:Z -Landroid/app/usage/NetworkStatsManager;->FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN:I -Landroid/app/usage/NetworkStatsManager;->FLAG_POLL_FORCE:I -Landroid/app/usage/NetworkStatsManager;->FLAG_POLL_ON_OPEN:I -Landroid/app/usage/NetworkStatsManager;->mContext:Landroid/content/Context; -Landroid/app/usage/NetworkStatsManager;->mFlags:I -Landroid/app/usage/NetworkStatsManager;->MIN_THRESHOLD_BYTES:J -Landroid/app/usage/NetworkStatsManager;->mService:Landroid/net/INetworkStatsService; -Landroid/app/usage/NetworkStatsManager;->querySummaryForDevice(Landroid/net/NetworkTemplate;JJ)Landroid/app/usage/NetworkStats$Bucket; -Landroid/app/usage/NetworkStatsManager;->registerUsageCallback(Landroid/net/NetworkTemplate;IJLandroid/app/usage/NetworkStatsManager$UsageCallback;Landroid/os/Handler;)V -Landroid/app/usage/NetworkStatsManager;->setAugmentWithSubscriptionPlan(Z)V -Landroid/app/usage/NetworkStatsManager;->setPollOnOpen(Z)V -Landroid/app/usage/NetworkStatsManager;->TAG:Ljava/lang/String; Landroid/app/usage/StorageStats;-><init>()V Landroid/app/usage/StorageStats;-><init>(Landroid/os/Parcel;)V Landroid/app/usage/StorageStats;->cacheBytes:J @@ -35338,13 +35266,6 @@ Landroid/net/ConnectivityMetricsEvent;->transports:J Landroid/net/Credentials;->gid:I Landroid/net/Credentials;->pid:I Landroid/net/Credentials;->uid:I -Landroid/net/DataUsageRequest;-><init>(ILandroid/net/NetworkTemplate;J)V -Landroid/net/DataUsageRequest;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/DataUsageRequest;->PARCELABLE_KEY:Ljava/lang/String; -Landroid/net/DataUsageRequest;->requestId:I -Landroid/net/DataUsageRequest;->REQUEST_ID_UNSET:I -Landroid/net/DataUsageRequest;->template:Landroid/net/NetworkTemplate; -Landroid/net/DataUsageRequest;->thresholdInBytes:J Landroid/net/DhcpResults;->addDns(Ljava/lang/String;)Z Landroid/net/DhcpResults;->clear()V Landroid/net/DhcpResults;->CREATOR:Landroid/os/Parcelable$Creator; @@ -35793,68 +35714,6 @@ Landroid/net/INetworkScoreService;->requestScores([Landroid/net/NetworkKey;)Z Landroid/net/INetworkScoreService;->setActiveScorer(Ljava/lang/String;)Z Landroid/net/INetworkScoreService;->unregisterNetworkScoreCache(ILandroid/net/INetworkScoreCache;)V Landroid/net/INetworkScoreService;->updateScores([Landroid/net/ScoredNetwork;)Z -Landroid/net/INetworkStatsService$Stub$Proxy;->forceUpdate()V -Landroid/net/INetworkStatsService$Stub$Proxy;->forceUpdateIfaces([Landroid/net/Network;)V -Landroid/net/INetworkStatsService$Stub$Proxy;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsService$Stub$Proxy;->getDetailedUidStats([Ljava/lang/String;)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsService$Stub$Proxy;->getIfaceStats(Ljava/lang/String;I)J -Landroid/net/INetworkStatsService$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/INetworkStatsService$Stub$Proxy;->getTotalStats(I)J -Landroid/net/INetworkStatsService$Stub$Proxy;->getUidStats(II)J -Landroid/net/INetworkStatsService$Stub$Proxy;->incrementOperationCount(III)V -Landroid/net/INetworkStatsService$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/INetworkStatsService$Stub$Proxy;->openSession()Landroid/net/INetworkStatsSession; -Landroid/net/INetworkStatsService$Stub$Proxy;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession; -Landroid/net/INetworkStatsService$Stub$Proxy;->registerUsageCallback(Ljava/lang/String;Landroid/net/DataUsageRequest;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/DataUsageRequest; -Landroid/net/INetworkStatsService$Stub$Proxy;->unregisterUsageRequest(Landroid/net/DataUsageRequest;)V -Landroid/net/INetworkStatsService$Stub;-><init>()V -Landroid/net/INetworkStatsService$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_forceUpdate:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_forceUpdateIfaces:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getDataLayerSnapshotForUid:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getDetailedUidStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getIfaceStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getMobileIfaces:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getTotalStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getUidStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_incrementOperationCount:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_openSession:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_openSessionForUsageStats:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_registerUsageCallback:I -Landroid/net/INetworkStatsService$Stub;->TRANSACTION_unregisterUsageRequest:I -Landroid/net/INetworkStatsService;->forceUpdateIfaces([Landroid/net/Network;)V -Landroid/net/INetworkStatsService;->getDetailedUidStats([Ljava/lang/String;)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsService;->getIfaceStats(Ljava/lang/String;I)J -Landroid/net/INetworkStatsService;->getTotalStats(I)J -Landroid/net/INetworkStatsService;->getUidStats(II)J -Landroid/net/INetworkStatsService;->incrementOperationCount(III)V -Landroid/net/INetworkStatsService;->registerUsageCallback(Ljava/lang/String;Landroid/net/DataUsageRequest;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/DataUsageRequest; -Landroid/net/INetworkStatsService;->unregisterUsageRequest(Landroid/net/DataUsageRequest;)V -Landroid/net/INetworkStatsSession$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/INetworkStatsSession$Stub$Proxy;->close()V -Landroid/net/INetworkStatsSession$Stub$Proxy;->getDeviceSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryIntervalForUid(Landroid/net/NetworkTemplate;IIIIJJ)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getRelevantUids()[I -Landroid/net/INetworkStatsSession$Stub$Proxy;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession$Stub$Proxy;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/INetworkStatsSession$Stub;-><init>()V -Landroid/net/INetworkStatsSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsSession; -Landroid/net/INetworkStatsSession$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_close:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getDeviceSummaryForNetwork:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryForNetwork:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryForUid:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryIntervalForUid:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getRelevantUids:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getSummaryForAllUid:I -Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getSummaryForNetwork:I -Landroid/net/INetworkStatsSession;->getDeviceSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession;->getHistoryIntervalForUid(Landroid/net/NetworkTemplate;IIIIJJ)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession;->getRelevantUids()[I Landroid/net/InterfaceConfiguration;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/net/InterfaceConfiguration;->FLAG_DOWN:Ljava/lang/String; Landroid/net/InterfaceConfiguration;->FLAG_UP:Ljava/lang/String; @@ -36529,41 +36388,6 @@ Landroid/net/SSLSessionCache;->TAG:Ljava/lang/String; Landroid/net/StringNetworkSpecifier;-><init>(Ljava/lang/String;)V Landroid/net/StringNetworkSpecifier;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/net/StringNetworkSpecifier;->satisfiedBy(Landroid/net/NetworkSpecifier;)Z -Landroid/net/TrafficStats;->addIfSupported(J)J -Landroid/net/TrafficStats;->closeQuietly(Landroid/net/INetworkStatsSession;)V -Landroid/net/TrafficStats;->GB_IN_BYTES:J -Landroid/net/TrafficStats;->getDataLayerSnapshotForUid(Landroid/content/Context;)Landroid/net/NetworkStats; -Landroid/net/TrafficStats;->getRxPackets(Ljava/lang/String;)J -Landroid/net/TrafficStats;->getTxPackets(Ljava/lang/String;)J -Landroid/net/TrafficStats;->KB_IN_BYTES:J -Landroid/net/TrafficStats;->LOOPBACK_IFACE:Ljava/lang/String; -Landroid/net/TrafficStats;->MB_IN_BYTES:J -Landroid/net/TrafficStats;->PB_IN_BYTES:J -Landroid/net/TrafficStats;->sActiveProfilingStart:Landroid/net/NetworkStats; -Landroid/net/TrafficStats;->sProfilingLock:Ljava/lang/Object; -Landroid/net/TrafficStats;->sStatsService:Landroid/net/INetworkStatsService; -Landroid/net/TrafficStats;->startDataProfiling(Landroid/content/Context;)V -Landroid/net/TrafficStats;->stopDataProfiling(Landroid/content/Context;)Landroid/net/NetworkStats; -Landroid/net/TrafficStats;->TAG_SYSTEM_APP:I -Landroid/net/TrafficStats;->TAG_SYSTEM_BACKUP:I -Landroid/net/TrafficStats;->TAG_SYSTEM_DHCP:I -Landroid/net/TrafficStats;->TAG_SYSTEM_DOWNLOAD:I -Landroid/net/TrafficStats;->TAG_SYSTEM_GPS:I -Landroid/net/TrafficStats;->TAG_SYSTEM_MEDIA:I -Landroid/net/TrafficStats;->TAG_SYSTEM_NEIGHBOR:I -Landroid/net/TrafficStats;->TAG_SYSTEM_NTP:I -Landroid/net/TrafficStats;->TAG_SYSTEM_PAC:I -Landroid/net/TrafficStats;->TAG_SYSTEM_PROBE:I -Landroid/net/TrafficStats;->TAG_SYSTEM_RESTORE:I -Landroid/net/TrafficStats;->TB_IN_BYTES:J -Landroid/net/TrafficStats;->TYPE_RX_BYTES:I -Landroid/net/TrafficStats;->TYPE_RX_PACKETS:I -Landroid/net/TrafficStats;->TYPE_TCP_RX_PACKETS:I -Landroid/net/TrafficStats;->TYPE_TCP_TX_PACKETS:I -Landroid/net/TrafficStats;->TYPE_TX_BYTES:I -Landroid/net/TrafficStats;->TYPE_TX_PACKETS:I -Landroid/net/TrafficStats;->UID_REMOVED:I -Landroid/net/TrafficStats;->UID_TETHERING:I Landroid/net/Uri$AbstractHierarchicalUri;-><init>()V Landroid/net/Uri$AbstractHierarchicalUri;->getUserInfoPart()Landroid/net/Uri$Part; Landroid/net/Uri$AbstractHierarchicalUri;->host:Ljava/lang/String; diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt index 002d42dbf1dc..e47149b7e671 100644 --- a/boot/hiddenapi/hiddenapi-unsupported.txt +++ b/boot/hiddenapi/hiddenapi-unsupported.txt @@ -170,9 +170,6 @@ Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager; Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService; -Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String; -Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService; Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/os/IDeviceIdentifiersPolicyService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IDeviceIdentifiersPolicyService; Landroid/os/IDeviceIdleController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IDeviceIdleController; diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index c134822585e0..c202f6f03b5b 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -39,6 +39,7 @@ cc_defaults { "-modernize-pass-by-value", "-modernize-replace-disallow-copy-and-assign-macro", "-modernize-return-braced-init-list", + "-modernize-use-default-member-init", "-modernize-use-equals-default", "-modernize-use-nodiscard", "-modernize-use-override", diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index a8d648917b08..1b2d905aec0a 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -39,11 +39,9 @@ #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -using android::IPCThreadState; using android::base::StringPrintf; using android::binder::Status; using android::idmap2::BinaryStreamVisitor; -using android::idmap2::FabricatedOverlay; using android::idmap2::FabricatedOverlayContainer; using android::idmap2::Idmap; using android::idmap2::IdmapHeader; diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 9c6402a6e36e..738b9cf237c9 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -37,7 +37,6 @@ #include "idmap2/Idmap.h" #include "idmap2/LogInfo.h" -using android::Res_value; using ::testing::NotNull; using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp index 5a1d808af06f..32b3d1326d92 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -29,8 +29,6 @@ #include "idmap2/LogInfo.h" #include "idmap2/ResourceMapping.h" -using android::Res_value; - using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { diff --git a/core/api/current.txt b/core/api/current.txt index 3a8076c932b7..42407ea10fb5 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -8339,62 +8339,6 @@ package android.app.usage { field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.ExternalStorageStats> CREATOR; } - public final class NetworkStats implements java.lang.AutoCloseable { - method public void close(); - method public boolean getNextBucket(android.app.usage.NetworkStats.Bucket); - method public boolean hasNextBucket(); - } - - public static class NetworkStats.Bucket { - ctor public NetworkStats.Bucket(); - method public int getDefaultNetworkStatus(); - method public long getEndTimeStamp(); - method public int getMetered(); - method public int getRoaming(); - method public long getRxBytes(); - method public long getRxPackets(); - method public long getStartTimeStamp(); - method public int getState(); - method public int getTag(); - method public long getTxBytes(); - method public long getTxPackets(); - method public int getUid(); - field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff - field public static final int DEFAULT_NETWORK_NO = 1; // 0x1 - field public static final int DEFAULT_NETWORK_YES = 2; // 0x2 - field public static final int METERED_ALL = -1; // 0xffffffff - field public static final int METERED_NO = 1; // 0x1 - field public static final int METERED_YES = 2; // 0x2 - field public static final int ROAMING_ALL = -1; // 0xffffffff - field public static final int ROAMING_NO = 1; // 0x1 - field public static final int ROAMING_YES = 2; // 0x2 - field public static final int STATE_ALL = -1; // 0xffffffff - field public static final int STATE_DEFAULT = 1; // 0x1 - field public static final int STATE_FOREGROUND = 2; // 0x2 - field public static final int TAG_NONE = 0; // 0x0 - field public static final int UID_ALL = -1; // 0xffffffff - field public static final int UID_REMOVED = -4; // 0xfffffffc - field public static final int UID_TETHERING = -5; // 0xfffffffb - } - - public class NetworkStatsManager { - method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException; - method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException; - method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback); - method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler); - method public void unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback); - } - - public abstract static class NetworkStatsManager.UsageCallback { - ctor public NetworkStatsManager.UsageCallback(); - method public abstract void onThresholdReached(int, String); - } - public final class StorageStats implements android.os.Parcelable { method public int describeContents(); method public long getAppBytes(); @@ -25311,9 +25255,9 @@ package android.net { public abstract class PlatformVpnProfile { method public final boolean areLocalRoutesExcluded(); - method public final boolean getRequiresInternetValidation(); method public final int getType(); method @NonNull public final String getTypeString(); + method public final boolean isInternetValidationRequired(); field public static final int TYPE_IKEV2_IPSEC_PSK = 7; // 0x7 field public static final int TYPE_IKEV2_IPSEC_RSA = 8; // 0x8 field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6 @@ -25366,50 +25310,6 @@ package android.net { method @NonNull public android.net.TelephonyNetworkSpecifier.Builder setSubscriptionId(int); } - public class TrafficStats { - ctor public TrafficStats(); - method public static void clearThreadStatsTag(); - method public static void clearThreadStatsUid(); - method public static int getAndSetThreadStatsTag(int); - method public static long getMobileRxBytes(); - method public static long getMobileRxPackets(); - method public static long getMobileTxBytes(); - method public static long getMobileTxPackets(); - method public static long getRxBytes(@NonNull String); - method public static long getRxPackets(@NonNull String); - method public static int getThreadStatsTag(); - method public static int getThreadStatsUid(); - method public static long getTotalRxBytes(); - method public static long getTotalRxPackets(); - method public static long getTotalTxBytes(); - method public static long getTotalTxPackets(); - method public static long getTxBytes(@NonNull String); - method public static long getTxPackets(@NonNull String); - method public static long getUidRxBytes(int); - method public static long getUidRxPackets(int); - method @Deprecated public static long getUidTcpRxBytes(int); - method @Deprecated public static long getUidTcpRxSegments(int); - method @Deprecated public static long getUidTcpTxBytes(int); - method @Deprecated public static long getUidTcpTxSegments(int); - method public static long getUidTxBytes(int); - method public static long getUidTxPackets(int); - method @Deprecated public static long getUidUdpRxBytes(int); - method @Deprecated public static long getUidUdpRxPackets(int); - method @Deprecated public static long getUidUdpTxBytes(int); - method @Deprecated public static long getUidUdpTxPackets(int); - method public static void incrementOperationCount(int); - method public static void incrementOperationCount(int, int); - method public static void setThreadStatsTag(int); - method public static void setThreadStatsUid(int); - method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException; - method public static void tagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException; - method public static void tagSocket(java.net.Socket) throws java.net.SocketException; - method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException; - method public static void untagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException; - method public static void untagSocket(java.net.Socket) throws java.net.SocketException; - field public static final int UNSUPPORTED = -1; // 0xffffffff - } - public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method public abstract android.net.Uri.Builder buildUpon(); method public int compareTo(android.net.Uri); @@ -25564,7 +25464,7 @@ package android.net { field public static final String EXTRA_ERROR_CLASS = "android.net.extra.ERROR_CLASS"; field public static final String EXTRA_ERROR_CODE = "android.net.extra.ERROR_CODE"; field public static final String EXTRA_SESSION_KEY = "android.net.extra.SESSION_KEY"; - field public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP"; + field public static final String EXTRA_TIMESTAMP_MILLIS = "android.net.extra.TIMESTAMP_MILLIS"; field public static final String EXTRA_UNDERLYING_LINK_PROPERTIES = "android.net.extra.UNDERLYING_LINK_PROPERTIES"; field public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK"; field public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES = "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES"; @@ -26189,7 +26089,6 @@ package android.nfc.cardemulation { field public static final String CATEGORY_PAYMENT = "payment"; field public static final String EXTRA_CATEGORY = "category"; field public static final String EXTRA_SERVICE_COMPONENT = "component"; - field public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID"; field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1 field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2 field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0 @@ -29424,7 +29323,7 @@ package android.os { public class BaseBundle { method public void clear(); method public boolean containsKey(String); - method @Nullable public Object get(String); + method @Deprecated @Nullable public Object get(String); method public boolean getBoolean(String); method public boolean getBoolean(String, boolean); method @Nullable public boolean[] getBooleanArray(@Nullable String); @@ -29596,6 +29495,7 @@ package android.os { field public static final int PREVIEW_SDK_INT; field public static final String RELEASE; field @NonNull public static final String RELEASE_OR_CODENAME; + field @NonNull public static final String RELEASE_OR_PREVIEW_DISPLAY; field @Deprecated public static final String SDK; field public static final int SDK_INT; field public static final String SECURITY_PATCH; @@ -29665,16 +29565,21 @@ package android.os { method public float getFloat(String, float); method @Nullable public float[] getFloatArray(@Nullable String); method @Nullable public java.util.ArrayList<java.lang.Integer> getIntegerArrayList(@Nullable String); - method @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String); - method @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String); - method @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String); - method @Nullable public java.io.Serializable getSerializable(@Nullable String); + method @Deprecated @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String); + method @Nullable public <T> T getParcelable(@Nullable String, @NonNull Class<T>); + method @Deprecated @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String); + method @Nullable public <T> T[] getParcelableArray(@Nullable String, @NonNull Class<T>); + method @Deprecated @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String); + method @Nullable public <T> java.util.ArrayList<T> getParcelableArrayList(@Nullable String, @NonNull Class<? extends T>); + method @Deprecated @Nullable public java.io.Serializable getSerializable(@Nullable String); + method @Nullable public <T extends java.io.Serializable> T getSerializable(@Nullable String, @NonNull Class<T>); method public short getShort(String); method public short getShort(String, short); method @Nullable public short[] getShortArray(@Nullable String); method @Nullable public android.util.Size getSize(@Nullable String); method @Nullable public android.util.SizeF getSizeF(@Nullable String); - method @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String); + method @Deprecated @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String); + method @Nullable public <T> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String, @NonNull Class<? extends T>); method @Nullable public java.util.ArrayList<java.lang.String> getStringArrayList(@Nullable String); method public boolean hasFileDescriptors(); method public void putAll(android.os.Bundle); @@ -30297,7 +30202,7 @@ package android.os { method @Deprecated @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 @Deprecated @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader); - method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<T>); + method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<? extends T>); method @Nullable public android.os.PersistableBundle readPersistableBundle(); method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader); method @Deprecated @Nullable public java.io.Serializable readSerializable(); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index bcdd0a4664d7..a9f0d2e54b21 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -51,31 +51,6 @@ package android.app { } -package android.app.usage { - - public class NetworkStatsManager { - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void forceUpdate(); - method public static int getCollapsedRatType(int); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyNetworkStatus(@NonNull java.util.List<android.net.Network>, @NonNull java.util.List<android.net.NetworkStateSnapshot>, @Nullable String, @NonNull java.util.List<android.net.UnderlyingNetworkInfo>); - method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForDevice(@NonNull android.net.NetworkTemplate, long, long); - method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(@NonNull android.net.NetworkTemplate, long, long, int, int, int) throws java.lang.SecurityException; - method @NonNull @WorkerThread public android.app.usage.NetworkStats querySummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException; - method @NonNull @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(@NonNull android.net.NetworkTemplate, long, long); - method @NonNull @WorkerThread public android.app.usage.NetworkStats queryTaggedSummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException; - method public void registerUsageCallback(@NonNull android.net.NetworkTemplate, long, @NonNull java.util.concurrent.Executor, @NonNull android.app.usage.NetworkStatsManager.UsageCallback); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setDefaultGlobalAlert(long); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setPollOnOpen(boolean); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setStatsProviderWarningAndLimitAsync(@NonNull String, long, long); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setUidForeground(int, boolean); - field public static final int NETWORK_TYPE_5G_NSA = -2; // 0xfffffffe - } - - public abstract static class NetworkStatsManager.UsageCallback { - method public void onThresholdReached(@NonNull android.net.NetworkTemplate); - } - -} - package android.content { public abstract class ContentProvider implements android.content.ComponentCallbacks2 { @@ -238,32 +213,6 @@ package android.net { ctor public LocalSocket(@NonNull java.io.FileDescriptor); } - public class NetworkIdentity { - method public int getOemManaged(); - method public int getRatType(); - method @Nullable public String getSubscriberId(); - method public int getType(); - method @Nullable public String getWifiNetworkKey(); - method public boolean isDefaultNetwork(); - method public boolean isMetered(); - method public boolean isRoaming(); - } - - public static final class NetworkIdentity.Builder { - ctor public NetworkIdentity.Builder(); - method @NonNull public android.net.NetworkIdentity build(); - method @NonNull public android.net.NetworkIdentity.Builder clearRatType(); - method @NonNull public android.net.NetworkIdentity.Builder setDefaultNetwork(boolean); - method @NonNull public android.net.NetworkIdentity.Builder setMetered(boolean); - method @NonNull public android.net.NetworkIdentity.Builder setNetworkStateSnapshot(@NonNull android.net.NetworkStateSnapshot); - method @NonNull public android.net.NetworkIdentity.Builder setOemManaged(int); - method @NonNull public android.net.NetworkIdentity.Builder setRatType(int); - method @NonNull public android.net.NetworkIdentity.Builder setRoaming(boolean); - method @NonNull public android.net.NetworkIdentity.Builder setSubscriberId(@Nullable String); - method @NonNull public android.net.NetworkIdentity.Builder setType(int); - method @NonNull public android.net.NetworkIdentity.Builder setWifiNetworkKey(@Nullable String); - } - public class NetworkPolicyManager { method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int); @@ -279,94 +228,6 @@ package android.net { method public default void onUidBlockedReasonChanged(int, int); } - public final class NetworkStateSnapshot implements android.os.Parcelable { - ctor public NetworkStateSnapshot(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @Nullable String, int); - method public int describeContents(); - method public int getLegacyType(); - method @NonNull public android.net.LinkProperties getLinkProperties(); - method @NonNull public android.net.Network getNetwork(); - method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); - method @Nullable public String getSubscriberId(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR; - } - - public class NetworkStatsCollection { - method @NonNull public java.util.Map<android.net.NetworkStatsCollection.Key,android.net.NetworkStatsHistory> getEntries(); - } - - public static final class NetworkStatsCollection.Builder { - ctor public NetworkStatsCollection.Builder(long); - method @NonNull public android.net.NetworkStatsCollection.Builder addEntry(@NonNull android.net.NetworkStatsCollection.Key, @NonNull android.net.NetworkStatsHistory); - method @NonNull public android.net.NetworkStatsCollection build(); - } - - public static class NetworkStatsCollection.Key { - ctor public NetworkStatsCollection.Key(@NonNull java.util.Set<android.net.NetworkIdentity>, int, int, int); - } - - public final class NetworkStatsHistory implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.List<android.net.NetworkStatsHistory.Entry> getEntries(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStatsHistory> CREATOR; - } - - public static final class NetworkStatsHistory.Builder { - ctor public NetworkStatsHistory.Builder(long, int); - method @NonNull public android.net.NetworkStatsHistory.Builder addEntry(@NonNull android.net.NetworkStatsHistory.Entry); - method @NonNull public android.net.NetworkStatsHistory build(); - } - - public static final class NetworkStatsHistory.Entry { - ctor public NetworkStatsHistory.Entry(long, long, long, long, long, long, long); - method public long getActiveTime(); - method public long getBucketStart(); - method public long getOperations(); - method public long getRxBytes(); - method public long getRxPackets(); - method public long getTxBytes(); - method public long getTxPackets(); - } - - public final class NetworkTemplate implements android.os.Parcelable { - method public int describeContents(); - method public int getDefaultNetworkStatus(); - method public int getMatchRule(); - method public int getMeteredness(); - method public int getOemManaged(); - method public int getRatType(); - method public int getRoaming(); - method @NonNull public java.util.Set<java.lang.String> getSubscriberIds(); - method @NonNull public java.util.Set<java.lang.String> getWifiNetworkKeys(); - method public boolean matches(@NonNull android.net.NetworkIdentity); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkTemplate> CREATOR; - field public static final int MATCH_BLUETOOTH = 8; // 0x8 - field public static final int MATCH_CARRIER = 10; // 0xa - field public static final int MATCH_ETHERNET = 5; // 0x5 - field public static final int MATCH_MOBILE = 1; // 0x1 - field public static final int MATCH_WIFI = 4; // 0x4 - field public static final int NETWORK_TYPE_ALL = -1; // 0xffffffff - field public static final int OEM_MANAGED_ALL = -1; // 0xffffffff - field public static final int OEM_MANAGED_NO = 0; // 0x0 - field public static final int OEM_MANAGED_PAID = 1; // 0x1 - field public static final int OEM_MANAGED_PRIVATE = 2; // 0x2 - field public static final int OEM_MANAGED_YES = -2; // 0xfffffffe - } - - public static final class NetworkTemplate.Builder { - ctor public NetworkTemplate.Builder(int); - method @NonNull public android.net.NetworkTemplate build(); - method @NonNull public android.net.NetworkTemplate.Builder setDefaultNetworkStatus(int); - method @NonNull public android.net.NetworkTemplate.Builder setMeteredness(int); - method @NonNull public android.net.NetworkTemplate.Builder setOemManaged(int); - method @NonNull public android.net.NetworkTemplate.Builder setRatType(int); - method @NonNull public android.net.NetworkTemplate.Builder setRoaming(int); - method @NonNull public android.net.NetworkTemplate.Builder setSubscriberIds(@NonNull java.util.Set<java.lang.String>); - method @NonNull public android.net.NetworkTemplate.Builder setWifiNetworkKeys(@NonNull java.util.Set<java.lang.String>); - } - public class NetworkWatchlistManager { method @Nullable public byte[] getWatchlistConfigHash(); } @@ -385,21 +246,6 @@ package android.net { method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo); } - public class TrafficStats { - method public static void attachSocketTagger(); - method public static void init(@NonNull android.content.Context); - } - - public final class UnderlyingNetworkInfo implements android.os.Parcelable { - ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>); - method public int describeContents(); - method @NonNull public String getInterface(); - method public int getOwnerUid(); - method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR; - } - public class VpnManager { field public static final int TYPE_VPN_LEGACY = 3; // 0x3 field public static final int TYPE_VPN_NONE = -1; // 0xffffffff diff --git a/core/api/removed.txt b/core/api/removed.txt index bf8642223ef5..32a46735abea 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -260,10 +260,6 @@ package android.net { method @Deprecated public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache); } - public class TrafficStats { - method @Deprecated public static void setThreadStatsUidSelf(); - } - } package android.os { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2c7f456c5e76..9db26bd1e373 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1852,13 +1852,6 @@ package android.app.usage { field public static final String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService"; } - public class NetworkStatsManager { - method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getMobileUidStats(); - method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getWifiUidStats(); - method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider); - method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider); - } - public static final class UsageEvents.Event { method public int getInstanceId(); method @Nullable public String getNotificationChannelId(); @@ -7046,14 +7039,21 @@ package android.net { } public final class EthernetNetworkUpdateRequest implements android.os.Parcelable { - ctor public EthernetNetworkUpdateRequest(@NonNull android.net.StaticIpConfiguration, @NonNull android.net.NetworkCapabilities); method public int describeContents(); - method @NonNull public android.net.StaticIpConfiguration getIpConfig(); + method @NonNull public android.net.IpConfiguration getIpConfiguration(); method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkUpdateRequest> CREATOR; } + public static final class EthernetNetworkUpdateRequest.Builder { + ctor public EthernetNetworkUpdateRequest.Builder(); + ctor public EthernetNetworkUpdateRequest.Builder(@NonNull android.net.EthernetNetworkUpdateRequest); + method @NonNull public android.net.EthernetNetworkUpdateRequest build(); + method @NonNull public android.net.EthernetNetworkUpdateRequest.Builder setIpConfiguration(@NonNull android.net.IpConfiguration); + method @NonNull public android.net.EthernetNetworkUpdateRequest.Builder setNetworkCapabilities(@NonNull android.net.NetworkCapabilities); + } + public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { ctor public MatchAllNetworkSpecifier(); method public int describeContents(); @@ -7115,48 +7115,6 @@ package android.net { field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; } - public final class NetworkStats implements java.lang.Iterable<android.net.NetworkStats.Entry> android.os.Parcelable { - ctor public NetworkStats(long, int); - method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats); - method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry); - method public int describeContents(); - method @NonNull public java.util.Iterator<android.net.NetworkStats.Entry> iterator(); - method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR; - field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff - field public static final int DEFAULT_NETWORK_NO = 0; // 0x0 - field public static final int DEFAULT_NETWORK_YES = 1; // 0x1 - field public static final String IFACE_VT = "vt_data0"; - field public static final int METERED_ALL = -1; // 0xffffffff - field public static final int METERED_NO = 0; // 0x0 - field public static final int METERED_YES = 1; // 0x1 - field public static final int ROAMING_ALL = -1; // 0xffffffff - field public static final int ROAMING_NO = 0; // 0x0 - field public static final int ROAMING_YES = 1; // 0x1 - field public static final int SET_ALL = -1; // 0xffffffff - field public static final int SET_DEFAULT = 0; // 0x0 - field public static final int SET_FOREGROUND = 1; // 0x1 - field public static final int TAG_NONE = 0; // 0x0 - field public static final int UID_ALL = -1; // 0xffffffff - field public static final int UID_TETHERING = -5; // 0xfffffffb - } - - public static class NetworkStats.Entry { - ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long); - method public int getDefaultNetwork(); - method public int getMetered(); - method public long getOperations(); - method public int getRoaming(); - method public long getRxBytes(); - method public long getRxPackets(); - method public int getSet(); - method public int getTag(); - method public long getTxBytes(); - method public long getTxPackets(); - method public int getUid(); - } - public class RssiCurve implements android.os.Parcelable { ctor public RssiCurve(int, int, byte[]); ctor public RssiCurve(int, int, byte[], int); @@ -7188,19 +7146,6 @@ package android.net { field public final android.net.RssiCurve rssiCurve; } - public class TrafficStats { - method public static void setThreadStatsTagApp(); - method public static void setThreadStatsTagBackup(); - method public static void setThreadStatsTagDownload(); - method public static void setThreadStatsTagRestore(); - field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f - field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80 - field public static final int TAG_NETWORK_STACK_RANGE_END = -257; // 0xfffffeff - field public static final int TAG_NETWORK_STACK_RANGE_START = -768; // 0xfffffd00 - field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = -241; // 0xffffff0f - field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00 - } - public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method @NonNull public String toSafeString(); } @@ -7381,23 +7326,6 @@ package android.net.metrics { } -package android.net.netstats.provider { - - public abstract class NetworkStatsProvider { - ctor public NetworkStatsProvider(); - method public void notifyAlertReached(); - method public void notifyLimitReached(); - method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats); - method public void notifyWarningReached(); - method public abstract void onRequestStatsUpdate(int); - method public abstract void onSetAlert(long); - method public abstract void onSetLimit(@NonNull String, long); - method public void onSetWarningAndLimit(@NonNull String, long, long); - field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff - } - -} - package android.net.sip { @Deprecated public class SipAudioCall { @@ -9006,6 +8934,7 @@ package android.provider { field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS"; field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS"; + field public static final String ACTION_TETHER_UNSUPPORTED_CARRIER_UI = "android.settings.TETHER_UNSUPPORTED_CARRIER_UI"; } public static final class Settings.Global extends android.provider.Settings.NameValueTable { @@ -10131,8 +10060,6 @@ package android.service.tracing { public class TraceReportService extends android.app.Service { ctor public TraceReportService(); - method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); - method public boolean onMessage(@NonNull android.os.Message); method public void onReportTrace(@NonNull android.service.tracing.TraceReportService.TraceParams); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index a0b35800d4a2..73caa5d5f0d4 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -635,10 +635,6 @@ package android.app.prediction { package android.app.usage { - public class NetworkStatsManager { - method public void setPollForce(boolean); - } - public class StorageStatsManager { method public boolean isQuotaSupported(@NonNull java.util.UUID); method public boolean isReservedSupported(@NonNull java.util.UUID); @@ -1589,13 +1585,6 @@ package android.net { method @Nullable public byte[] getWatchlistConfigHash(); } - public class TrafficStats { - method public static long getLoopbackRxBytes(); - method public static long getLoopbackRxPackets(); - method public static long getLoopbackTxBytes(); - method public static long getLoopbackTxPackets(); - } - } package android.os { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 3766dc8a66f6..6a0a2892cb96 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -44,7 +44,6 @@ import android.app.timezonedetector.TimeZoneDetectorImpl; import android.app.trust.TrustManager; import android.app.usage.IStorageStatsManager; import android.app.usage.IUsageStatsManager; -import android.app.usage.NetworkStatsManager; import android.app.usage.StorageStatsManager; import android.app.usage.UsageStatsManager; import android.apphibernation.AppHibernationManager; @@ -129,7 +128,6 @@ import android.net.ConnectivityFrameworkInitializerTiramisu; import android.net.EthernetManager; import android.net.IEthernetManager; import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.IPacProxyManager; import android.net.IVpnManager; import android.net.NetworkPolicyManager; @@ -967,17 +965,6 @@ public final class SystemServiceRegistry { return new UsageStatsManager(ctx.getOuterContext(), service); }}); - registerService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class, - new CachedServiceFetcher<NetworkStatsManager>() { - @Override - public NetworkStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException { - // TODO: Replace with an initializer in the module, see - // {@code ConnectivityFrameworkInitializer}. - final INetworkStatsService service = INetworkStatsService.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)); - return new NetworkStatsManager(ctx.getOuterContext(), service); - }}); - registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class, new StaticServiceFetcher<PersistentDataBlockManager>() { @Override diff --git a/core/java/android/app/assist/OWNERS b/core/java/android/app/assist/OWNERS index 46b5ea03c545..e857c72bb28e 100644 --- a/core/java/android/app/assist/OWNERS +++ b/core/java/android/app/assist/OWNERS @@ -1,7 +1,5 @@ +augale@google.com joannechung@google.com -adamhe@google.com -tymtsai@google.com +markpun@google.com lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com +tymtsai@google.com diff --git a/core/java/android/app/contentsuggestions/OWNERS b/core/java/android/app/contentsuggestions/OWNERS index 482abb2d94e9..cf54c2a6fcbc 100644 --- a/core/java/android/app/contentsuggestions/OWNERS +++ b/core/java/android/app/contentsuggestions/OWNERS @@ -1,9 +1,7 @@ # Bug component: 643919 +augale@google.com joannechung@google.com -adamhe@google.com -tymtsai@google.com +markpun@google.com lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com +tymtsai@google.com diff --git a/core/java/android/app/time/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java index 8e281c07c45d..a7c0e5c79607 100644 --- a/core/java/android/app/time/ExternalTimeSuggestion.java +++ b/core/java/android/app/time/ExternalTimeSuggestion.java @@ -50,16 +50,17 @@ import java.util.Objects; * <p>The creator of an external suggestion is expected to be separate Android process, e.g. a * process integrating with the external time source via a HAL or local network. The creator must * capture the elapsed realtime reference clock, e.g. via {@link SystemClock#elapsedRealtime()}, - * when the UTC time is first obtained (usually under a wakelock). This enables Android to adjust - * for latency introduced between suggestion creation and eventual use. Adjustments for other + * when the Unix epoch time is first obtained (usually under a wakelock). This enables Android to + * adjust for latency introduced between suggestion creation and eventual use. Adjustments for other * sources of latency, i.e. those before the external time suggestion is created, must be handled by * the creator. * * <p>{@code elapsedRealtimeMillis} and {@code suggestionMillis} represent the suggested time. - * {@code suggestionMillis} is the number of milliseconds elapsed since 1/1/1970 00:00:00 UTC. - * {@code elapsedRealtimeMillis} is the value of the elapsed realtime clock when {@code - * suggestionMillis} was established. Note that the elapsed realtime clock is considered accurate - * but it is volatile, so time suggestions cannot be persisted across device resets. + * {@code suggestionMillis} is the number of milliseconds elapsed since 1/1/1970 00:00:00 UTC + * according to the Unix time scale. {@code elapsedRealtimeMillis} is the value of the elapsed + * realtime clock when {@code suggestionMillis} was established. Note that the elapsed realtime + * clock is considered accurate but it is volatile, so time suggestions cannot be persisted across + * device resets. * * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to * record why the suggestion exists and how it was entered. This information exists only to aid in @@ -83,7 +84,7 @@ public final class ExternalTimeSuggestion implements Parcelable { }; @NonNull - private final TimestampedValue<Long> mUtcTime; + private final TimestampedValue<Long> mUnixEpochTime; @Nullable private ArrayList<String> mDebugInfo; @@ -92,12 +93,12 @@ public final class ExternalTimeSuggestion implements Parcelable { * ExternalTimeSuggestion} for more details. * * @param elapsedRealtimeMillis the elapsed realtime clock reference for the suggestion - * @param suggestionMillis the suggested UTC time in milliseconds since the start of the + * @param suggestionMillis the suggested time in milliseconds since the start of the * Unix epoch */ public ExternalTimeSuggestion(@ElapsedRealtimeLong long elapsedRealtimeMillis, @CurrentTimeMillisLong long suggestionMillis) { - mUtcTime = new TimestampedValue(elapsedRealtimeMillis, suggestionMillis); + mUnixEpochTime = new TimestampedValue(elapsedRealtimeMillis, suggestionMillis); } private static ExternalTimeSuggestion createFromParcel(Parcel in) { @@ -117,7 +118,7 @@ public final class ExternalTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mUtcTime, 0); + dest.writeParcelable(mUnixEpochTime, 0); dest.writeList(mDebugInfo); } @@ -125,8 +126,8 @@ public final class ExternalTimeSuggestion implements Parcelable { * {@hide} */ @NonNull - public TimestampedValue<Long> getUtcTime() { - return mUtcTime; + public TimestampedValue<Long> getUnixEpochTime() { + return mUnixEpochTime; } /** @@ -160,17 +161,18 @@ public final class ExternalTimeSuggestion implements Parcelable { return false; } ExternalTimeSuggestion that = (ExternalTimeSuggestion) o; - return Objects.equals(mUtcTime, that.mUtcTime); + return Objects.equals(mUnixEpochTime, that.mUnixEpochTime); } @Override public int hashCode() { - return Objects.hash(mUtcTime); + return Objects.hash(mUnixEpochTime); } @Override public String toString() { - return "ExternalTimeSuggestion{" + "mUtcTime=" + mUtcTime + ", mDebugInfo=" + mDebugInfo + return "ExternalTimeSuggestion{" + "mUnixEpochTime=" + mUnixEpochTime + + ", mDebugInfo=" + mDebugInfo + '}'; } } diff --git a/core/java/android/app/timedetector/GnssTimeSuggestion.java b/core/java/android/app/timedetector/GnssTimeSuggestion.java index 6478a2dd2aa9..34f4565fb410 100644 --- a/core/java/android/app/timedetector/GnssTimeSuggestion.java +++ b/core/java/android/app/timedetector/GnssTimeSuggestion.java @@ -31,11 +31,11 @@ import java.util.Objects; /** * A time signal from a GNSS source. * - * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds - * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the - * elapsed realtime clock when the {@code utcTime.value} was established. - * Note that the elapsed realtime clock is considered accurate but it is volatile, so time - * suggestions cannot be persisted across device resets. + * <p>{@code unixEpochTime} is the suggested time. The {@code unixEpochTime.value} is the number of + * milliseconds elapsed since 1/1/1970 00:00:00 UTC according to the Unix time system. The {@code + * unixEpochTime.referenceTimeMillis} is the value of the elapsed realtime clock when the {@code + * unixEpochTime.value} was established. Note that the elapsed realtime clock is considered accurate + * but it is volatile, so time suggestions cannot be persisted across device resets. * * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to * record why the suggestion exists and how it was entered. This information exists only to aid in @@ -57,17 +57,17 @@ public final class GnssTimeSuggestion implements Parcelable { } }; - @NonNull private final TimestampedValue<Long> mUtcTime; + @NonNull private final TimestampedValue<Long> mUnixEpochTime; @Nullable private ArrayList<String> mDebugInfo; - public GnssTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) { - mUtcTime = Objects.requireNonNull(utcTime); - Objects.requireNonNull(utcTime.getValue()); + public GnssTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) { + mUnixEpochTime = Objects.requireNonNull(unixEpochTime); + Objects.requireNonNull(unixEpochTime.getValue()); } private static GnssTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); - GnssTimeSuggestion suggestion = new GnssTimeSuggestion(utcTime); + TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */); + GnssTimeSuggestion suggestion = new GnssTimeSuggestion(unixEpochTime); @SuppressWarnings("unchecked") ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; @@ -81,13 +81,13 @@ public final class GnssTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mUtcTime, 0); + dest.writeParcelable(mUnixEpochTime, 0); dest.writeList(mDebugInfo); } @NonNull - public TimestampedValue<Long> getUtcTime() { - return mUtcTime; + public TimestampedValue<Long> getUnixEpochTime() { + return mUnixEpochTime; } @NonNull @@ -117,18 +117,18 @@ public final class GnssTimeSuggestion implements Parcelable { return false; } GnssTimeSuggestion that = (GnssTimeSuggestion) o; - return Objects.equals(mUtcTime, that.mUtcTime); + return Objects.equals(mUnixEpochTime, that.mUnixEpochTime); } @Override public int hashCode() { - return Objects.hash(mUtcTime); + return Objects.hash(mUnixEpochTime); } @Override public String toString() { return "GnssTimeSuggestion{" - + "mUtcTime=" + mUtcTime + + "mUnixEpochTime=" + mUnixEpochTime + ", mDebugInfo=" + mDebugInfo + '}'; } diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java index 299e9518e329..76db33b1c32b 100644 --- a/core/java/android/app/timedetector/ManualTimeSuggestion.java +++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java @@ -31,9 +31,9 @@ import java.util.Objects; /** * A time signal from a manual (user provided) source. * - * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds - * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the - * elapsed realtime clock when the {@code utcTime.value} was established. + * <p>{@code unixEpochTime} is the suggested time. The {@code unixEpochTime.value} is the number of + * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code unixEpochTime.referenceTimeMillis} + * is the value of the elapsed realtime clock when the {@code unixEpochTime.value} was established. * Note that the elapsed realtime clock is considered accurate but it is volatile, so time * suggestions cannot be persisted across device resets. * @@ -57,17 +57,17 @@ public final class ManualTimeSuggestion implements Parcelable { } }; - @NonNull private final TimestampedValue<Long> mUtcTime; + @NonNull private final TimestampedValue<Long> mUnixEpochTime; @Nullable private ArrayList<String> mDebugInfo; - public ManualTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) { - mUtcTime = Objects.requireNonNull(utcTime); - Objects.requireNonNull(utcTime.getValue()); + public ManualTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) { + mUnixEpochTime = Objects.requireNonNull(unixEpochTime); + Objects.requireNonNull(unixEpochTime.getValue()); } private static ManualTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); - ManualTimeSuggestion suggestion = new ManualTimeSuggestion(utcTime); + TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */); + ManualTimeSuggestion suggestion = new ManualTimeSuggestion(unixEpochTime); @SuppressWarnings("unchecked") ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; @@ -81,13 +81,13 @@ public final class ManualTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mUtcTime, 0); + dest.writeParcelable(mUnixEpochTime, 0); dest.writeList(mDebugInfo); } @NonNull - public TimestampedValue<Long> getUtcTime() { - return mUtcTime; + public TimestampedValue<Long> getUnixEpochTime() { + return mUnixEpochTime; } @NonNull @@ -117,18 +117,18 @@ public final class ManualTimeSuggestion implements Parcelable { return false; } ManualTimeSuggestion that = (ManualTimeSuggestion) o; - return Objects.equals(mUtcTime, that.mUtcTime); + return Objects.equals(mUnixEpochTime, that.mUnixEpochTime); } @Override public int hashCode() { - return Objects.hash(mUtcTime); + return Objects.hash(mUnixEpochTime); } @Override public String toString() { return "ManualTimeSuggestion{" - + "mUtcTime=" + mUtcTime + + "mUnixEpochTime=" + mUnixEpochTime + ", mDebugInfo=" + mDebugInfo + '}'; } diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java index a5259c27ec42..e22f1d6ea8be 100644 --- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java +++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java @@ -31,11 +31,12 @@ import java.util.Objects; /** * A time signal from a network time source like NTP. * - * <p>{@code utcTime} contains the suggested time. The {@code utcTime.value} is the number of - * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the - * value of the elapsed realtime clock when the {@code utcTime.value} was established. - * Note that the elapsed realtime clock is considered accurate but it is volatile, so time - * suggestions cannot be persisted across device resets. + * <p>{@code unixEpochTime} contains the suggested time. The {@code unixEpochTime.value} is the + * number of milliseconds elapsed since 1/1/1970 00:00:00 UTC according to the Unix time system. + * The {@code unixEpochTime.referenceTimeMillis} is the value of the elapsed realtime clock when + * the {@code unixEpochTime.value} was established. Note that the elapsed realtime clock is + * considered accurate but it is volatile, so time suggestions cannot be persisted across device + * resets. * * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to * record why the suggestion exists and how it was determined. This information exists only to aid @@ -57,17 +58,17 @@ public final class NetworkTimeSuggestion implements Parcelable { } }; - @NonNull private final TimestampedValue<Long> mUtcTime; + @NonNull private final TimestampedValue<Long> mUnixEpochTime; @Nullable private ArrayList<String> mDebugInfo; - public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) { - mUtcTime = Objects.requireNonNull(utcTime); - Objects.requireNonNull(utcTime.getValue()); + public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> unixEpochTime) { + mUnixEpochTime = Objects.requireNonNull(unixEpochTime); + Objects.requireNonNull(unixEpochTime.getValue()); } private static NetworkTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); - NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(utcTime); + TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */); + NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(unixEpochTime); @SuppressWarnings("unchecked") ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; @@ -81,13 +82,13 @@ public final class NetworkTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mUtcTime, 0); + dest.writeParcelable(mUnixEpochTime, 0); dest.writeList(mDebugInfo); } @NonNull - public TimestampedValue<Long> getUtcTime() { - return mUtcTime; + public TimestampedValue<Long> getUnixEpochTime() { + return mUnixEpochTime; } @NonNull @@ -117,18 +118,18 @@ public final class NetworkTimeSuggestion implements Parcelable { return false; } NetworkTimeSuggestion that = (NetworkTimeSuggestion) o; - return Objects.equals(mUtcTime, that.mUtcTime); + return Objects.equals(mUnixEpochTime, that.mUnixEpochTime); } @Override public int hashCode() { - return Objects.hash(mUtcTime); + return Objects.hash(mUnixEpochTime); } @Override public String toString() { return "NetworkTimeSuggestion{" - + "mUtcTime=" + mUtcTime + + "mUnixEpochTime=" + mUnixEpochTime + ", mDebugInfo=" + mDebugInfo + '}'; } diff --git a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java index 6c3a304ed3a7..4ff75174ab57 100644 --- a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java +++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java @@ -34,12 +34,12 @@ import java.util.Objects; * <p>{@code slotIndex} identifies the suggestion source. This enables detection logic to identify * suggestions from the same source when there are several in use. * - * <p>{@code utcTime}. When not {@code null}, the {@code utcTime.value} is the number of - * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the - * value of the elapsed realtime clock when the {@code utcTime.value} was established. + * <p>{@code unixEpochTime}. When not {@code null}, the {@code unixEpochTime.value} is the number of + * milliseconds elapsed since 1/1/1970 00:00:00 UTC. The {@code unixEpochTime.referenceTimeMillis} + * is the value of the elapsed realtime clock when the {@code unixEpochTime.value} was established. * Note that the elapsed realtime clock is considered accurate but it is volatile, so time - * suggestions cannot be persisted across device resets. {@code utcTime} can be {@code null} to - * indicate that the telephony source has entered an "un-opinionated" state and any previous + * suggestions cannot be persisted across device resets. {@code unixEpochTime} can be {@code null} + * to indicate that the telephony source has entered an "un-opinionated" state and any previous * suggestion from the source is being withdrawn. * * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to @@ -65,19 +65,20 @@ public final class TelephonyTimeSuggestion implements Parcelable { }; private final int mSlotIndex; - @Nullable private final TimestampedValue<Long> mUtcTime; + @Nullable private final TimestampedValue<Long> mUnixEpochTime; @Nullable private ArrayList<String> mDebugInfo; private TelephonyTimeSuggestion(Builder builder) { mSlotIndex = builder.mSlotIndex; - mUtcTime = builder.mUtcTime; + mUnixEpochTime = builder.mUnixEpochTime; mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null; } private static TelephonyTimeSuggestion createFromParcel(Parcel in) { int slotIndex = in.readInt(); + TimestampedValue<Long> unixEpochTime = in.readParcelable(null /* classLoader */); TelephonyTimeSuggestion suggestion = new TelephonyTimeSuggestion.Builder(slotIndex) - .setUtcTime(in.readParcelable(null /* classLoader */)) + .setUnixEpochTime(unixEpochTime) .build(); @SuppressWarnings("unchecked") ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); @@ -95,7 +96,7 @@ public final class TelephonyTimeSuggestion implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mSlotIndex); - dest.writeParcelable(mUtcTime, 0); + dest.writeParcelable(mUnixEpochTime, 0); dest.writeList(mDebugInfo); } @@ -111,11 +112,11 @@ public final class TelephonyTimeSuggestion implements Parcelable { /** * Returns the suggested time or {@code null} if there isn't one. * - * <p>See {@link TelephonyTimeSuggestion} for more information about {@code utcTime}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code unixEpochTime}. */ @Nullable - public TimestampedValue<Long> getUtcTime() { - return mUtcTime; + public TimestampedValue<Long> getUnixEpochTime() { + return mUnixEpochTime; } /** @@ -163,19 +164,19 @@ public final class TelephonyTimeSuggestion implements Parcelable { } TelephonyTimeSuggestion that = (TelephonyTimeSuggestion) o; return mSlotIndex == that.mSlotIndex - && Objects.equals(mUtcTime, that.mUtcTime); + && Objects.equals(mUnixEpochTime, that.mUnixEpochTime); } @Override public int hashCode() { - return Objects.hash(mSlotIndex, mUtcTime); + return Objects.hash(mSlotIndex, mUnixEpochTime); } @Override public String toString() { return "TelephonyTimeSuggestion{" + "mSlotIndex='" + mSlotIndex + '\'' - + ", mUtcTime=" + mUtcTime + + ", mUnixEpochTime=" + mUnixEpochTime + ", mDebugInfo=" + mDebugInfo + '}'; } @@ -187,7 +188,7 @@ public final class TelephonyTimeSuggestion implements Parcelable { */ public static final class Builder { private final int mSlotIndex; - @Nullable private TimestampedValue<Long> mUtcTime; + @Nullable private TimestampedValue<Long> mUnixEpochTime; @Nullable private List<String> mDebugInfo; /** @@ -202,16 +203,16 @@ public final class TelephonyTimeSuggestion implements Parcelable { /** * Returns the builder for call chaining. * - * <p>See {@link TelephonyTimeSuggestion} for more information about {@code utcTime}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code unixEpochTime}. */ @NonNull - public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) { - if (utcTime != null) { - // utcTime can be null, but the value it holds cannot. - Objects.requireNonNull(utcTime.getValue()); + public Builder setUnixEpochTime(@Nullable TimestampedValue<Long> unixEpochTime) { + if (unixEpochTime != null) { + // unixEpochTime can be null, but the value it holds cannot. + Objects.requireNonNull(unixEpochTime.getValue()); } - mUtcTime = utcTime; + mUnixEpochTime = unixEpochTime; return this; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 30850e3ba06c..7cb093421417 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3434,7 +3434,7 @@ public abstract class Context { public abstract boolean stopServiceAsUser(Intent service, UserHandle user); /** - * Connect to an application service, creating it if needed. This defines + * Connects to an application service, creating it if needed. This defines * a dependency between your application and the service. The given * <var>conn</var> will receive the service object when it is created and be * told if it dies and restarts. The service will be considered required @@ -3449,11 +3449,8 @@ public abstract class Context { * will be invoked instead of * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder) onServiceConnected()}. * - * <p>This method will throw {@link SecurityException} if the calling app does not - * have permission to bind to the given service. - * - * <p class="note">Note: this method <em>cannot be called from a - * {@link BroadcastReceiver} component</em>. A pattern you can use to + * <p class="note"><b>Note:</b> This method <em>cannot</em> be called from a + * {@link BroadcastReceiver} component. A pattern you can use to * communicate from a BroadcastReceiver to a Service is to call * {@link #startService} with the arguments containing the command to be * sent, with the service calling its @@ -3468,43 +3465,49 @@ public abstract class Context { * specify an explicit component name. * @param conn Receives information as the service is started and stopped. * This must be a valid ServiceConnection object; it must not be null. - * @param flags Operation options for the binding. May be 0, - * {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND}, - * {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT}, - * {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}. - * {@link #BIND_IMPORTANT}, {@link #BIND_ADJUST_WITH_ACTIVITY}, - * {@link #BIND_NOT_PERCEPTIBLE}, or {@link #BIND_INCLUDE_CAPABILITIES}. + * @param flags Operation options for the binding. Can be: + * <ul> + * <li>0 + * <li>{@link #BIND_AUTO_CREATE} + * <li>{@link #BIND_DEBUG_UNBIND} + * <li>{@link #BIND_NOT_FOREGROUND} + * <li>{@link #BIND_ABOVE_CLIENT} + * <li>{@link #BIND_ALLOW_OOM_MANAGEMENT} + * <li>{@link #BIND_WAIVE_PRIORITY} + * <li>{@link #BIND_IMPORTANT} + * <li>{@link #BIND_ADJUST_WITH_ACTIVITY} + * <li>{@link #BIND_NOT_PERCEPTIBLE} + * <li>{@link #BIND_INCLUDE_CAPABILITIES} + * </ul> + * * @return {@code true} if the system is in the process of bringing up a - * service that your client has permission to bind to; {@code false} - * if the system couldn't find the service or if your client doesn't - * have permission to bind to it. If this value is {@code true}, you - * should later call {@link #unbindService} to release the - * connection. + * service that your client has permission to bind to; {@code false} + * if the system couldn't find the service or if your client doesn't + * have permission to bind to it. Regardless of the return value, you + * should later call {@link #unbindService} to release the connection. * - * @throws SecurityException If the caller does not have permission to access the service - * or the service can not be found. + * @throws SecurityException If the caller does not have permission to + * access the service or the service cannot be found. Call + * {@link #unbindService} to release the connection when this exception + * is thrown. * * @see #unbindService * @see #startService - * @see #BIND_AUTO_CREATE - * @see #BIND_DEBUG_UNBIND - * @see #BIND_NOT_FOREGROUND - * @see #BIND_ABOVE_CLIENT - * @see #BIND_ALLOW_OOM_MANAGEMENT - * @see #BIND_WAIVE_PRIORITY - * @see #BIND_IMPORTANT - * @see #BIND_ADJUST_WITH_ACTIVITY - * @see #BIND_NOT_PERCEPTIBLE - * @see #BIND_INCLUDE_CAPABILITIES */ public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags); /** - * Same as {@link #bindService(Intent, ServiceConnection, int)} with executor to control - * ServiceConnection callbacks. + * Same as {@link #bindService(Intent, ServiceConnection, int) + * bindService(Intent, ServiceConnection, int)} with executor to control ServiceConnection + * callbacks. + * * @param executor Callbacks on ServiceConnection will be called on executor. Must use same * instance for the same instance of ServiceConnection. + * + * @return The result of the binding as described in + * {@link #bindService(Intent, ServiceConnection, int) + * bindService(Intent, ServiceConnection, int)}. */ public boolean bindService(@RequiresPermission @NonNull Intent service, @BindServiceFlags int flags, @NonNull @CallbackExecutor Executor executor, @@ -3530,12 +3533,13 @@ public abstract class Context { * @param instanceName Unique identifier for the service instance. Each unique * name here will result in a different service instance being created. Identifiers * must only contain ASCII letters, digits, underscores, and periods. - * @return Returns success of binding as per {@link #bindService}. * @param executor Callbacks on ServiceConnection will be called on executor. * Must use same instance for the same instance of ServiceConnection. * @param conn Receives information as the service is started and stopped. * This must be a valid ServiceConnection object; it must not be null. * + * @return Returns success of binding as per {@link #bindService}. + * * @throws SecurityException If the caller does not have permission to access the service * @throws IllegalArgumentException If the instanceName is invalid. * @@ -3550,8 +3554,7 @@ public abstract class Context { } /** - * Binds to a service in the given {@code user} in the same manner as - * {@link #bindService(Intent, ServiceConnection, int)}. + * Binds to a service in the given {@code user} in the same manner as {@link #bindService}. * * <p>If the given {@code user} is in the same profile group and the target package is the * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java index 21398f6e6473..660a7f0acbba 100644 --- a/core/java/android/content/ServiceConnection.java +++ b/core/java/android/content/ServiceConnection.java @@ -63,8 +63,12 @@ public interface ServiceConnection { * happen, for example, if the application hosting the service it is bound to * has been updated. * - * @param name The concrete component name of the service whose - * connection is dead. + * <p class="note"><b>Note:</b> The app that requested the binding must call + * {@link Context#unbindService(ServiceConnection)} to release the tracking + * resources associated with this ServiceConnection even if this callback was + * invoked following {@link Context#bindService Context.bindService() bindService()}. + * + * @param name The concrete component name of the service whose connection is dead. */ default void onBindingDied(ComponentName name) { } @@ -72,10 +76,10 @@ public interface ServiceConnection { /** * Called when the service being bound has returned {@code null} from its * {@link android.app.Service#onBind(Intent) onBind()} method. This indicates - * that the attempting service binding represented by this ServiceConnection + * that the attempted service binding represented by this ServiceConnection * will never become usable. * - * <p class="note">The app which requested the binding must still call + * <p class="note"><b>Note:</b> The app that requested the binding must still call * {@link Context#unbindService(ServiceConnection)} to release the tracking * resources associated with this ServiceConnection even if this callback was * invoked following {@link Context#bindService Context.bindService() bindService()}. diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index e3ff0891cc95..ee62170dbfd5 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -638,7 +638,7 @@ public abstract class SensorManager { /** * Unregisters a listener for the sensors with which it is registered. * - * <p class="note"></p> + * <p class="note"> * Note: Don't use this method with a one shot trigger sensor such as * {@link Sensor#TYPE_SIGNIFICANT_MOTION}. * Use {@link #cancelTriggerSensor(TriggerEventListener, Sensor)} instead. diff --git a/core/java/android/net/PlatformVpnProfile.java b/core/java/android/net/PlatformVpnProfile.java index c0fb4cf4f3dd..6b6f1cad2cd8 100644 --- a/core/java/android/net/PlatformVpnProfile.java +++ b/core/java/android/net/PlatformVpnProfile.java @@ -100,7 +100,7 @@ public abstract class PlatformVpnProfile { * always gain the {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} capability * immediately after it connects, whether it can reach public Internet destinations or not. */ - public final boolean getRequiresInternetValidation() { + public final boolean isInternetValidationRequired() { return mRequiresInternetValidation; } diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index 779d931245c8..c51444cd31b6 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -108,7 +108,7 @@ public class VpnManager { * <ul> * <li>{@link #EXTRA_SESSION_KEY}, a {@code String} for the session key, as returned by * {@link #startProvisionedVpnProfileSession}. - * <li>{@link #EXTRA_TIMESTAMP}, a long for the timestamp at which the error occurred, + * <li>{@link #EXTRA_TIMESTAMP_MILLIS}, a long for the timestamp at which the error occurred, * in milliseconds since the epoch, as returned by * {@link java.lang.System#currentTimeMillis}. * <li>{@link #EXTRA_UNDERLYING_NETWORK}, a {@link Network} containing the underlying @@ -196,7 +196,7 @@ public class VpnManager { * This is a number of milliseconds since the epoch, suitable to be compared with * {@link java.lang.System#currentTimeMillis}. */ - public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP"; + public static final String EXTRA_TIMESTAMP_MILLIS = "android.net.extra.TIMESTAMP_MILLIS"; /** * Extra for the error class, as an {@code int}. diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java index 9772bde94ac9..2dd3aaa1f55a 100644 --- a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java +++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java @@ -24,6 +24,7 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.NonNull; import android.annotation.SystemApi; @@ -108,6 +109,7 @@ public class NetworkStatsDataMigrationUtils { static final int VERSION_ADD_METERED = 4; static final int VERSION_ADD_DEFAULT_NETWORK = 5; static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6; + static final int VERSION_ADD_SUB_ID = 7; } /** @@ -448,6 +450,13 @@ public class NetworkStatsDataMigrationUtils { oemNetCapabilities = NetworkTemplate.OEM_MANAGED_NO; } + final int subId; + if (version >= IdentitySetVersion.VERSION_ADD_SUB_ID) { + subId = in.readInt(); + } else { + subId = INVALID_SUBSCRIPTION_ID; + } + // Legacy files might contain TYPE_MOBILE_* types which were deprecated in later // releases. For backward compatibility, record them as TYPE_MOBILE instead. final int collapsedLegacyType = getCollapsedLegacyType(type); @@ -457,7 +466,8 @@ public class NetworkStatsDataMigrationUtils { .setWifiNetworkKey(networkId) .setRoaming(roaming).setMetered(metered) .setDefaultNetwork(defaultNetwork) - .setOemManaged(oemNetCapabilities); + .setOemManaged(oemNetCapabilities) + .setSubId(subId); if (type == TYPE_MOBILE && ratType != NetworkTemplate.NETWORK_TYPE_ALL) { builder.setRatType(ratType); } @@ -501,10 +511,10 @@ public class NetworkStatsDataMigrationUtils { * This is copied from {@code NetworkStatsCollection#readLegacyUid}. * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}. * - * @param taggedData whether to read tagged data. For legacy uid files, the tagged - * data was stored in the same binary file with non-tagged data. - * But in later releases, these data should be kept in different - * recorders. + * @param taggedData whether to read only tagged data (true) or only non-tagged data + * (false). For legacy uid files, the tagged data was stored in + * the same binary file with non-tagged data. But in later releases, + * these data should be kept in different recorders. * @hide */ @VisibleForTesting diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index 0a9fe90f2524..2b34d8639235 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -25,6 +25,7 @@ import android.app.Activity; import android.app.ActivityThread; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.nfc.INfcCardEmulation; @@ -62,7 +63,9 @@ public final class CardEmulation { * replace the current default service with the service * identified with the ComponentName specified in * {@link #EXTRA_SERVICE_COMPONENT}, for the category - * specified in {@link #EXTRA_CATEGORY} + * specified in {@link #EXTRA_CATEGORY}. There is an optional + * extra field using {@link Intent#EXTRA_USER} to specify + * the {@link UserHandle} of the user that owns the app. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CHANGE_DEFAULT = @@ -84,13 +87,6 @@ public final class CardEmulation { public static final String EXTRA_SERVICE_COMPONENT = "component"; /** - * The caller userId extra for {@link #ACTION_CHANGE_DEFAULT}. - * - * @see #ACTION_CHANGE_DEFAULT - */ - public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID"; - - /** * Category used for NFC payment services. */ public static final String CATEGORY_PAYMENT = "payment"; diff --git a/apex/media/aidl/stable/android/media/Session2Token.aidl b/core/java/android/os/BadTypeParcelableException.java index c5980e9e77fd..2ca3bd2adca1 100644 --- a/apex/media/aidl/stable/android/media/Session2Token.aidl +++ b/core/java/android/os/BadTypeParcelableException.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,6 +14,17 @@ * limitations under the License. */ -package android.media; +package android.os; -parcelable Session2Token; +/** Used by Parcel to signal that the type on the payload was not expected by the caller. */ +class BadTypeParcelableException extends BadParcelableException { + BadTypeParcelableException(String msg) { + super(msg); + } + BadTypeParcelableException(Exception cause) { + super(cause); + } + BadTypeParcelableException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index ad3de25fecc2..e5dab0539a8e 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -16,6 +16,8 @@ package android.os; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; @@ -31,7 +33,7 @@ import com.android.internal.util.IndentingPrintWriter; import java.io.Serializable; import java.util.ArrayList; import java.util.Set; -import java.util.function.Supplier; +import java.util.function.BiFunction; /** * A mapping from String keys to values of various types. In most cases, you @@ -252,11 +254,10 @@ public class BaseBundle { if (size == 0) { return null; } - Object o = getValueAt(0); try { - return (String) o; - } catch (ClassCastException e) { - typeWarning("getPairValue()", o, "String", e); + return getValueAt(0, String.class); + } catch (ClassCastException | BadTypeParcelableException e) { + typeWarning("getPairValue()", "String", e); return null; } } @@ -309,7 +310,7 @@ public class BaseBundle { } for (int i = 0, n = mMap.size(); i < n; i++) { // Triggers deserialization of i-th item, if needed - getValueAt(i); + getValueAt(i, /* clazz */ null); } } } @@ -321,26 +322,59 @@ public class BaseBundle { * This call should always be made after {@link #unparcel()} or inside a lock after making sure * {@code mMap} is not null. * + * @deprecated Use {@link #getValue(String, Class, Class[])}. This method should only be used in + * other deprecated APIs. + * * @hide */ + @Deprecated + @Nullable final Object getValue(String key) { + return getValue(key, /* clazz */ null); + } + + /** Same as {@link #getValue(String, Class, Class[])} with no item types. */ + @Nullable + final <T> T getValue(String key, @Nullable Class<T> clazz) { + // Avoids allocating Class[0] array + return getValue(key, clazz, (Class<?>[]) null); + } + + /** + * Returns the value for key {@code key} for expected return type {@code clazz} (or pass {@code + * null} for no type check). + * + * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}. + * + * This call should always be made after {@link #unparcel()} or inside a lock after making sure + * {@code mMap} is not null. + * + * @hide + */ + @Nullable + final <T> T getValue(String key, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) { int i = mMap.indexOfKey(key); - return (i >= 0) ? getValueAt(i) : null; + return (i >= 0) ? getValueAt(i, clazz, itemTypes) : null; } /** - * Returns the value for a certain position in the array map. + * Returns the value for a certain position in the array map for expected return type {@code + * clazz} (or pass {@code null} for no type check). + * + * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}. * * 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) { + @SuppressWarnings("unchecked") + @Nullable + final <T> T getValueAt(int i, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) { Object object = mMap.valueAt(i); - if (object instanceof Supplier<?>) { + if (object instanceof BiFunction<?, ?, ?>) { try { - object = ((Supplier<?>) object).get(); + object = ((BiFunction<Class<?>, Class<?>[], ?>) object).apply(clazz, itemTypes); } catch (BadParcelableException e) { if (sShouldDefuse) { Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e); @@ -351,7 +385,7 @@ public class BaseBundle { } mMap.setValueAt(i, object); } - return object; + return (clazz != null) ? clazz.cast(object) : (T) object; } private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel, @@ -528,7 +562,7 @@ public class BaseBundle { } else { // Following semantic above of failing in case we get a serialized value vs a // deserialized one, we'll compare the map. If a certain element hasn't been - // deserialized yet, it's a Supplier (or more specifically a LazyValue, but let's + // deserialized yet, it's a function object (or more specifically a LazyValue, but let's // pretend we don't know that here :P), we'll use that element's equality comparison as // map naturally does. That will takes care of comparing the payload if needed (see // Parcel.readLazyValue() for details). @@ -602,7 +636,11 @@ public class BaseBundle { * * @param key a String key * @return an Object, or null + * + * @deprecated Use the type-safe specific APIs depending on the type of the item to be + * retrieved, eg. {@link #getString(String)}. */ + @Deprecated @Nullable public Object get(String key) { unparcel(); @@ -610,6 +648,32 @@ public class BaseBundle { } /** + * Returns the object of type {@code clazz} for the given {@code key}, or {@code null} if: + * <ul> + * <li>No mapping of the desired type exists for the given key. + * <li>A {@code null} value is explicitly associated with the key. + * <li>The object is not of type {@code clazz}. + * </ul> + * + * <p>Use the more specific APIs where possible, especially in the case of containers such as + * lists, since those APIs allow you to specify the type of the items. + * + * @param key String key + * @param clazz The type of the object expected + * @return an Object, or null + */ + @Nullable + <T> T get(@Nullable String key, @NonNull Class<T> clazz) { + unparcel(); + try { + return getValue(key, requireNonNull(clazz)); + } catch (ClassCastException | BadTypeParcelableException e) { + typeWarning(key, clazz.getCanonicalName(), e); + return null; + } + } + + /** * Removes any entry with the given key from the mapping of this Bundle. * * @param key a String key @@ -982,15 +1046,19 @@ public class BaseBundle { } // Log a message if the value was non-null but not of the expected type - void typeWarning(String key, Object value, String className, - Object defaultValue, ClassCastException e) { + void typeWarning(String key, @Nullable Object value, String className, + Object defaultValue, RuntimeException e) { StringBuilder sb = new StringBuilder(); sb.append("Key "); sb.append(key); sb.append(" expected "); sb.append(className); - sb.append(" but value was a "); - sb.append(value.getClass().getName()); + if (value != null) { + sb.append(" but value was a "); + sb.append(value.getClass().getName()); + } else { + sb.append(" but value was of a different type"); + } sb.append(". The default value "); sb.append(defaultValue); sb.append(" was returned."); @@ -998,11 +1066,14 @@ public class BaseBundle { Log.w(TAG, "Attempt to cast generated internal exception:", e); } - void typeWarning(String key, Object value, String className, - ClassCastException e) { + void typeWarning(String key, @Nullable Object value, String className, RuntimeException e) { typeWarning(key, value, className, "<null>", e); } + void typeWarning(String key, String className, RuntimeException e) { + typeWarning(key, /* value */ null, className, "<null>", e); + } + /** * Returns the value associated with the given key, or defaultValue if * no mapping of the desired type exists for the given key. @@ -1342,7 +1413,11 @@ public class BaseBundle { * * @param key a String, or null * @return a Serializable value, or null + * + * @deprecated Use {@link #getSerializable(String, Class)}. This method should only be used in + * other deprecated APIs. */ + @Deprecated @Nullable Serializable getSerializable(@Nullable String key) { unparcel(); @@ -1359,6 +1434,36 @@ public class BaseBundle { } /** + * Returns the value associated with the given key, or {@code null} if: + * <ul> + * <li>No mapping of the desired type exists for the given key. + * <li>A {@code null} value is explicitly associated with the key. + * <li>The object is not of type {@code clazz}. + * </ul> + * + * @param key a String, or null + * @param clazz The expected class of the returned type + * @return a Serializable value, or null + */ + @Nullable + <T extends Serializable> T getSerializable(@Nullable String key, @NonNull Class<T> clazz) { + return get(key, clazz); + } + + + @SuppressWarnings("unchecked") + @Nullable + <T> ArrayList<T> getArrayList(@Nullable String key, @NonNull Class<? extends T> clazz) { + unparcel(); + try { + return getValue(key, ArrayList.class, requireNonNull(clazz)); + } catch (ClassCastException | BadTypeParcelableException e) { + typeWarning(key, "ArrayList<" + clazz.getCanonicalName() + ">", e); + return null; + } + } + + /** * Returns the value associated with the given key, or null if * no mapping of the desired type exists for the given key or a null * value is explicitly associated with the key. @@ -1368,17 +1473,7 @@ public class BaseBundle { */ @Nullable ArrayList<Integer> getIntegerArrayList(@Nullable String key) { - unparcel(); - Object o = getValue(key); - if (o == null) { - return null; - } - try { - return (ArrayList<Integer>) o; - } catch (ClassCastException e) { - typeWarning(key, o, "ArrayList<Integer>", e); - return null; - } + return getArrayList(key, Integer.class); } /** @@ -1391,17 +1486,7 @@ public class BaseBundle { */ @Nullable ArrayList<String> getStringArrayList(@Nullable String key) { - unparcel(); - Object o = getValue(key); - if (o == null) { - return null; - } - try { - return (ArrayList<String>) o; - } catch (ClassCastException e) { - typeWarning(key, o, "ArrayList<String>", e); - return null; - } + return getArrayList(key, String.class); } /** @@ -1414,17 +1499,7 @@ public class BaseBundle { */ @Nullable ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) { - unparcel(); - Object o = getValue(key); - if (o == null) { - return null; - } - try { - return (ArrayList<CharSequence>) o; - } catch (ClassCastException e) { - typeWarning(key, o, "ArrayList<CharSequence>", e); - return null; - } + return getArrayList(key, CharSequence.class); } /** diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 545ae38c38c0..03492aa56631 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -285,13 +285,20 @@ public class Build { public static final String RELEASE = getString("ro.build.version.release"); /** - * The version string we show to the user; may be {@link #RELEASE} or - * {@link #CODENAME} if not a final release build. + * The version string. May be {@link #RELEASE} or {@link #CODENAME} if + * not a final release build. */ @NonNull public static final String RELEASE_OR_CODENAME = getString( "ro.build.version.release_or_codename"); /** + * The version string we show to the user; may be {@link #RELEASE} or + * a descriptive string if not a final release build. + */ + @NonNull public static final String RELEASE_OR_PREVIEW_DISPLAY = getString( + "ro.build.version.release_or_preview_display"); + + /** * The base OS build the product is based on. */ public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", ""); diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index b2bbfd6c163d..a19b51b7811b 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -16,7 +16,11 @@ package android.os; +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.compat.annotation.UnsupportedAppUsage; import android.util.ArrayMap; import android.util.Size; @@ -873,7 +877,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { @Nullable public Bundle getBundle(@Nullable String key) { unparcel(); - Object o = getValue(key); + Object o = mMap.get(key); if (o == null) { return null; } @@ -896,7 +900,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { * * @param key a String, or {@code null} * @return a Parcelable value, or {@code null} + * + * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android + * {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable public <T extends Parcelable> T getParcelable(@Nullable String key) { unparcel(); @@ -913,6 +921,31 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Returns the value associated with the given key or {@code null} if: + * <ul> + * <li>No mapping of the desired type exists for the given key. + * <li>A {@code null} value is explicitly associated with the key. + * <li>The object is not of type {@code clazz}. + * </ul> + * + * <p><b>Note: </b> if the expected value is not a class provided by the Android platform, + * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first. + * Otherwise, this method might throw an exception or return {@code null}. + * + * @param key a String, or {@code null} + * @param clazz The type of the object expected + * @return a Parcelable value, or {@code null} + */ + @SuppressWarnings("unchecked") + @Nullable + public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) { + // The reason for not using <T extends Parcelable> is because the caller could provide a + // super class to restrict the children that doesn't implement Parcelable itself while the + // children do, more details at b/210800751 (same reasoning applies here). + return get(key, clazz); + } + + /** * Returns the value associated with the given key, or {@code null} if * no mapping of the desired type exists for the given key or a null * value is explicitly associated with the key. @@ -923,7 +956,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { * * @param key a String, or {@code null} * @return a Parcelable[] value, or {@code null} + * + * @deprecated Use the type-safer {@link #getParcelableArray(String, Class)} starting from + * Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable public Parcelable[] getParcelableArray(@Nullable String key) { unparcel(); @@ -940,6 +977,39 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Returns the value associated with the given key, or {@code null} if: + * <ul> + * <li>No mapping of the desired type exists for the given key. + * <li>A {@code null} value is explicitly associated with the key. + * <li>The object is not of type {@code clazz}. + * </ul> + * + * <p><b>Note: </b> if the expected value is not a class provided by the Android platform, + * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first. + * Otherwise, this method might throw an exception or return {@code null}. + * + * @param key a String, or {@code null} + * @param clazz The type of the items inside the array + * @return a Parcelable[] value, or {@code null} + */ + @SuppressLint({"ArrayReturn", "NullableCollection"}) + @SuppressWarnings("unchecked") + @Nullable + public <T> T[] getParcelableArray(@Nullable String key, @NonNull Class<T> clazz) { + // The reason for not using <T extends Parcelable> is because the caller could provide a + // super class to restrict the children that doesn't implement Parcelable itself while the + // children do, more details at b/210800751 (same reasoning applies here). + unparcel(); + try { + // In Java 12, we can pass clazz.arrayType() instead of Parcelable[] and later casting. + return (T[]) getValue(key, Parcelable[].class, requireNonNull(clazz)); + } catch (ClassCastException | BadTypeParcelableException e) { + typeWarning(key, clazz.getCanonicalName() + "[]", e); + return null; + } + } + + /** * Returns the value associated with the given key, or {@code null} if * no mapping of the desired type exists for the given key or a {@code null} * value is explicitly associated with the key. @@ -950,7 +1020,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { * * @param key a String, or {@code null} * @return an ArrayList<T> value, or {@code null} + * + * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android + * {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) { unparcel(); @@ -967,14 +1041,44 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Returns the value associated with the given key, or {@code null} if: + * <ul> + * <li>No mapping of the desired type exists for the given key. + * <li>A {@code null} value is explicitly associated with the key. + * <li>The object is not of type {@code clazz}. + * </ul> + * + * <p><b>Note: </b> if the expected value is not a class provided by the Android platform, + * you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first. + * Otherwise, this method might throw an exception or return {@code null}. + * + * @param key a String, or {@code null} + * @param clazz The type of the items inside the array list + * @return an ArrayList<T> value, or {@code null} + */ + @SuppressLint("NullableCollection") + @SuppressWarnings("unchecked") + @Nullable + public <T> ArrayList<T> getParcelableArrayList(@Nullable String key, + @NonNull Class<? extends T> clazz) { + // The reason for not using <T extends Parcelable> is because the caller could provide a + // super class to restrict the children that doesn't implement Parcelable itself while the + // children do, more details at b/210800751 (same reasoning applies here). + return getArrayList(key, clazz); + } + + /** * Returns the value associated with the given key, or null if * no mapping of the desired type exists for the given key or a null * value is explicitly associated with the key. * * @param key a String, or null - * * @return a SparseArray of T values, or null + * + * @deprecated Use the type-safer {@link #getSparseParcelableArray(String, Class)} starting from + * Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) { unparcel(); @@ -991,13 +1095,44 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Returns the value associated with the given key, or {@code null} if: + * <ul> + * <li>No mapping of the desired type exists for the given key. + * <li>A {@code null} value is explicitly associated with the key. + * <li>The object is not of type {@code clazz}. + * </ul> + * + * @param key a String, or null + * @return a SparseArray of T values, or null + */ + @SuppressWarnings("unchecked") + @Nullable + public <T> SparseArray<T> getSparseParcelableArray(@Nullable String key, + @NonNull Class<? extends T> clazz) { + // The reason for not using <T extends Parcelable> is because the caller could provide a + // super class to restrict the children that doesn't implement Parcelable itself while the + // children do, more details at b/210800751 (same reasoning applies here). + unparcel(); + try { + return (SparseArray<T>) getValue(key, SparseArray.class, requireNonNull(clazz)); + } catch (ClassCastException | BadTypeParcelableException e) { + typeWarning(key, "SparseArray<" + clazz.getCanonicalName() + ">", e); + return null; + } + } + + /** * Returns the value associated with the given key, or null if * no mapping of the desired type exists for the given key or a null * value is explicitly associated with the key. * * @param key a String, or null * @return a Serializable value, or null + * + * @deprecated Use the type-safer {@link #getSerializable(String, Class)} starting from Android + * {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Override @Nullable public Serializable getSerializable(@Nullable String key) { @@ -1005,6 +1140,24 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Returns the value associated with the given key, or {@code null} if: + * <ul> + * <li>No mapping of the desired type exists for the given key. + * <li>A {@code null} value is explicitly associated with the key. + * <li>The object is not of type {@code clazz}. + * </ul> + * + * @param key a String, or null + * @param clazz The expected class of the returned type + * @return a Serializable value, or null + */ + @Nullable + public <T extends Serializable> T getSerializable(@Nullable String key, + @NonNull Class<T> clazz) { + return super.getSerializable(key, requireNonNull(clazz)); + } + + /** * Returns the value associated with the given key, or null if * no mapping of the desired type exists for the given key or a null * value is explicitly associated with the key. diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 2ed0bad69460..2f2f65bbe9d2 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -101,6 +101,7 @@ public class Environment { private static final File DIR_ANDROID_EXPAND = getDirectory(ENV_ANDROID_EXPAND, "/mnt/expand"); private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage"); private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache"); + private static final File DIR_METADATA = new File("/metadata"); private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem"); private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm"); private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor"); @@ -1102,6 +1103,15 @@ public class Environment { } /** + * Return the metadata directory. + * + * @hide + */ + public static @NonNull File getMetadataDirectory() { + return DIR_METADATA; + } + + /** * Unknown storage state, such as when a path isn't backed by known storage * media. * diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 5d9f2189df1b..8e5ed8f6e578 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -52,6 +52,9 @@ per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS per-file *Telephony* = file:/telephony/OWNERS per-file *Zygote* = file:/ZYGOTE_OWNERS +# Time +per-file *Clock* = file:/services/core/java/com/android/server/timezonedetector/OWNERS + # RecoverySystem per-file *Recovery* = file:/services/core/java/com/android/server/recoverysystem/OWNERS diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 13d1d96fadc8..9b03172a45d6 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -16,6 +16,8 @@ package android.os; +import static com.android.internal.util.Preconditions.checkArgument; + import static java.util.Objects.requireNonNull; import android.annotation.IntDef; @@ -65,6 +67,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; @@ -198,7 +201,7 @@ import java.util.function.Supplier; * The methods to use are {@link #writeFileDescriptor(FileDescriptor)}, * {@link #readFileDescriptor()}. * - * <h3>Untyped Containers</h3> + * <h3>Parcelable Containers</h3> * * <p>A final class of methods are for writing and reading standard Java * containers of arbitrary types. These all revolve around the @@ -210,6 +213,19 @@ import java.util.function.Supplier; * {@link #writeMap(Map)}, {@link #readMap(Map, ClassLoader)}, * {@link #writeSparseArray(SparseArray)}, * {@link #readSparseArray(ClassLoader)}. + * + * <h3>Restricted Parcelable Containers</h3> + * + * <p>A final class of methods are for reading standard Java containers of restricted types. + * These methods replace methods for reading containers of arbitrary types from previous section + * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. The pairing writing methods are + * still the same from previous section. + * These methods accepts additional {@code clazz} parameters as the required types. + * The Restricted Parcelable container methods are {@link #readArray(ClassLoader, Class)}, + * {@link #readList(List, ClassLoader, Class)}, + * {@link #readArrayList(ClassLoader, Class)}, + * {@link #readMap(Map, ClassLoader, Class, Class)}, + * {@link #readSparseArray(ClassLoader, Class)}. */ public final class Parcel { @@ -287,26 +303,26 @@ public final class Parcel { private static final int VAL_NULL = -1; private static final int VAL_STRING = 0; private static final int VAL_INTEGER = 1; - private static final int VAL_MAP = 2; + private static final int VAL_MAP = 2; // length-prefixed private static final int VAL_BUNDLE = 3; - private static final int VAL_PARCELABLE = 4; + private static final int VAL_PARCELABLE = 4; // length-prefixed private static final int VAL_SHORT = 5; private static final int VAL_LONG = 6; private static final int VAL_FLOAT = 7; private static final int VAL_DOUBLE = 8; private static final int VAL_BOOLEAN = 9; private static final int VAL_CHARSEQUENCE = 10; - private static final int VAL_LIST = 11; - private static final int VAL_SPARSEARRAY = 12; + private static final int VAL_LIST = 11; // length-prefixed + private static final int VAL_SPARSEARRAY = 12; // length-prefixed private static final int VAL_BYTEARRAY = 13; private static final int VAL_STRINGARRAY = 14; private static final int VAL_IBINDER = 15; - private static final int VAL_PARCELABLEARRAY = 16; - private static final int VAL_OBJECTARRAY = 17; + private static final int VAL_PARCELABLEARRAY = 16; // length-prefixed + private static final int VAL_OBJECTARRAY = 17; // length-prefixed private static final int VAL_INTARRAY = 18; private static final int VAL_LONGARRAY = 19; private static final int VAL_BYTE = 20; - private static final int VAL_SERIALIZABLE = 21; + private static final int VAL_SERIALIZABLE = 21; // length-prefixed private static final int VAL_SPARSEBOOLEANARRAY = 22; private static final int VAL_BOOLEANARRAY = 23; private static final int VAL_CHARSEQUENCEARRAY = 24; @@ -3170,8 +3186,7 @@ public final class Parcel { */ @Deprecated public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) { - int n = readInt(); - readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null); + readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null); } /** @@ -3186,8 +3201,7 @@ public final class Parcel { @NonNull Class<V> clazzValue) { Objects.requireNonNull(clazzKey); Objects.requireNonNull(clazzValue); - int n = readInt(); - readMapInternal(outVal, n, loader, clazzKey, clazzValue); + readMapInternal(outVal, loader, clazzKey, clazzValue); } /** @@ -3236,13 +3250,7 @@ public final class Parcel { @Deprecated @Nullable public HashMap readHashMap(@Nullable ClassLoader loader) { - int n = readInt(); - if (n < 0) { - return null; - } - HashMap m = new HashMap(n); - readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null); - return m; + return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null); } /** @@ -3258,13 +3266,7 @@ public final class Parcel { @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) { Objects.requireNonNull(clazzKey); Objects.requireNonNull(clazzValue); - int n = readInt(); - if (n < 0) { - return null; - } - HashMap<K, V> map = new HashMap<>(n); - readMapInternal(map, n, loader, clazzKey, clazzValue); - return map; + return readHashMapInternal(loader, clazzKey, clazzValue); } /** @@ -3843,7 +3845,7 @@ public final class Parcel { */ @NonNull public <T> List<T> readParcelableList(@NonNull List<T> list, - @Nullable ClassLoader cl, @NonNull Class<T> clazz) { + @Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) { Objects.requireNonNull(list); Objects.requireNonNull(clazz); return readParcelableListInternal(list, cl, clazz); @@ -3854,7 +3856,7 @@ public final class Parcel { */ @NonNull private <T> List<T> readParcelableListInternal(@NonNull List<T> list, - @Nullable ClassLoader cl, @Nullable Class<T> clazz) { + @Nullable ClassLoader cl, @Nullable Class<? extends T> clazz) { final int n = readInt(); if (n == -1) { list.clear(); @@ -4289,16 +4291,17 @@ public final class Parcel { /** - * @param clazz The type of the object expected or {@code null} for performing no checks. + * @see #readValue(int, ClassLoader, Class, Class[]) */ @Nullable - private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) { + private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz, + @Nullable Class<?>... itemTypes) { int type = readInt(); final T object; if (isLengthPrefixed(type)) { int length = readInt(); int start = dataPosition(); - object = readValue(type, loader, clazz); + object = readValue(type, loader, clazz, itemTypes); int actual = dataPosition() - start; if (actual != length) { Slog.wtfStack(TAG, @@ -4306,24 +4309,26 @@ public final class Parcel { + " consumed " + actual + " bytes, but " + length + " expected."); } } else { - object = readValue(type, loader, clazz); + object = readValue(type, loader, clazz, itemTypes); } return object; } /** - * This will return a {@link Supplier} for length-prefixed types that deserializes the object - * when {@link Supplier#get()} is called, for other types it will return the object itself. + * This will return a {@link BiFunction} for length-prefixed types that deserializes the object + * when {@link BiFunction#apply} is called (the arguments correspond to the ones of {@link + * #readValue(int, ClassLoader, Class, Class[])} after the class loader), for other types it + * will return the object itself. * - * <p>After calling {@link Supplier#get()} the parcel cursor will not change. Note that you + * <p>After calling {@link BiFunction#apply} the parcel cursor will not change. Note that you * shouldn't recycle the parcel, not at least until all objects have been retrieved. No * synchronization attempts are made. * - * </p>The supplier returned implements {@link #equals(Object)} and {@link #hashCode()}. Two - * suppliers are equal if either of the following is true: + * </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two + * function objects are equal if either of the following is true: * <ul> - * <li>{@link Supplier#get()} has been called on both and both objects returned are equal. - * <li>{@link Supplier#get()} hasn't been called on either one and everything below is true: + * <li>{@link BiFunction#apply} has been called on both and both objects returned are equal. + * <li>{@link BiFunction#apply} hasn't been called on either one and everything below is true: * <ul> * <li>The {@code loader} parameters used to retrieve each are equal. * <li>They both have the same type. @@ -4350,7 +4355,7 @@ public final class Parcel { } - private static final class LazyValue implements Supplier<Object> { + private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> { /** * | 4B | 4B | * mSource = Parcel{... | type | length | object | ...} @@ -4382,7 +4387,7 @@ public final class Parcel { } @Override - public Object get() { + public Object apply(@Nullable Class<?> clazz, @Nullable Class<?>[] itemTypes) { Parcel source = mSource; if (source != null) { synchronized (source) { @@ -4391,7 +4396,7 @@ public final class Parcel { int restore = source.dataPosition(); try { source.setDataPosition(mPosition); - mObject = source.readValue(mLoader); + mObject = source.readValue(mLoader, clazz, itemTypes); } finally { source.setDataPosition(restore); } @@ -4471,14 +4476,25 @@ public final class Parcel { } } + /** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */ + private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) { + // Avoids allocating Class[0] array + return readValue(type, loader, clazz, (Class<?>[]) null); + } + /** * 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. + * @param itemTypes If the value is a container, these represent the item types (eg. for a list + * it's the item type, for a map, it's the key type, followed by the value + * type). */ @SuppressWarnings("unchecked") @Nullable - private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) { + private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz, + @Nullable Class<?>... itemTypes) { final Object object; switch (type) { case VAL_NULL: @@ -4494,7 +4510,11 @@ public final class Parcel { break; case VAL_MAP: - object = readHashMap(loader); + checkTypeToUnparcel(clazz, HashMap.class); + Class<?> keyType = ArrayUtils.getOrNull(itemTypes, 0); + Class<?> valueType = ArrayUtils.getOrNull(itemTypes, 1); + checkArgument((keyType == null) == (valueType == null)); + object = readHashMapInternal(loader, keyType, valueType); break; case VAL_PARCELABLE: @@ -4525,10 +4545,12 @@ public final class Parcel { object = readCharSequence(); break; - case VAL_LIST: - object = readArrayList(loader); + case VAL_LIST: { + checkTypeToUnparcel(clazz, ArrayList.class); + Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0); + object = readArrayListInternal(loader, itemType); break; - + } case VAL_BOOLEANARRAY: object = createBooleanArray(); break; @@ -4549,10 +4571,12 @@ public final class Parcel { object = readStrongBinder(); break; - case VAL_OBJECTARRAY: - object = readArray(loader); + case VAL_OBJECTARRAY: { + Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0); + checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Object.class); + object = readArrayInternal(loader, itemType); break; - + } case VAL_INTARRAY: object = createIntArray(); break; @@ -4569,14 +4593,18 @@ public final class Parcel { object = readSerializableInternal(loader, clazz); break; - case VAL_PARCELABLEARRAY: - object = readParcelableArray(loader); + case VAL_PARCELABLEARRAY: { + Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0); + checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Parcelable.class); + object = readParcelableArrayInternal(loader, itemType); break; - - case VAL_SPARSEARRAY: - object = readSparseArray(loader); + } + case VAL_SPARSEARRAY: { + checkTypeToUnparcel(clazz, SparseArray.class); + Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0); + object = readSparseArrayInternal(loader, itemType); break; - + } case VAL_SPARSEBOOLEANARRAY: object = readSparseBooleanArray(); break; @@ -4624,7 +4652,7 @@ public final class Parcel { + " at offset " + off); } if (object != null && clazz != null && !clazz.isInstance(object)) { - throw new BadParcelableException("Unparcelled object " + object + throw new BadTypeParcelableException("Unparcelled object " + object + " is not an instance of required class " + clazz.getName() + " provided in the parameter"); } @@ -4651,6 +4679,38 @@ public final class Parcel { } /** + * Checks that an array of type T[], where T is {@code componentTypeToUnparcel}, is a subtype of + * {@code requiredArrayType}. + */ + private void checkArrayTypeToUnparcel(@Nullable Class<?> requiredArrayType, + Class<?> componentTypeToUnparcel) { + if (requiredArrayType != null) { + // In Java 12, we could use componentTypeToUnparcel.arrayType() for the check + Class<?> requiredComponentType = requiredArrayType.getComponentType(); + if (requiredComponentType == null) { + throw new BadTypeParcelableException( + "About to unparcel an array but type " + + requiredArrayType.getCanonicalName() + + " required by caller is not an array."); + } + checkTypeToUnparcel(requiredComponentType, componentTypeToUnparcel); + } + } + + /** + * Checks that {@code typeToUnparcel} is a subtype of {@code requiredType}, if {@code + * requiredType} is not {@code null}. + */ + private void checkTypeToUnparcel(@Nullable Class<?> requiredType, Class<?> typeToUnparcel) { + if (requiredType != null && !requiredType.isAssignableFrom(typeToUnparcel)) { + throw new BadTypeParcelableException( + "About to unparcel a " + typeToUnparcel.getCanonicalName() + + ", which is not a subtype of type " + requiredType.getCanonicalName() + + " required by caller."); + } + } + + /** * Read and return a new Parcelable from the parcel. The given class loader * will be used to load any enclosed Parcelables. If it is null, the default * class loader will be used. @@ -4780,7 +4840,7 @@ public final class Parcel { if (clazz != null) { Class<?> parcelableClass = creator.getClass().getEnclosingClass(); if (!clazz.isAssignableFrom(parcelableClass)) { - throw new BadParcelableException("Parcelable creator " + name + " is not " + throw new BadTypeParcelableException("Parcelable creator " + name + " is not " + "a subclass of required class " + clazz.getName() + " provided in the parameter"); } @@ -4803,7 +4863,7 @@ public final class Parcel { } if (clazz != null) { if (!clazz.isAssignableFrom(parcelableClass)) { - throw new BadParcelableException("Parcelable creator " + name + " is not " + throw new BadTypeParcelableException("Parcelable creator " + name + " is not " + "a subclass of required class " + clazz.getName() + " provided in the parameter"); } @@ -4864,15 +4924,7 @@ public final class Parcel { @Deprecated @Nullable public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) { - int N = readInt(); - if (N < 0) { - return null; - } - Parcelable[] p = new Parcelable[N]; - for (int i = 0; i < N; i++) { - p[i] = readParcelable(loader); - } - return p; + return readParcelableArrayInternal(loader, /* clazz */ null); } /** @@ -4884,14 +4936,20 @@ public final class Parcel { * trying to instantiate an element. */ @SuppressLint({"ArrayReturn", "NullableCollection"}) - @SuppressWarnings("unchecked") @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) { + return readParcelableArrayInternal(loader, requireNonNull(clazz)); + } + + @SuppressWarnings("unchecked") + @Nullable + private <T> T[] readParcelableArrayInternal(@Nullable ClassLoader loader, + @Nullable Class<T> clazz) { int n = readInt(); if (n < 0) { return null; } - T[] p = (T[]) Array.newInstance(clazz, n); + T[] p = (T[]) ((clazz == null) ? new Parcelable[n] : Array.newInstance(clazz, n)); for (int i = 0; i < n; i++) { p[i] = readParcelableInternal(loader, clazz); } @@ -4954,7 +5012,7 @@ public final class Parcel { // the class the same way as ObjectInputStream, using the provided classloader. Class<?> cl = Class.forName(name, false, loader); if (!clazz.isAssignableFrom(cl)) { - throw new BadParcelableException("Serializable object " + throw new BadTypeParcelableException("Serializable object " + cl.getName() + " is not a subclass of required class " + clazz.getName() + " provided in the parameter"); } @@ -4979,7 +5037,7 @@ public final class Parcel { // the deserialized object, as we cannot resolve the class the same way as // ObjectInputStream. if (!clazz.isAssignableFrom(object.getClass())) { - throw new BadParcelableException("Serializable object " + throw new BadTypeParcelableException("Serializable object " + object.getClass().getName() + " is not a subclass of required class " + clazz.getName() + " provided in the parameter"); } @@ -5089,7 +5147,26 @@ public final class Parcel { readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null); } - /* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n, + @Nullable + private <K, V> HashMap<K, V> readHashMapInternal(@Nullable ClassLoader loader, + @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) { + int n = readInt(); + if (n < 0) { + return null; + } + HashMap<K, V> map = new HashMap<>(n); + readMapInternal(map, n, loader, clazzKey, clazzValue); + return map; + } + + private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, + @Nullable ClassLoader loader, @Nullable Class<K> clazzKey, + @Nullable Class<V> clazzValue) { + int n = readInt(); + readMapInternal(outVal, n, loader, clazzKey, clazzValue); + } + + private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n, @Nullable ClassLoader loader, @Nullable Class<K> clazzKey, @Nullable Class<V> clazzValue) { while (n > 0) { @@ -5100,7 +5177,7 @@ public final class Parcel { } } - /* package */ void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal, + private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal, int size, @Nullable ClassLoader loader) { readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader); } diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 944b71700450..a3b836adfc8b 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -674,10 +674,12 @@ public class RecoverySystem { } try { if (!rs.allocateSpaceForUpdate(packageFile)) { + rs.clearBcb(); throw new IOException("Failed to allocate space for update " + packageFile.getAbsolutePath()); } } catch (RemoteException e) { + rs.clearBcb(); e.rethrowAsRuntimeException(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d0ef546a8122..e79cc32ac99d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -238,6 +238,20 @@ public final class Settings { "android.settings.TETHER_PROVISIONING_UI"; /** + * Activity Action: Show a dialog activity to notify tethering is NOT supported by carrier. + * + * When {@link android.telephony.CarrierConfigManager#KEY_CARRIER_SUPPORTS_TETHERING_BOOL} + * is false, and tethering is started by Settings, this dialog activity will be started to + * tell the user that tethering is not supported by carrier. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + @SystemApi + public static final String ACTION_TETHER_UNSUPPORTED_CARRIER_UI = + "android.settings.TETHER_UNSUPPORTED_CARRIER_UI"; + + /** * Activity Action: Show settings to allow entering/exiting airplane mode. * <p> * In some cases, a matching Activity may not exist, so ensure you diff --git a/core/java/android/service/autofill/OWNERS b/core/java/android/service/autofill/OWNERS index a08863276da7..9a30e826a24f 100644 --- a/core/java/android/service/autofill/OWNERS +++ b/core/java/android/service/autofill/OWNERS @@ -1,9 +1,3 @@ # Bug component: 351486 -joannechung@google.com -adamhe@google.com -tymtsai@google.com -lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com +include /core/java/android/view/autofill/OWNERS diff --git a/core/java/android/service/contentcapture/OWNERS b/core/java/android/service/contentcapture/OWNERS index 6337327cec25..24561c59bba6 100644 --- a/core/java/android/service/contentcapture/OWNERS +++ b/core/java/android/service/contentcapture/OWNERS @@ -1,9 +1,3 @@ # Bug component: 544200 -joannechung@google.com -adamhe@google.com -tymtsai@google.com -lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com +include /core/java/android/view/contentcapture/OWNERS diff --git a/core/java/android/service/contentsuggestions/OWNERS b/core/java/android/service/contentsuggestions/OWNERS index 46b5ea03c545..72fe0b1c6392 100644 --- a/core/java/android/service/contentsuggestions/OWNERS +++ b/core/java/android/service/contentsuggestions/OWNERS @@ -1,7 +1,2 @@ -joannechung@google.com -adamhe@google.com -tymtsai@google.com -lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com + +include /core/java/android/app/contentsuggestions/OWNERS diff --git a/core/java/android/service/textclassifier/OWNERS b/core/java/android/service/textclassifier/OWNERS index a535f5258732..c85c69ef14df 100644 --- a/core/java/android/service/textclassifier/OWNERS +++ b/core/java/android/service/textclassifier/OWNERS @@ -1,9 +1,3 @@ # Bug component: 709498 -joannechung@google.com -adamhe@google.com -tymtsai@google.com -lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com +include /core/java/android/view/textclassifier/OWNERS diff --git a/core/java/android/service/tracing/TraceReportService.java b/core/java/android/service/tracing/TraceReportService.java index 3d16a3d41ea3..6fdc8e8eb961 100644 --- a/core/java/android/service/tracing/TraceReportService.java +++ b/core/java/android/service/tracing/TraceReportService.java @@ -112,7 +112,6 @@ public class TraceReportService extends Service { } } - // Methods to override. /** * Called when a trace is reported and sent to this class. * @@ -123,15 +122,10 @@ public class TraceReportService extends Service { public void onReportTrace(@NonNull TraceParams args) { } - // Optional methods to override. - // Realistically, these methods are internal implementation details but since this class is - // a SystemApi, it's better to err on the side of flexibility just in-case we need to override - // these methods down the line. - /** * Handles binder calls from system_server. */ - public boolean onMessage(@NonNull Message msg) { + private boolean onMessage(@NonNull Message msg) { if (msg.what == MSG_REPORT_TRACE) { if (!(msg.obj instanceof TraceReportParams)) { Log.e(TAG, "Received invalid type for report trace message."); @@ -153,10 +147,12 @@ public class TraceReportService extends Service { /** * Returns an IBinder for handling binder calls from system_server. + * + * @hide */ @Nullable @Override - public IBinder onBind(@NonNull Intent intent) { + public final IBinder onBind(@NonNull Intent intent) { if (mMessenger == null) { mMessenger = new Messenger(new Handler(Looper.getMainLooper(), this::onMessage)); } diff --git a/core/java/android/service/translation/OWNERS b/core/java/android/service/translation/OWNERS index a1e663aa8ff7..440f9a840057 100644 --- a/core/java/android/service/translation/OWNERS +++ b/core/java/android/service/translation/OWNERS @@ -1,8 +1,3 @@ # Bug component: 994311 -adamhe@google.com -augale@google.com -joannechung@google.com -lpeter@google.com -svetoslavganov@google.com -tymtsai@google.com +include /core/java/android/view/translation/OWNERS diff --git a/core/java/android/service/voice/OWNERS b/core/java/android/service/voice/OWNERS index 46b5ea03c545..59a0c2e36612 100644 --- a/core/java/android/service/voice/OWNERS +++ b/core/java/android/service/voice/OWNERS @@ -1,7 +1,3 @@ -joannechung@google.com -adamhe@google.com -tymtsai@google.com -lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com +# Bug component: 533220 + +include /core/java/android/app/assist/OWNERS diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index 4ac3178ecb4c..40beab323576 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -81,7 +81,16 @@ public class NtpTrustedTime implements TrustedTime { /** Calculates and returns the age of this result. */ public long getAgeMillis() { - return SystemClock.elapsedRealtime() - mElapsedRealtimeMillis; + return getAgeMillis(SystemClock.elapsedRealtime()); + } + + /** + * Calculates and returns the age of this result relative to currentElapsedRealtimeMillis. + * + * @param currentElapsedRealtimeMillis - reference elapsed real time + */ + public long getAgeMillis(long currentElapsedRealtimeMillis) { + return currentElapsedRealtimeMillis - mElapsedRealtimeMillis; } @Override @@ -256,6 +265,13 @@ public class NtpTrustedTime implements TrustedTime { return mTimeResult; } + /** Clears the last received NTP. Intended for use during tests. */ + public void clearCachedTimeResult() { + synchronized (this) { + mTimeResult = null; + } + } + private static class NtpConnectionInfo { @NonNull private final String mServer; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 00f4eb83bdaa..5b0bc4ad1aec 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3128,7 +3128,8 @@ public interface WindowManager extends ViewManager { /** * The window is allowed to extend into the {@link DisplayCutout} area, only if the - * {@link DisplayCutout} is fully contained within a system bar. Otherwise, the window is + * {@link DisplayCutout} is fully contained within a system bar or the {@link DisplayCutout} + * is not deeper than 16 dp, but this depends on the OEM choice. Otherwise, the window is * laid out such that it does not overlap with the {@link DisplayCutout} area. * * <p> @@ -3143,6 +3144,13 @@ public interface WindowManager extends ViewManager { * The usual precautions for not overlapping with the status and navigation bar are * sufficient for ensuring that no important content overlaps with the DisplayCutout. * + * <p> + * Note: OEMs can have an option to allow the window to always extend into the + * {@link DisplayCutout} area, no matter the cutout flag set, when the {@link DisplayCutout} + * is on the different side from system bars, only if the {@link DisplayCutout} overlaps at + * most 16dp with the windows. + * In such case, OEMs must provide an opt-in/out affordance for users. + * * @see DisplayCutout * @see WindowInsets * @see #layoutInDisplayCutoutMode @@ -3155,8 +3163,16 @@ public interface WindowManager extends ViewManager { * The window is always allowed to extend into the {@link DisplayCutout} areas on the short * edges of the screen. * + * <p> * The window will never extend into a {@link DisplayCutout} area on the long edges of the - * screen. + * screen, unless the {@link DisplayCutout} is not deeper than 16 dp, but this depends on + * the OEM choice. + * + * <p> + * Note: OEMs can have an option to allow the window to extend into the + * {@link DisplayCutout} area on the long edge side, only if the cutout overlaps at most + * 16dp with the windows. In such case, OEMs must provide an opt-in/out affordance for + * users. * * <p> * The window must make sure that no important content overlaps with the diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS index a08863276da7..108c42cdde2a 100644 --- a/core/java/android/view/autofill/OWNERS +++ b/core/java/android/view/autofill/OWNERS @@ -1,9 +1,7 @@ # Bug component: 351486 +augale@google.com joannechung@google.com -adamhe@google.com -tymtsai@google.com +markpun@google.com lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com +tymtsai@google.com diff --git a/core/java/android/view/contentcapture/OWNERS b/core/java/android/view/contentcapture/OWNERS index 6337327cec25..1a5cb1e4ca4a 100644 --- a/core/java/android/view/contentcapture/OWNERS +++ b/core/java/android/view/contentcapture/OWNERS @@ -1,9 +1,7 @@ # Bug component: 544200 +augale@google.com joannechung@google.com -adamhe@google.com -tymtsai@google.com +markpun@google.com lpeter@google.com -augale@google.com -svetoslavganov@android.com -svetoslavganov@google.com +tymtsai@google.com diff --git a/core/java/android/view/inputmethod/OWNERS b/core/java/android/view/inputmethod/OWNERS index d7db7c741364..9fa7e8f11364 100644 --- a/core/java/android/view/inputmethod/OWNERS +++ b/core/java/android/view/inputmethod/OWNERS @@ -3,4 +3,4 @@ set noparent include /services/core/java/com/android/server/inputmethod/OWNERS -per-file *InlineSuggestion* = file:/core/java/android/service/autofill/OWNERS +per-file *InlineSuggestion* = file:/core/java/android/view/autofill/OWNERS diff --git a/core/java/android/view/textclassifier/OWNERS b/core/java/android/view/textclassifier/OWNERS index 4bcdeea472e3..a205be2f39d0 100644 --- a/core/java/android/view/textclassifier/OWNERS +++ b/core/java/android/view/textclassifier/OWNERS @@ -2,8 +2,6 @@ mns@google.com toki@google.com -svetoslavganov@android.com -svetoslavganov@google.com augale@google.com joannechung@google.com tonymak@google.com diff --git a/core/java/android/view/textclassifier/logging/OWNERS b/core/java/android/view/textclassifier/logging/OWNERS deleted file mode 100644 index ac80d9f4cdd0..000000000000 --- a/core/java/android/view/textclassifier/logging/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -# Bug component: 709498 - -mns@google.com -toki@google.com -svetoslavganov@android.com -svetoslavganov@google.com -augale@google.com -joannechung@google.com diff --git a/core/java/android/view/translation/OWNERS b/core/java/android/view/translation/OWNERS index a1e663aa8ff7..b772ad3f7cab 100644 --- a/core/java/android/view/translation/OWNERS +++ b/core/java/android/view/translation/OWNERS @@ -1,8 +1,7 @@ # Bug component: 994311 -adamhe@google.com augale@google.com joannechung@google.com +markpun@google.com lpeter@google.com -svetoslavganov@google.com tymtsai@google.com diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 452dad3b79db..9791da5f41eb 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -12521,7 +12521,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Called when a context menu option for the text view is selected. Currently * this will be one of {@link android.R.id#selectAll}, {@link android.R.id#cut}, - * {@link android.R.id#copy}, {@link android.R.id#paste} or {@link android.R.id#shareText}. + * {@link android.R.id#copy}, {@link android.R.id#paste}, + * {@link android.R.id#pasteAsPlainText} (starting at API level 23) or + * {@link android.R.id#shareText}. * * @return true if the context menu item action was performed. */ @@ -12712,6 +12714,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * method. The default actions can also be removed from the menu using * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll}, * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste}, + * {@link android.R.id#pasteAsPlainText} (starting at API level 23), * {@link android.R.id#replaceText} or {@link android.R.id#shareText} ids as parameters. * * <p>Returning false from @@ -12750,7 +12753,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * {@link android.view.ActionMode.Callback#onPrepareActionMode(android.view.ActionMode, * android.view.Menu)} method. The default actions can also be removed from the menu using * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll}, - * {@link android.R.id#paste} or {@link android.R.id#replaceText} ids as parameters.</p> + * {@link android.R.id#paste}, {@link android.R.id#pasteAsPlainText} (starting at API + * level 23) or {@link android.R.id#replaceText} ids as parameters.</p> * * <p>Returning false from * {@link android.view.ActionMode.Callback#onCreateActionMode(android.view.ActionMode, diff --git a/core/java/android/widget/inline/OWNERS b/core/java/android/widget/inline/OWNERS new file mode 100644 index 000000000000..9a30e826a24f --- /dev/null +++ b/core/java/android/widget/inline/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 351486 + +include /core/java/android/view/autofill/OWNERS diff --git a/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java b/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java index 615e4b793752..a03bac45d14f 100644 --- a/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java +++ b/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java @@ -29,22 +29,66 @@ public final class SystemServerClassLoaderFactory { private static final ArrayMap<String, PathClassLoader> sLoadedPaths = new ArrayMap<>(); /** - * Creates and caches a ClassLoader for the jar at the given path, or returns a cached - * ClassLoader if it exists. + * Creates and caches a ClassLoader for the jar at the given path. + * + * This method should only be called by ZygoteInit to prefetch jars. For other users, use + * {@link getOrCreateClassLoader} instead. * * The parent class loader should always be the system server class loader. Changing it has * implications that require discussion with the mainline team. * * @hide for internal use only */ - public static PathClassLoader getOrCreateClassLoader(String path, ClassLoader parent) { - PathClassLoader pathClassLoader = sLoadedPaths.get(path); - if (pathClassLoader == null) { - pathClassLoader = (PathClassLoader) ClassLoaderFactory.createClassLoader( - path, /*librarySearchPath=*/null, /*libraryPermittedPath=*/null, parent, - Build.VERSION.SDK_INT, /*isNamespaceShared=*/true , /*classLoaderName=*/null); - sLoadedPaths.put(path, pathClassLoader); + /* package */ static PathClassLoader createClassLoader(String path, ClassLoader parent) { + if (sLoadedPaths.containsKey(path)) { + throw new IllegalStateException("A ClassLoader for " + path + " already exists"); } + PathClassLoader pathClassLoader = (PathClassLoader) ClassLoaderFactory.createClassLoader( + path, /*librarySearchPath=*/null, /*libraryPermittedPath=*/null, parent, + Build.VERSION.SDK_INT, /*isNamespaceShared=*/true , /*classLoaderName=*/null); + sLoadedPaths.put(path, pathClassLoader); return pathClassLoader; } + + /** + * Returns a cached ClassLoader to be used at runtime for the jar at the given path. Or, creates + * one if it is not prefetched and is allowed to be created at runtime. + * + * The parent class loader should always be the system server class loader. Changing it has + * implications that require discussion with the mainline team. + * + * @hide for internal use only + */ + public static PathClassLoader getOrCreateClassLoader( + String path, ClassLoader parent, boolean isTestOnly) { + PathClassLoader pathClassLoader = sLoadedPaths.get(path); + if (pathClassLoader != null) { + return pathClassLoader; + } + if (!allowClassLoaderCreation(path, isTestOnly)) { + throw new RuntimeException("Creating a ClassLoader from " + path + " is not allowed. " + + "Please make sure that the jar is listed in " + + "`PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS` in the Makefile and added as a " + + "`standalone_contents` of a `systemserverclasspath_fragment` in " + + "`Android.bp`."); + } + return createClassLoader(path, parent); + } + + /** + * Returns whether a class loader for the jar is allowed to be created at runtime. + */ + private static boolean allowClassLoaderCreation(String path, boolean isTestOnly) { + // Currently, we only enforce prefetching for APEX jars. + if (!path.startsWith("/apex/")) { + return true; + } + // APEXes for testing only are okay to ignore. + if (isTestOnly) { + return true; + } + return false; + } + + } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index f258f84c5e3e..e5e6949851b9 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -586,7 +586,7 @@ public class ZygoteInit { } for (String jar : envStr.split(":")) { try { - SystemServerClassLoaderFactory.getOrCreateClassLoader( + SystemServerClassLoaderFactory.createClassLoader( jar, getOrCreateSystemServerClassLoader()); } catch (Error e) { // We don't want the process to crash for this error because prefetching is just an diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index c6fd6eec2f91..55df09a95838 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -888,6 +888,15 @@ public class ArrayUtils { } } + /** + * Returns the {@code i}-th item in {@code items}, if it exists and {@code items} is not {@code + * null}, otherwise returns {@code null}. + */ + @Nullable + public static <T> T getOrNull(@Nullable T[] items, int i) { + return (items != null && items.length > i) ? items[i] : null; + } + public static @Nullable <T> T firstOrNull(T[] items) { return items.length > 0 ? items[0] : null; } diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index ae9d71610aaa..fb0b7fdd6ce3 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -112,17 +112,74 @@ public class SystemConfig { public final String name; public final String filename; public final String[] dependencies; + + /** + * SDK version this library was added to the BOOTCLASSPATH. + * + * <p>At the SDK level specified in this field and higher, the apps' uses-library tags for + * this library will be ignored, since the library is always available on BOOTCLASSPATH. + * + * <p>0 means not specified. + */ + public final int onBootclasspathSince; + + /** + * SDK version this library was removed from the BOOTCLASSPATH. + * + * <p>At the SDK level specified in this field and higher, this library needs to be + * explicitly added by apps. For compatibility reasons, when an app + * targets an SDK less than the value of this attribute, this library is automatically + * added. + * + * <p>0 means not specified. + */ + public final int onBootclasspathBefore; + + /** + * Declares whether this library can be safely ignored from <uses-library> tags. + * + * <p> This can happen if the library initially had to be explicitly depended-on using that + * tag but has since been moved to the BOOTCLASSPATH which means now is always available + * and the tag is no longer required. + */ + public final boolean canBeSafelyIgnored; + public final boolean isNative; - SharedLibraryEntry(String name, String filename, String[] dependencies) { - this(name, filename, dependencies, false /* isNative */); + + @VisibleForTesting + public SharedLibraryEntry(String name, String filename, String[] dependencies, + boolean isNative) { + this(name, filename, dependencies, 0 /* onBootclasspathSince */, + 0 /* onBootclasspathBefore */, isNative); } - SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) { + @VisibleForTesting + public SharedLibraryEntry(String name, String filename, String[] dependencies, + int onBootclasspathSince, int onBootclassPathBefore) { + this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore, + false /* isNative */); + } + + SharedLibraryEntry(String name, String filename, String[] dependencies, + int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) { this.name = name; this.filename = filename; this.dependencies = dependencies; + this.onBootclasspathSince = onBootclasspathSince; + this.onBootclasspathBefore = onBootclasspathBefore; this.isNative = isNative; + + canBeSafelyIgnored = this.onBootclasspathSince != 0 + && isSdkAtLeast(this.onBootclasspathSince); + } + + private static boolean isSdkAtLeast(int level) { + if ("REL".equals(Build.VERSION.CODENAME)) { + return Build.VERSION.SDK_INT >= level; + } + return level == Build.VERSION_CODES.CUR_DEVELOPMENT + || Build.VERSION.SDK_INT >= level; } } @@ -509,12 +566,14 @@ public class SystemConfig { } private void readAllPermissions() { + final XmlPullParser parser = Xml.newPullParser(); + // Read configuration from system - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL); // Read configuration from the old permissions dir - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL); // Vendors are only allowed to customize these @@ -524,18 +583,18 @@ public class SystemConfig { // For backward compatibility vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS); } - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag); - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag); String vendorSkuProperty = SystemProperties.get(VENDOR_SKU_PROPERTY, ""); if (!vendorSkuProperty.isEmpty()) { String vendorSkuDir = "sku_" + vendorSkuProperty; - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getVendorDirectory(), "etc", "sysconfig", vendorSkuDir), vendorPermissionFlag); - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getVendorDirectory(), "etc", "permissions", vendorSkuDir), vendorPermissionFlag); } @@ -543,18 +602,18 @@ public class SystemConfig { // Allow ODM to customize system configs as much as Vendor, because /odm is another // vendor partition other than /vendor. int odmPermissionFlag = vendorPermissionFlag; - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag); - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag); String skuProperty = SystemProperties.get(SKU_PROPERTY, ""); if (!skuProperty.isEmpty()) { String skuDir = "sku_" + skuProperty; - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getOdmDirectory(), "etc", "sysconfig", skuDir), odmPermissionFlag); - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getOdmDirectory(), "etc", "permissions", skuDir), odmPermissionFlag); } @@ -562,9 +621,9 @@ public class SystemConfig { // Allow OEM to customize these int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX; - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag); - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag); // Allow Product to customize these configs @@ -579,15 +638,15 @@ public class SystemConfig { // DEVICE_INITIAL_SDK_INT for the devices without product interface enforcement. productPermissionFlag = ALLOW_ALL; } - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag); - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag); // Allow /system_ext to customize all system configs - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getSystemExtDirectory(), "etc", "sysconfig"), ALLOW_ALL); - readPermissions(Environment.buildPath( + readPermissions(parser, Environment.buildPath( Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL); // Skip loading configuration from apex if it is not a system process. @@ -601,12 +660,14 @@ public class SystemConfig { if (f.isFile() || f.getPath().contains("@")) { continue; } - readPermissions(Environment.buildPath(f, "etc", "permissions"), apexPermissionFlag); + readPermissions(parser, Environment.buildPath(f, "etc", "permissions"), + apexPermissionFlag); } + pruneVendorApexPrivappAllowlists(); } @VisibleForTesting - public void readPermissions(File libraryDir, int permissionFlag) { + public void readPermissions(final XmlPullParser parser, File libraryDir, int permissionFlag) { // Read permissions from given directory. if (!libraryDir.exists() || !libraryDir.isDirectory()) { if (permissionFlag == ALLOW_ALL) { @@ -641,12 +702,12 @@ public class SystemConfig { continue; } - readPermissionsFromXml(f, permissionFlag); + readPermissionsFromXml(parser, f, permissionFlag); } // Read platform permissions last so it will take precedence if (platformFile != null) { - readPermissionsFromXml(platformFile, permissionFlag); + readPermissionsFromXml(parser, platformFile, permissionFlag); } } @@ -655,8 +716,9 @@ public class SystemConfig { + permFile + " at " + parser.getPositionDescription()); } - private void readPermissionsFromXml(File permFile, int permissionFlag) { - FileReader permReader = null; + private void readPermissionsFromXml(final XmlPullParser parser, File permFile, + int permissionFlag) { + final FileReader permReader; try { permReader = new FileReader(permFile); } catch (FileNotFoundException e) { @@ -668,7 +730,6 @@ public class SystemConfig { final boolean lowRam = ActivityManager.isLowRamDeviceStatic(); try { - XmlPullParser parser = Xml.newPullParser(); parser.setInput(permReader); int type; @@ -789,11 +850,17 @@ public class SystemConfig { XmlUtils.skipCurrentTag(parser); } } break; + case "apex-library": + // "apex-library" is meant to behave exactly like "library" case "library": { if (allowLibs) { String lname = parser.getAttributeValue(null, "name"); String lfile = parser.getAttributeValue(null, "file"); String ldependency = parser.getAttributeValue(null, "dependency"); + int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk", + 0); + int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk", + 0); if (lname == null) { Slog.w(TAG, "<" + name + "> without name in " + permFile + " at " + parser.getPositionDescription()); @@ -801,10 +868,34 @@ public class SystemConfig { Slog.w(TAG, "<" + name + "> without file in " + permFile + " at " + parser.getPositionDescription()); } else { - //Log.i(TAG, "Got library " + lname + " in " + lfile); - SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile, - ldependency == null ? new String[0] : ldependency.split(":")); - mSharedLibraries.put(lname, entry); + boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT; + boolean allowedMaxSdk = + maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT; + final boolean exists = new File(lfile).exists(); + if (allowedMinSdk && allowedMaxSdk && exists) { + int bcpSince = XmlUtils.readIntAttribute(parser, + "on-bootclasspath-since", 0); + int bcpBefore = XmlUtils.readIntAttribute(parser, + "on-bootclasspath-before", 0); + SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile, + ldependency == null + ? new String[0] : ldependency.split(":"), + bcpSince, bcpBefore); + mSharedLibraries.put(lname, entry); + } else { + final StringBuilder msg = new StringBuilder( + "Ignore shared library ").append(lname).append(":"); + if (!allowedMinSdk) { + msg.append(" min-device-sdk=").append(minDeviceSdk); + } + if (!allowedMaxSdk) { + msg.append(" max-device-sdk=").append(maxDeviceSdk); + } + if (!exists) { + msg.append(" ").append(lfile).append(" does not exist"); + } + Slog.i(TAG, msg.toString()); + } } } else { logNotAllowedInPartition(name, permFile, parser); @@ -1085,7 +1176,8 @@ public class SystemConfig { readPrivAppPermissions(parser, mSystemExtPrivAppPermissions, mSystemExtPrivAppDenyPermissions); } else if (apex) { - readApexPrivAppPermissions(parser, permFile); + readApexPrivAppPermissions(parser, permFile, + Environment.getApexDirectory().toPath()); } else { readPrivAppPermissions(parser, mPrivAppPermissions, mPrivAppDenyPermissions); @@ -1435,6 +1527,21 @@ public class SystemConfig { } } + /** + * Prunes out any privileged permission allowlists bundled in vendor apexes. + */ + @VisibleForTesting + public void pruneVendorApexPrivappAllowlists() { + for (String moduleName: mAllowedVendorApexes.keySet()) { + if (mApexPrivAppPermissions.containsKey(moduleName) + || mApexPrivAppDenyPermissions.containsKey(moduleName)) { + Slog.w(TAG, moduleName + " is a vendor apex, ignore its priv-app allowlist"); + mApexPrivAppPermissions.remove(moduleName); + mApexPrivAppDenyPermissions.remove(moduleName); + } + } + } + private void readInstallInUserType(XmlPullParser parser, Map<String, Set<String>> doInstallMap, Map<String, Set<String>> nonInstallMap) @@ -1645,8 +1752,7 @@ public class SystemConfig { /** * Returns the module name for a file in the apex module's partition. */ - private String getApexModuleNameFromFilePath(Path path) { - final Path apexDirectoryPath = Environment.getApexDirectory().toPath(); + private String getApexModuleNameFromFilePath(Path path, Path apexDirectoryPath) { if (!path.startsWith(apexDirectoryPath)) { throw new IllegalArgumentException("File " + path + " is not part of an APEX."); } @@ -1658,9 +1764,14 @@ public class SystemConfig { return path.getName(apexDirectoryPath.getNameCount()).toString(); } - private void readApexPrivAppPermissions(XmlPullParser parser, File permFile) - throws IOException, XmlPullParserException { - final String moduleName = getApexModuleNameFromFilePath(permFile.toPath()); + /** + * Reads the contents of the privileged permission allowlist stored inside an APEX. + */ + @VisibleForTesting + public void readApexPrivAppPermissions(XmlPullParser parser, File permFile, + Path apexDirectoryPath) throws IOException, XmlPullParserException { + final String moduleName = + getApexModuleNameFromFilePath(permFile.toPath(), apexDirectoryPath); final ArrayMap<String, ArraySet<String>> privAppPermissions; if (mApexPrivAppPermissions.containsKey(moduleName)) { privAppPermissions = mApexPrivAppPermissions.get(moduleName); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 57a0b488f692..1d181dd9f434 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -297,8 +297,6 @@ cc_library_shared { "libdl_android", "libtimeinstate", "server_configurable_flags", - // TODO: delete when ConnectivityT moves to APEX. - "libframework-connectivity-tiramisu-jni", ], export_shared_lib_headers: [ // our headers include libnativewindow's public headers diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto index f79de394a50f..ad3d67346292 100644 --- a/core/proto/android/service/diskstats.proto +++ b/core/proto/android/service/diskstats.proto @@ -99,6 +99,8 @@ message DiskStatsFreeSpaceProto { FOLDER_CACHE = 1; // System folder FOLDER_SYSTEM = 2; + // Metadata folder + FOLDER_METADATA = 3; } // Which folder? optional Folder folder = 1; diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 590fcf46f373..ad368a819bd4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1277,6 +1277,8 @@ <java-symbol type="array" name="vendor_required_apps_managed_user" /> <java-symbol type="array" name="vendor_required_apps_managed_profile" /> <java-symbol type="array" name="vendor_required_apps_managed_device" /> + <java-symbol type="array" name="vendor_required_attestation_certificates" /> + <java-symbol type="string" name="vendor_required_attestation_revocation_list_url" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_user" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_profile" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_device" /> diff --git a/core/res/res/values/vendor_required_attestation_certificates.xml b/core/res/res/values/vendor_required_attestation_certificates.xml new file mode 100644 index 000000000000..ff7313ed1b4d --- /dev/null +++ b/core/res/res/values/vendor_required_attestation_certificates.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (C) 2022 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. + */ +--> +<resources> + <!-- The PEM-encoded certificates added here are used for verifying attestations. + The trustworthiness of the attestation depends on the root certificate of the chain. + + Certificates that can be used can be retrieved from: + https://developer.android.com/training/articles/security-key-attestation#root_certificate. + + If not already present in resource overlay, please add + vendor_required_attestation_certificates.xml (matching this file) in vendor overlay + with <item></item> of the PEM-encoded root certificates. + --> + <string-array translatable="false" name="vendor_required_attestation_certificates"> + </string-array> + + <!-- Url to mapping of revoked certificates' hex encoded serial numbers. Example format + can be found at: + https://developer.android.com/training/articles/security-key-attestation#certificate_status + --> + <string translatable="false" name="vendor_required_attestation_revocation_list_url"></string> +</resources> diff --git a/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java index 4b64dfc84fb7..cc7557977e80 100644 --- a/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java @@ -45,13 +45,13 @@ public class TelephonyTimeSuggestionTest { assertEquals(two, one); } - builder1.setUtcTime(new TimestampedValue<>(1111L, 2222L)); + builder1.setUnixEpochTime(new TimestampedValue<>(1111L, 2222L)); { TelephonyTimeSuggestion one = builder1.build(); assertEquals(one, one); } - builder2.setUtcTime(new TimestampedValue<>(1111L, 2222L)); + builder2.setUnixEpochTime(new TimestampedValue<>(1111L, 2222L)); { TelephonyTimeSuggestion one = builder1.build(); TelephonyTimeSuggestion two = builder2.build(); @@ -61,7 +61,7 @@ public class TelephonyTimeSuggestionTest { TelephonyTimeSuggestion.Builder builder3 = new TelephonyTimeSuggestion.Builder(SLOT_INDEX + 1); - builder3.setUtcTime(new TimestampedValue<>(1111L, 2222L)); + builder3.setUnixEpochTime(new TimestampedValue<>(1111L, 2222L)); { TelephonyTimeSuggestion one = builder1.build(); TelephonyTimeSuggestion three = builder3.build(); @@ -84,7 +84,7 @@ public class TelephonyTimeSuggestionTest { TelephonyTimeSuggestion.Builder builder = new TelephonyTimeSuggestion.Builder(SLOT_INDEX); assertRoundTripParcelable(builder.build()); - builder.setUtcTime(new TimestampedValue<>(1111L, 2222L)); + builder.setUnixEpochTime(new TimestampedValue<>(1111L, 2222L)); assertRoundTripParcelable(builder.build()); // DebugInfo should also be stored (but is not checked by equals() diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 0d012538f7e1..82aff6027653 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -20,13 +20,6 @@ This XML file declares which signature|privileged permissions should be granted applications that come with the platform --> <permissions> - <privapp-permissions package="android.ext.services"> - <permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" /> - <permission name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" /> - <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" /> - <permission name="android.permission.INTERACT_ACROSS_USERS" /> - </privapp-permissions> - <!-- Needed for Build.getSerial(), which is used to send a unique number for serial, per HUIG. --> <privapp-permissions package="android.car.usb.handler"> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> @@ -274,6 +267,7 @@ applications that come with the platform <privapp-permissions package="com.android.server.telecom"> <permission name="android.permission.BIND_CONNECTION_SERVICE"/> <permission name="android.permission.BIND_INCALL_SERVICE"/> + <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> <permission name="android.permission.CALL_PRIVILEGED"/> <permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> @@ -464,6 +458,9 @@ applications that come with the platform <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> <permission name="android.permission.NEARBY_WIFI_DEVICES" /> <permission name="android.permission.OVERRIDE_WIFI_CONFIG" /> + <!-- Permission needed for CTS test - ConcurrencyTest#testP2pExternalApprover + P2P external approver API sets require MANAGE_WIFI_AUTO_JOIN permission. --> + <permission name="android.permission.MANAGE_WIFI_AUTO_JOIN" /> <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest --> <permission name="android.permission.BIND_CARRIER_SERVICES"/> <!-- Permission required for CTS test - MusicRecognitionManagerTest --> diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index 2e85b304ec47..31dd10a8ed53 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -66,6 +66,7 @@ import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; +import java.security.spec.NamedParameterSpec; import java.security.spec.RSAKeyGenParameterSpec; import java.util.ArrayList; import java.util.Arrays; @@ -119,36 +120,42 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private static final int RSA_MIN_KEY_SIZE = 512; private static final int RSA_MAX_KEY_SIZE = 8192; - private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE = + private static final Map<String, Integer> SUPPORTED_EC_CURVE_NAME_TO_SIZE = new HashMap<String, Integer>(); - private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>(); - private static final List<Integer> SUPPORTED_EC_NIST_CURVE_SIZES = new ArrayList<Integer>(); + private static final List<String> SUPPORTED_EC_CURVE_NAMES = new ArrayList<String>(); + private static final List<Integer> SUPPORTED_EC_CURVE_SIZES = new ArrayList<Integer>(); + private static final String CURVE_X_25519 = NamedParameterSpec.X25519.getName(); + private static final String CURVE_ED_25519 = NamedParameterSpec.ED25519.getName(); + static { // Aliases for NIST P-224 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-224", 224); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp224r1", 224); // Aliases for NIST P-256 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-256", 256); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp256r1", 256); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("prime256v1", 256); + // Aliases for Curve 25519 + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put(CURVE_X_25519.toLowerCase(Locale.US), 256); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put(CURVE_ED_25519.toLowerCase(Locale.US), 256); // Aliases for NIST P-384 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-384", 384); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp384r1", 384); // Aliases for NIST P-521 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-521", 521); + SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp521r1", 521); - SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet()); - Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES); + SUPPORTED_EC_CURVE_NAMES.addAll(SUPPORTED_EC_CURVE_NAME_TO_SIZE.keySet()); + Collections.sort(SUPPORTED_EC_CURVE_NAMES); - SUPPORTED_EC_NIST_CURVE_SIZES.addAll( - new HashSet<Integer>(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.values())); - Collections.sort(SUPPORTED_EC_NIST_CURVE_SIZES); + SUPPORTED_EC_CURVE_SIZES.addAll( + new HashSet<Integer>(SUPPORTED_EC_CURVE_NAME_TO_SIZE.values())); + Collections.sort(SUPPORTED_EC_CURVE_SIZES); } private final int mOriginalKeymasterAlgorithm; @@ -164,6 +171,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private int mKeySizeBits; private SecureRandom mRng; private KeyDescriptor mAttestKeyDescriptor; + private String mEcCurveName; private int[] mKeymasterPurposes; private int[] mKeymasterBlockModes; @@ -177,12 +185,15 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mOriginalKeymasterAlgorithm = keymasterAlgorithm; } - private @EcCurve int keySize2EcCurve(int keySizeBits) + private static @EcCurve int keySizeAndNameToEcCurve(int keySizeBits, String ecCurveName) throws InvalidAlgorithmParameterException { switch (keySizeBits) { case 224: return EcCurve.P_224; case 256: + if (isCurve25519(ecCurveName)) { + return EcCurve.CURVE_25519; + } return EcCurve.P_256; case 384: return EcCurve.P_384; @@ -247,7 +258,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato if (mKeySizeBits == -1) { mKeySizeBits = getDefaultKeySize(keymasterAlgorithm); } - checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked()); + checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked(), + mEcCurveName); if (spec.getKeystoreAlias() == null) { throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided"); @@ -299,6 +311,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mAttestKeyDescriptor = buildAndCheckAttestKeyDescriptor(spec); checkAttestKeyPurpose(spec); + checkCorrectKeyPurposeForCurve(spec); success = true; } finally { @@ -317,6 +330,42 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } } + private void checkCorrectKeyPurposeForCurve(KeyGenParameterSpec spec) + throws InvalidAlgorithmParameterException { + // Validate the key usage purposes against the curve. x25519 should be + // key exchange only, ed25519 signing and attesting. + + if (!isCurve25519(mEcCurveName)) { + return; + } + + if (mEcCurveName.equalsIgnoreCase(CURVE_X_25519) + && spec.getPurposes() != KeyProperties.PURPOSE_AGREE_KEY) { + throw new InvalidAlgorithmParameterException( + "x25519 may only be used for key agreement."); + } else if (mEcCurveName.equalsIgnoreCase(CURVE_ED_25519) + && !hasOnlyAllowedPurposeForEd25519(spec.getPurposes())) { + throw new InvalidAlgorithmParameterException( + "ed25519 may not be used for key agreement."); + } + } + + private static boolean isCurve25519(String ecCurveName) { + if (ecCurveName == null) { + return false; + } + return ecCurveName.equalsIgnoreCase(CURVE_X_25519) + || ecCurveName.equalsIgnoreCase(CURVE_ED_25519); + } + + private static boolean hasOnlyAllowedPurposeForEd25519(@KeyProperties.PurposeEnum int purpose) { + final int allowedPurposes = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY + | KeyProperties.PURPOSE_ATTEST_KEY; + boolean hasAllowedPurpose = (purpose & allowedPurposes) != 0; + boolean hasDisallowedPurpose = (purpose & ~allowedPurposes) != 0; + return hasAllowedPurpose && !hasDisallowedPurpose; + } + private KeyDescriptor buildAndCheckAttestKeyDescriptor(KeyGenParameterSpec spec) throws InvalidAlgorithmParameterException { if (spec.getAttestKeyAlias() != null) { @@ -473,6 +522,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mRSAPublicExponent = null; mRng = null; mKeyStore = null; + mEcCurveName = null; } private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException { @@ -514,13 +564,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato case KeymasterDefs.KM_ALGORITHM_EC: if (algSpecificSpec instanceof ECGenParameterSpec) { ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec; - String curveName = ecSpec.getName(); - Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get( - curveName.toLowerCase(Locale.US)); + mEcCurveName = ecSpec.getName(); + final Integer ecSpecKeySizeBits = SUPPORTED_EC_CURVE_NAME_TO_SIZE.get( + mEcCurveName.toLowerCase(Locale.US)); if (ecSpecKeySizeBits == null) { throw new InvalidAlgorithmParameterException( - "Unsupported EC curve name: " + curveName - + ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES); + "Unsupported EC curve name: " + mEcCurveName + + ". Supported: " + SUPPORTED_EC_CURVE_NAMES); } if (mKeySizeBits == -1) { mKeySizeBits = ecSpecKeySizeBits; @@ -744,7 +794,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { params.add(KeyStore2ParameterUtils.makeEnum( - Tag.EC_CURVE, keySize2EcCurve(mKeySizeBits) + Tag.EC_CURVE, keySizeAndNameToEcCurve(mKeySizeBits, mEcCurveName) )); } @@ -864,7 +914,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private static void checkValidKeySize( int keymasterAlgorithm, int keySize, - boolean isStrongBoxBacked) + boolean isStrongBoxBacked, + String mEcCurveName) throws InvalidAlgorithmParameterException { switch (keymasterAlgorithm) { case KeymasterDefs.KM_ALGORITHM_EC: @@ -873,9 +924,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato "Unsupported StrongBox EC key size: " + keySize + " bits. Supported: 256"); } - if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) { + if (isStrongBoxBacked && isCurve25519(mEcCurveName)) { + throw new InvalidAlgorithmParameterException( + "Unsupported StrongBox EC: " + mEcCurveName); + } + if (!SUPPORTED_EC_CURVE_SIZES.contains(keySize)) { throw new InvalidAlgorithmParameterException("Unsupported EC key size: " - + keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES); + + keySize + " bits. Supported: " + SUPPORTED_EC_CURVE_SIZES); } break; case KeymasterDefs.KM_ALGORITHM_RSA: diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 72a145fa6a05..358104fffbf6 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -66,6 +66,11 @@ public class AndroidKeyStoreProvider extends Provider { private static final String DESEDE_SYSTEM_PROPERTY = "ro.hardware.keystore_desede"; + // Conscrypt returns the Ed25519 OID as the JCA key algorithm. + private static final String ED25519_OID = "1.3.101.112"; + // Conscrypt returns "XDH" as the X25519 JCA key algorithm. + private static final String X25519_ALIAS = "XDH"; + /** @hide **/ public AndroidKeyStoreProvider() { super(PROVIDER_NAME, 1.0, "Android KeyStore security provider"); @@ -78,10 +83,16 @@ public class AndroidKeyStoreProvider extends Provider { // java.security.KeyPairGenerator put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); + put("KeyPairGenerator." + X25519_ALIAS, + PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); + put("KeyPairGenerator." + ED25519_OID, + PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); // java.security.KeyFactory putKeyFactoryImpl("EC"); putKeyFactoryImpl("RSA"); + putKeyFactoryImpl(X25519_ALIAS); + putKeyFactoryImpl(ED25519_OID); // javax.crypto.KeyGenerator put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); @@ -219,12 +230,17 @@ public class AndroidKeyStoreProvider extends Provider { KeyStoreSecurityLevel securityLevel = iSecurityLevel; if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) { - return new AndroidKeyStoreECPublicKey(descriptor, metadata, iSecurityLevel, (ECPublicKey) publicKey); } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) { return new AndroidKeyStoreRSAPublicKey(descriptor, metadata, iSecurityLevel, (RSAPublicKey) publicKey); + } else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) { + //TODO(b/214203951) missing classes in conscrypt + throw new ProviderException("Curve " + ED25519_OID + " not supported yet"); + } else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) { + //TODO(b/214203951) missing classes in conscrypt + throw new ProviderException("Curve " + X25519_ALIAS + " not supported yet"); } else { throw new ProviderException("Unsupported Android Keystore public key algorithm: " + jcaKeyAlgorithm); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java deleted file mode 100644 index f23794b50543..000000000000 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.mediaframeworktest.unit; - -import static org.junit.Assert.assertEquals; - -import android.bluetooth.BluetoothProfile; -import android.media.BluetoothProfileConnectionInfo; - -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class BluetoothProfileConnectionInfoTest { - - @Test - public void testCoverageA2dp() { - final boolean supprNoisy = false; - final int volume = 42; - final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo - .createA2dpInfo(supprNoisy, volume); - assertEquals(info.getProfile(), BluetoothProfile.A2DP); - assertEquals(info.isSuppressNoisyIntent(), supprNoisy); - assertEquals(info.getVolume(), volume); - } - - @Test - public void testCoverageA2dpSink() { - final int volume = 42; - final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo - .createA2dpSinkInfo(volume); - assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK); - assertEquals(info.getVolume(), volume); - } - - @Test - public void testCoveragehearingAid() { - final boolean supprNoisy = true; - final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo - .createHearingAidInfo(supprNoisy); - assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID); - assertEquals(info.isSuppressNoisyIntent(), supprNoisy); - } - - @Test - public void testCoverageLeAudio() { - final boolean supprNoisy = false; - final boolean isLeOutput = true; - final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo - .createLeAudioInfo(supprNoisy, isLeOutput); - assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO); - assertEquals(info.isSuppressNoisyIntent(), supprNoisy); - assertEquals(info.isLeOutput(), isLeOutput); - } -} - diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp index 6329565ca567..66527802a0e4 100644 --- a/packages/ConnectivityT/framework-t/Android.bp +++ b/packages/ConnectivityT/framework-t/Android.bp @@ -158,7 +158,6 @@ filegroup { name: "framework-connectivity-tiramisu-sources", srcs: [ ":framework-connectivity-ethernet-sources", - ":framework-connectivity-netstats-sources", ], visibility: ["//frameworks/base"], } @@ -167,6 +166,7 @@ filegroup { name: "framework-connectivity-tiramisu-updatable-sources", srcs: [ ":framework-connectivity-ipsec-sources", + ":framework-connectivity-netstats-sources", ":framework-connectivity-nsd-sources", ":framework-connectivity-tiramisu-internal-sources", ], @@ -194,15 +194,12 @@ cc_library_shared { "jni/onload.cpp", ], shared_libs: [ + "libandroid", "liblog", - ], - static_libs: [ - "libnativehelper_compat_libc++", + "libnativehelper", ], stl: "none", apex_available: [ "com.android.tethering", - // TODO: remove when ConnectivityT moves to APEX. - "//apex_available:platform", ], } diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java index 5ce7e59b38ff..ca080ce4c64a 100644 --- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java +++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java @@ -27,7 +27,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.annotation.WorkerThread; import android.app.usage.NetworkStats.Bucket; import android.compat.annotation.UnsupportedAppUsage; @@ -192,9 +191,13 @@ public class NetworkStatsManager { } } - /** @hide */ + /** + * Set poll force flag to indicate that calling any subsequent query method will force a stats + * poll. + * @hide + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - @TestApi + @SystemApi(client = MODULE_LIBRARIES) public void setPollForce(boolean pollForce) { if (pollForce) { mFlags |= FLAG_POLL_FORCE; @@ -696,7 +699,9 @@ public class NetworkStatsManager { * @hide */ @SystemApi - @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK}) @NonNull public android.net.NetworkStats getMobileUidStats() { try { return mService.getUidStatsForTransport(TRANSPORT_CELLULAR); @@ -720,7 +725,9 @@ public class NetworkStatsManager { * @hide */ @SystemApi - @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK}) @NonNull public android.net.NetworkStats getWifiUidStats() { try { return mService.getUidStatsForTransport(TRANSPORT_WIFI); @@ -737,8 +744,9 @@ public class NetworkStatsManager { * {@link #unregisterUsageCallback} is called. * * @param template Template used to match networks. See {@link NetworkTemplate}. - * @param thresholdBytes Threshold in bytes to be notified on. The provided value that lower - * than 2MiB will be clamped for non-privileged callers. + * @param thresholdBytes Threshold in bytes to be notified on. Provided values lower than 2MiB + * will be clamped for callers except callers with the NETWORK_STACK + * permission. * @param executor The executor on which callback will be invoked. The provided {@link Executor} * must run callback sequentially, otherwise the order of callbacks cannot be * guaranteed. @@ -747,6 +755,9 @@ public class NetworkStatsManager { * @hide */ @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK}, conditional = true) public void registerUsageCallback(@NonNull NetworkTemplate template, long thresholdBytes, @NonNull @CallbackExecutor Executor executor, @NonNull UsageCallback callback) { Objects.requireNonNull(template, "NetworkTemplate cannot be null"); diff --git a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java index 577ac5466692..9bffbfb27a8d 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java +++ b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java @@ -18,6 +18,7 @@ package android.net; import android.annotation.SystemApi; import android.app.SystemServiceRegistry; +import android.app.usage.NetworkStatsManager; import android.content.Context; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; @@ -57,5 +58,15 @@ public final class ConnectivityFrameworkInitializerTiramisu { return new IpSecManager(context, service); } ); + + SystemServiceRegistry.registerContextAwareService( + Context.NETWORK_STATS_SERVICE, + NetworkStatsManager.class, + (context, serviceBinder) -> { + INetworkStatsService service = + INetworkStatsService.Stub.asInterface(serviceBinder); + return new NetworkStatsManager(context, service); + } + ); } } diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java index 1a955c4c57d7..72243f9e87d9 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java @@ -146,7 +146,7 @@ public class EthernetManager { * @param iface the name of the interface. * @param state the current state of the interface, or {@link #STATE_ABSENT} if the * interface was removed. - * @param role whether the interface is in the client mode or server mode. + * @param role whether the interface is in client mode or server mode. * @param configuration the current IP configuration of the interface. * @hide */ diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java index e879e40247cf..a6269711055a 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -27,13 +28,13 @@ import java.util.Objects; @SystemApi public final class EthernetNetworkUpdateRequest implements Parcelable { @NonNull - private final StaticIpConfiguration mIpConfig; + private final IpConfiguration mIpConfig; @NonNull private final NetworkCapabilities mNetworkCapabilities; @NonNull - public StaticIpConfiguration getIpConfig() { - return new StaticIpConfiguration(mIpConfig); + public IpConfiguration getIpConfiguration() { + return new IpConfiguration(mIpConfig); } @NonNull @@ -41,20 +42,75 @@ public final class EthernetNetworkUpdateRequest implements Parcelable { return new NetworkCapabilities(mNetworkCapabilities); } - public EthernetNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig, + private EthernetNetworkUpdateRequest(@NonNull final IpConfiguration ipConfig, @NonNull final NetworkCapabilities networkCapabilities) { Objects.requireNonNull(ipConfig); Objects.requireNonNull(networkCapabilities); - mIpConfig = new StaticIpConfiguration(ipConfig); + mIpConfig = new IpConfiguration(ipConfig); mNetworkCapabilities = new NetworkCapabilities(networkCapabilities); } private EthernetNetworkUpdateRequest(@NonNull final Parcel source) { Objects.requireNonNull(source); - mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source); + mIpConfig = IpConfiguration.CREATOR.createFromParcel(source); mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source); } + /** + * Builder used to create {@link EthernetNetworkUpdateRequest} objects. + */ + public static final class Builder { + @Nullable + private IpConfiguration mBuilderIpConfig; + @Nullable + private NetworkCapabilities mBuilderNetworkCapabilities; + + public Builder(){} + + /** + * Constructor to populate the builder's values with an already built + * {@link EthernetNetworkUpdateRequest}. + * @param request the {@link EthernetNetworkUpdateRequest} to populate with. + */ + public Builder(@NonNull final EthernetNetworkUpdateRequest request) { + Objects.requireNonNull(request); + mBuilderIpConfig = new IpConfiguration(request.mIpConfig); + mBuilderNetworkCapabilities = new NetworkCapabilities(request.mNetworkCapabilities); + } + + /** + * Set the {@link IpConfiguration} to be used with the {@code Builder}. + * @param ipConfig the {@link IpConfiguration} to set. + * @return The builder to facilitate chaining. + */ + @NonNull + public Builder setIpConfiguration(@NonNull final IpConfiguration ipConfig) { + Objects.requireNonNull(ipConfig); + mBuilderIpConfig = new IpConfiguration(ipConfig); + return this; + } + + /** + * Set the {@link NetworkCapabilities} to be used with the {@code Builder}. + * @param nc the {@link NetworkCapabilities} to set. + * @return The builder to facilitate chaining. + */ + @NonNull + public Builder setNetworkCapabilities(@NonNull final NetworkCapabilities nc) { + Objects.requireNonNull(nc); + mBuilderNetworkCapabilities = new NetworkCapabilities(nc); + return this; + } + + /** + * Build {@link EthernetNetworkUpdateRequest} return the current update request. + */ + @NonNull + public EthernetNetworkUpdateRequest build() { + return new EthernetNetworkUpdateRequest(mBuilderIpConfig, mBuilderNetworkCapabilities); + } + } + @Override public String toString() { return "EthernetNetworkUpdateRequest{" @@ -68,7 +124,7 @@ public final class EthernetNetworkUpdateRequest implements Parcelable { if (o == null || getClass() != o.getClass()) return false; EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o; - return Objects.equals(that.getIpConfig(), mIpConfig) + return Objects.equals(that.getIpConfiguration(), mIpConfig) && Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java index 56faa52e82df..a48f94b66def 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java @@ -20,6 +20,7 @@ import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.IntDef; import android.annotation.NonNull; @@ -86,6 +87,7 @@ public class NetworkIdentity { final int mType; final int mRatType; + final int mSubId; final String mSubscriberId; final String mWifiNetworkKey; final boolean mRoaming; @@ -96,7 +98,7 @@ public class NetworkIdentity { /** @hide */ public NetworkIdentity( int type, int ratType, @Nullable String subscriberId, @Nullable String wifiNetworkKey, - boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged) { + boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged, int subId) { mType = type; mRatType = ratType; mSubscriberId = subscriberId; @@ -105,12 +107,13 @@ public class NetworkIdentity { mMetered = metered; mDefaultNetwork = defaultNetwork; mOemManaged = oemManaged; + mSubId = subId; } @Override public int hashCode() { return Objects.hash(mType, mRatType, mSubscriberId, mWifiNetworkKey, mRoaming, mMetered, - mDefaultNetwork, mOemManaged); + mDefaultNetwork, mOemManaged, mSubId); } @Override @@ -122,7 +125,8 @@ public class NetworkIdentity { && Objects.equals(mWifiNetworkKey, ident.mWifiNetworkKey) && mMetered == ident.mMetered && mDefaultNetwork == ident.mDefaultNetwork - && mOemManaged == ident.mOemManaged; + && mOemManaged == ident.mOemManaged + && mSubId == ident.mSubId; } return false; } @@ -150,6 +154,7 @@ public class NetworkIdentity { builder.append(", metered=").append(mMetered); builder.append(", defaultNetwork=").append(mDefaultNetwork); builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged)); + builder.append(", subId=").append(mSubId); return builder.append("}").toString(); } @@ -184,14 +189,14 @@ public class NetworkIdentity { public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); - proto.write(NetworkIdentityProto.TYPE, mType); + proto.write(NetworkIdentityProto.TYPE_FIELD_NUMBER, mType); // TODO: dump mRatType as well. - proto.write(NetworkIdentityProto.ROAMING, mRoaming); - proto.write(NetworkIdentityProto.METERED, mMetered); - proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork); - proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged); + proto.write(NetworkIdentityProto.ROAMING_FIELD_NUMBER, mRoaming); + proto.write(NetworkIdentityProto.METERED_FIELD_NUMBER, mMetered); + proto.write(NetworkIdentityProto.DEFAULT_NETWORK_FIELD_NUMBER, mDefaultNetwork); + proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK_FIELD_NUMBER, mOemManaged); proto.end(start); } @@ -256,6 +261,11 @@ public class NetworkIdentity { return mOemManaged; } + /** Get the SubId of this instance. */ + public int getSubId() { + return mSubId; + } + /** * Assemble a {@link NetworkIdentity} from the passed arguments. * @@ -276,7 +286,8 @@ public class NetworkIdentity { public static NetworkIdentity buildNetworkIdentity(Context context, @NonNull NetworkStateSnapshot snapshot, boolean defaultNetwork, int ratType) { final NetworkIdentity.Builder builder = new NetworkIdentity.Builder() - .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork); + .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork) + .setSubId(snapshot.getSubId()); if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) { builder.setRatType(ratType); } @@ -325,6 +336,9 @@ public class NetworkIdentity { if (res == 0) { res = Integer.compare(left.mOemManaged, right.mOemManaged); } + if (res == 0) { + res = Integer.compare(left.mSubId, right.mSubId); + } return res; } @@ -345,6 +359,7 @@ public class NetworkIdentity { private boolean mMetered; private boolean mDefaultNetwork; private int mOemManaged; + private int mSubId; /** * Creates a new Builder. @@ -359,6 +374,7 @@ public class NetworkIdentity { mMetered = false; mDefaultNetwork = false; mOemManaged = NetworkTemplate.OEM_MANAGED_NO; + mSubId = INVALID_SUBSCRIPTION_ID; } /** @@ -537,6 +553,19 @@ public class NetworkIdentity { return this; } + /** + * Set the Subscription Id. + * + * @param subId the Subscription Id of the network. Or INVALID_SUBSCRIPTION_ID if not + * applicable. + * @return this builder. + */ + @NonNull + public Builder setSubId(int subId) { + mSubId = subId; + return this; + } + private void ensureValidParameters() { // Assert non-mobile network cannot have a ratType. if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) { @@ -559,7 +588,7 @@ public class NetworkIdentity { public NetworkIdentity build() { ensureValidParameters(); return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey, - mRoaming, mMetered, mDefaultNetwork, mOemManaged); + mRoaming, mMetered, mDefaultNetwork, mOemManaged, mSubId); } } } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java index dfa347f6f12b..56461babfe49 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java @@ -17,6 +17,7 @@ package android.net; import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.NonNull; import android.service.NetworkIdentitySetProto; @@ -42,6 +43,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> { private static final int VERSION_ADD_METERED = 4; private static final int VERSION_ADD_DEFAULT_NETWORK = 5; private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6; + private static final int VERSION_ADD_SUB_ID = 7; /** * Construct a {@link NetworkIdentitySet} object. @@ -103,8 +105,15 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> { oemNetCapabilities = NetworkIdentity.OEM_NONE; } + final int subId; + if (version >= VERSION_ADD_SUB_ID) { + subId = in.readInt(); + } else { + subId = INVALID_SUBSCRIPTION_ID; + } + add(new NetworkIdentity(type, ratType, subscriberId, networkId, roaming, metered, - defaultNetwork, oemNetCapabilities)); + defaultNetwork, oemNetCapabilities, subId)); } } @@ -113,7 +122,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> { * @hide */ public void writeToStream(DataOutput out) throws IOException { - out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK); + out.writeInt(VERSION_ADD_SUB_ID); out.writeInt(size()); for (NetworkIdentity ident : this) { out.writeInt(ident.getType()); @@ -124,6 +133,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> { out.writeBoolean(ident.isMetered()); out.writeBoolean(ident.isDefaultNetwork()); out.writeInt(ident.getOemManaged()); + out.writeInt(ident.getSubId()); } } @@ -212,7 +222,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> { final long start = proto.start(tag); for (NetworkIdentity ident : this) { - ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES); + ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES_FIELD_NUMBER); } proto.end(start); diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java index 39156343924d..d3f785a8f983 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java @@ -17,6 +17,8 @@ package android.net; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.NonNull; import android.annotation.Nullable; @@ -98,12 +100,29 @@ public final class NetworkStateSnapshot implements Parcelable { return mLinkProperties; } - /** Get the Subscriber Id of the network associated with this snapshot. */ + /** + * Get the Subscriber Id of the network associated with this snapshot. + * @deprecated Please use #getSubId, which doesn't return personally identifiable + * information. + */ + @Deprecated @Nullable public String getSubscriberId() { return mSubscriberId; } + /** Get the subId of the network associated with this snapshot. */ + public int getSubId() { + if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + final NetworkSpecifier spec = mNetworkCapabilities.getNetworkSpecifier(); + if (spec instanceof TelephonyNetworkSpecifier) { + return ((TelephonyNetworkSpecifier) spec).getSubscriptionId(); + } + } + return INVALID_SUBSCRIPTION_ID; + } + + /** * Get the legacy type of the network associated with this snapshot. * @return the legacy network type. See {@code ConnectivityManager#TYPE_*}. diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java index 9175809d9c7c..f681ba1c3853 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java @@ -28,6 +28,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.Process; import android.os.SystemClock; +import android.text.TextUtils; import android.util.SparseBooleanArray; import com.android.internal.annotations.VisibleForTesting; @@ -500,7 +501,7 @@ public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Ent && roaming == e.roaming && defaultNetwork == e.defaultNetwork && rxBytes == e.rxBytes && rxPackets == e.rxPackets && txBytes == e.txBytes && txPackets == e.txPackets - && operations == e.operations && iface.equals(e.iface); + && operations == e.operations && TextUtils.equals(iface, e.iface); } return false; } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java index 735c44d5c87e..67d48f0000d5 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java @@ -732,19 +732,19 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W final long start = proto.start(tag); for (Key key : getSortedKeys()) { - final long startStats = proto.start(NetworkStatsCollectionProto.STATS); + final long startStats = proto.start(NetworkStatsCollectionProto.STATS_FIELD_NUMBER); // Key - final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY); - key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY); - proto.write(NetworkStatsCollectionKeyProto.UID, key.uid); - proto.write(NetworkStatsCollectionKeyProto.SET, key.set); - proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag); + final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY_FIELD_NUMBER); + key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY_FIELD_NUMBER); + proto.write(NetworkStatsCollectionKeyProto.UID_FIELD_NUMBER, key.uid); + proto.write(NetworkStatsCollectionKeyProto.SET_FIELD_NUMBER, key.set); + proto.write(NetworkStatsCollectionKeyProto.TAG_FIELD_NUMBER, key.tag); proto.end(startKey); // Value final NetworkStatsHistory history = mStats.get(key); - history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY); + history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY_FIELD_NUMBER); proto.end(startStats); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java index 78c137073aaa..822a16e0bb41 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java @@ -915,17 +915,18 @@ public final class NetworkStatsHistory implements Parcelable { public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); - proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration); + proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS_FIELD_NUMBER, bucketDuration); for (int i = 0; i < bucketCount; i++) { - final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS); - - proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]); - dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i); - dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i); - dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i); - dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i); - dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i); + final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS_FIELD_NUMBER); + + proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS_FIELD_NUMBER, + bucketStart[i]); + dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES_FIELD_NUMBER, rxBytes, i); + dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS_FIELD_NUMBER, rxPackets, i); + dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES_FIELD_NUMBER, txBytes, i); + dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS_FIELD_NUMBER, txPackets, i); + dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS_FIELD_NUMBER, operations, i); proto.end(startBucket); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java index 9b58b016bbf3..7b5afd720016 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java @@ -79,7 +79,8 @@ public final class NetworkTemplate implements Parcelable { MATCH_WIFI, MATCH_ETHERNET, MATCH_BLUETOOTH, - MATCH_CARRIER + MATCH_PROXY, + MATCH_CARRIER, }) public @interface TemplateMatchRule{} @@ -104,9 +105,8 @@ public final class NetworkTemplate implements Parcelable { /** Match rule to match bluetooth networks. */ public static final int MATCH_BLUETOOTH = 8; /** - * Match rule to match networks with {@link Connectivity#TYPE_PROXY} as the legacy network type. - * - * @hide + * Match rule to match networks with {@link ConnectivityManager#TYPE_PROXY} as the legacy + * network type. */ public static final int MATCH_PROXY = 9; /** diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp index c3049dabefc2..5100e7c5b9a4 100644 --- a/packages/ConnectivityT/service/Android.bp +++ b/packages/ConnectivityT/service/Android.bp @@ -40,6 +40,30 @@ filegroup { ], } +// For test code only. +filegroup { + name: "lib_networkStatsFactory_native", + srcs: [ + "jni/com_android_server_net_NetworkStatsFactory.cpp", + ], + path: "jni", + visibility: [ + "//packages/modules/Connectivity:__subpackages__", + ], +} + +filegroup { + name: "services.connectivity-netstats-jni-sources", + srcs: [ + "jni/com_android_server_net_NetworkStatsFactory.cpp", + "jni/com_android_server_net_NetworkStatsService.cpp", + ], + path: "jni", + visibility: [ + "//packages/modules/Connectivity:__subpackages__", + ], +} + // Nsd related libraries. filegroup { @@ -88,7 +112,6 @@ filegroup { name: "services.connectivity-tiramisu-sources", srcs: [ ":services.connectivity-ethernet-sources", - ":services.connectivity-netstats-sources", ], path: "src", visibility: ["//frameworks/base/services/core"], @@ -98,6 +121,7 @@ filegroup { name: "services.connectivity-tiramisu-updatable-sources", srcs: [ ":services.connectivity-ipsec-sources", + ":services.connectivity-netstats-sources", ":services.connectivity-nsd-sources", ], path: "src", diff --git a/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsFactory.cpp index 8b6526ff49f0..8b6526ff49f0 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp +++ b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsFactory.cpp diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp index f8a81682bdcf..39cbaf716fc0 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp +++ b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp @@ -102,15 +102,10 @@ static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) { } } -static int deleteTagData(JNIEnv* /* env */, jclass /* clazz */, jint uid) { - return qtaguid_deleteTagData(0, uid); -} - static const JNINativeMethod gMethods[] = { {"nativeGetTotalStat", "(I)J", (void*)getTotalStat}, {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat}, {"nativeGetUidStat", "(II)J", (void*)getUidStat}, - {"nativeDeleteTagData", "(I)I", (void*)deleteTagData}, }; int register_android_server_net_NetworkStatsService(JNIEnv* env) { diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java index 668d1cba921b..151c90dd4155 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java @@ -54,6 +54,10 @@ import java.util.concurrent.ConcurrentHashMap; * @hide */ public class NetworkStatsFactory { + static { + System.loadLibrary("service-connectivity"); + } + private static final String TAG = "NetworkStatsFactory"; private static final boolean USE_NATIVE_PARSING = true; diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java index 19536247b23b..fdfc893f57cb 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java @@ -19,8 +19,11 @@ package com.android.server.net; import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES; import android.app.usage.NetworkStatsManager; +import android.content.Context; +import android.content.pm.PackageManager; import android.net.DataUsageRequest; import android.net.NetworkIdentitySet; +import android.net.NetworkStack; import android.net.NetworkStats; import android.net.NetworkStatsAccess; import android.net.NetworkStatsCollection; @@ -74,9 +77,9 @@ class NetworkStatsObservers { * * @return the normalized request wrapped within {@link RequestInfo}. */ - public DataUsageRequest register(DataUsageRequest inputRequest, IUsageCallback callback, - int callingUid, @NetworkStatsAccess.Level int accessLevel) { - DataUsageRequest request = buildRequest(inputRequest, callingUid); + public DataUsageRequest register(Context context, DataUsageRequest inputRequest, + IUsageCallback callback, int callingUid, @NetworkStatsAccess.Level int accessLevel) { + DataUsageRequest request = buildRequest(context, inputRequest, callingUid); RequestInfo requestInfo = buildRequestInfo(request, callback, callingUid, accessLevel); @@ -194,10 +197,13 @@ class NetworkStatsObservers { } } - private DataUsageRequest buildRequest(DataUsageRequest request, int callingUid) { - // For non-system uid, cap the minimum threshold to a safe default to avoid too - // many callbacks. - long thresholdInBytes = (callingUid == Process.SYSTEM_UID ? request.thresholdInBytes + private DataUsageRequest buildRequest(Context context, DataUsageRequest request, + int callingUid) { + // For non-NETWORK_STACK permission uid, cap the minimum threshold to a safe default to + // avoid too many callbacks. + final long thresholdInBytes = (context.checkPermission( + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, Process.myPid(), callingUid) + == PackageManager.PERMISSION_GRANTED ? request.thresholdInBytes : Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes)); if (thresholdInBytes > request.thresholdInBytes) { Log.w(TAG, "Threshold was too low for " + request diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java index c371f0859aa1..a006cd597568 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java @@ -471,9 +471,11 @@ public class NetworkStatsRecorder { public void dumpDebugLocked(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); if (mPending != null) { - proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES, mPending.getTotalBytes()); + proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES_FIELD_NUMBER, + mPending.getTotalBytes()); } - getOrLoadCompleteLocked().dumpDebug(proto, NetworkStatsRecorderProto.COMPLETE_HISTORY); + getOrLoadCompleteLocked().dumpDebug(proto, + NetworkStatsRecorderProto.COMPLETE_HISTORY_FIELD_NUMBER); proto.end(start); } diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java index 9f3371b724cf..ef6f39a5c040 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java @@ -51,6 +51,7 @@ import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID; import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG; import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT; import static android.os.Trace.TRACE_TAG_NETWORK; +import static android.system.OsConstants.ENOENT; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; @@ -62,6 +63,7 @@ import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.usage.NetworkStatsManager; @@ -102,6 +104,7 @@ import android.net.netstats.provider.INetworkStatsProvider; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.netstats.provider.NetworkStatsProvider; import android.os.Binder; +import android.os.Build; import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; @@ -120,7 +123,6 @@ import android.provider.Settings.Global; import android.service.NetworkInterfaceProto; import android.service.NetworkStatsServiceDumpProto; import android.system.ErrnoException; -import android.system.Os; import android.telephony.PhoneStateListener; import android.telephony.SubscriptionPlan; import android.text.TextUtils; @@ -169,7 +171,12 @@ import java.util.concurrent.TimeUnit; * Collect and persist detailed network statistics, and provide this data to * other system services. */ +@TargetApi(Build.VERSION_CODES.TIRAMISU) public class NetworkStatsService extends INetworkStatsService.Stub { + static { + System.loadLibrary("service-connectivity"); + } + static final String TAG = "NetworkStats"; static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE); @@ -214,6 +221,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // This is current path but may be changed soon. private static final String UID_COUNTERSET_MAP_PATH = "/sys/fs/bpf/map_netd_uid_counterset_map"; + private static final String COOKIE_TAG_MAP_PATH = + "/sys/fs/bpf/map_netd_cookie_tag_map"; + private static final String APP_UID_STATS_MAP_PATH = + "/sys/fs/bpf/map_netd_app_uid_stats_map"; + private static final String STATS_MAP_A_PATH = + "/sys/fs/bpf/map_netd_stats_map_A"; + private static final String STATS_MAP_B_PATH = + "/sys/fs/bpf/map_netd_stats_map_B"; private final Context mContext; private final NetworkStatsFactory mStatsFactory; @@ -341,6 +356,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ private SparseIntArray mActiveUidCounterSet = new SparseIntArray(); private final IBpfMap<U32, U8> mUidCounterSetMap; + private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap; + private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapA; + private final IBpfMap<StatsMapKey, StatsMapValue> mStatsMapB; + private final IBpfMap<UidStatsMapKey, StatsMapValue> mAppUidStatsMap; /** Data layer operation counters for splicing into other structures. */ private NetworkStats mUidOperations = new NetworkStats(0L, 10); @@ -474,6 +493,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mInterfaceMapUpdater = mDeps.makeBpfInterfaceMapUpdater(mContext, mHandler); mInterfaceMapUpdater.start(); mUidCounterSetMap = mDeps.getUidCounterSetMap(); + mCookieTagMap = mDeps.getCookieTagMap(); + mStatsMapA = mDeps.getStatsMapA(); + mStatsMapB = mDeps.getStatsMapB(); + mAppUidStatsMap = mDeps.getAppUidStatsMap(); } /** @@ -547,8 +570,48 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - public TagStatsDeleter getTagStatsDeleter() { - return NetworkStatsService::nativeDeleteTagData; + /** Gets the cookie tag map */ + public IBpfMap<CookieTagMapKey, CookieTagMapValue> getCookieTagMap() { + try { + return new BpfMap<CookieTagMapKey, CookieTagMapValue>(COOKIE_TAG_MAP_PATH, + BpfMap.BPF_F_RDWR, CookieTagMapKey.class, CookieTagMapValue.class); + } catch (ErrnoException e) { + Log.wtf(TAG, "Cannot create cookie tag map: " + e); + return null; + } + } + + /** Gets stats map A */ + public IBpfMap<StatsMapKey, StatsMapValue> getStatsMapA() { + try { + return new BpfMap<StatsMapKey, StatsMapValue>(STATS_MAP_A_PATH, + BpfMap.BPF_F_RDWR, StatsMapKey.class, StatsMapValue.class); + } catch (ErrnoException e) { + Log.wtf(TAG, "Cannot create stats map A: " + e); + return null; + } + } + + /** Gets stats map B */ + public IBpfMap<StatsMapKey, StatsMapValue> getStatsMapB() { + try { + return new BpfMap<StatsMapKey, StatsMapValue>(STATS_MAP_B_PATH, + BpfMap.BPF_F_RDWR, StatsMapKey.class, StatsMapValue.class); + } catch (ErrnoException e) { + Log.wtf(TAG, "Cannot create stats map B: " + e); + return null; + } + } + + /** Gets the uid stats map */ + public IBpfMap<UidStatsMapKey, StatsMapValue> getAppUidStatsMap() { + try { + return new BpfMap<UidStatsMapKey, StatsMapValue>(APP_UID_STATS_MAP_PATH, + BpfMap.BPF_F_RDWR, UidStatsMapKey.class, StatsMapValue.class); + } catch (ErrnoException e) { + Log.wtf(TAG, "Cannot create app uid stats map: " + e); + return null; + } } } @@ -1060,7 +1123,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getUidStatsForTransport(int transport) { - enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); + PermissionUtils.enforceNetworkStackPermission(mContext); try { final String[] relevantIfaces = transport == TRANSPORT_WIFI ? mWifiIfaces : mMobileIfaces; @@ -1222,7 +1285,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { DataUsageRequest normalizedRequest; final long token = Binder.clearCallingIdentity(); try { - normalizedRequest = mStatsObservers.register( + normalizedRequest = mStatsObservers.register(mContext, request, callback, callingUid, accessLevel); } finally { Binder.restoreCallingIdentity(token); @@ -1477,10 +1540,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.isMetered()) { // Copy the identify from IMS one but mark it as metered. - NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(), - ident.getRatType(), ident.getSubscriberId(), ident.getWifiNetworkKey(), - ident.isRoaming(), true /* metered */, - true /* onDefaultNetwork */, ident.getOemManaged()); + NetworkIdentity vtIdent = new NetworkIdentity.Builder() + .setType(ident.getType()) + .setRatType(ident.getRatType()) + .setSubscriberId(ident.getSubscriberId()) + .setWifiNetworkKey(ident.getWifiNetworkKey()) + .setRoaming(ident.isRoaming()).setMetered(true) + .setDefaultNetwork(true) + .setOemManaged(ident.getOemManaged()) + .setSubId(ident.getSubId()).build(); final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot); findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent); findOrCreateNetworkIdentitySet(mActiveUidIfaces, ifaceVt).add(vtIdent); @@ -1790,6 +1858,69 @@ public class NetworkStatsService extends INetworkStatsService.Stub { currentTime); } + // deleteKernelTagData can ignore ENOENT; otherwise we should log an error + private void logErrorIfNotErrNoent(final ErrnoException e, final String msg) { + if (e.errno != ENOENT) Log.e(TAG, msg, e); + } + + private <K extends StatsMapKey, V extends StatsMapValue> void deleteStatsMapTagData( + IBpfMap<K, V> statsMap, int uid) { + try { + statsMap.forEach((key, value) -> { + if (key.uid == uid) { + try { + statsMap.deleteEntry(key); + } catch (ErrnoException e) { + logErrorIfNotErrNoent(e, "Failed to delete data(uid = " + key.uid + ")"); + } + } + }); + } catch (ErrnoException e) { + Log.e(TAG, "FAILED to delete tag data from stats map", e); + } + } + + /** + * Deletes uid tag data from CookieTagMap, StatsMapA, StatsMapB, and UidStatsMap + * @param uid + */ + private void deleteKernelTagData(int uid) { + try { + mCookieTagMap.forEach((key, value) -> { + // If SkDestroyListener deletes the socket tag while this code is running, + // forEach will either restart iteration from the beginning or return null, + // depending on when the deletion happens. + // If it returns null, continue iteration to delete the data and in fact it would + // just iterate from first key because BpfMap#getNextKey would return first key + // if the current key is not exist. + if (value != null && value.uid == uid) { + try { + mCookieTagMap.deleteEntry(key); + } catch (ErrnoException e) { + logErrorIfNotErrNoent(e, "Failed to delete data(cookie = " + key + ")"); + } + } + }); + } catch (ErrnoException e) { + Log.e(TAG, "Failed to delete tag data from cookie tag map", e); + } + + deleteStatsMapTagData(mStatsMapA, uid); + deleteStatsMapTagData(mStatsMapB, uid); + + try { + mUidCounterSetMap.deleteEntry(new U32(uid)); + } catch (ErrnoException e) { + logErrorIfNotErrNoent(e, "Failed to delete tag data from uid counter set map"); + } + + try { + mAppUidStatsMap.deleteEntry(new UidStatsMapKey(uid)); + } catch (ErrnoException e) { + logErrorIfNotErrNoent(e, "Failed to delete tag data from app uid stats map"); + } + } + /** * Clean up {@link #mUidRecorder} after UID is removed. */ @@ -1805,10 +1936,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // Clear kernel stats associated with UID for (int uid : uids) { - final int ret = mDeps.getTagStatsDeleter().deleteTagData(uid); - if (ret < 0) { - Log.w(TAG, "problem clearing counters for uid " + uid + ": " + Os.strerror(-ret)); - } + deleteKernelTagData(uid); } } @@ -2004,12 +2132,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // TODO Right now it writes all history. Should it limit to the "since-boot" log? - dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES, mActiveIfaces); - dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES, mActiveUidIfaces); - mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS); - mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS); - mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS); - mUidTagRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_TAG_STATS); + dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES_FIELD_NUMBER, + mActiveIfaces); + dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES_FIELD_NUMBER, + mActiveUidIfaces); + mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS_FIELD_NUMBER); + mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS_FIELD_NUMBER); + mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS_FIELD_NUMBER); + mUidTagRecorder.dumpDebugLocked(proto, + NetworkStatsServiceDumpProto.UID_TAG_STATS_FIELD_NUMBER); proto.flush(); } @@ -2019,8 +2150,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { for (int i = 0; i < ifaces.size(); i++) { final long start = proto.start(tag); - proto.write(NetworkInterfaceProto.INTERFACE, ifaces.keyAt(i)); - ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES); + proto.write(NetworkInterfaceProto.INTERFACE_FIELD_NUMBER, ifaces.keyAt(i)); + ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES_FIELD_NUMBER); proto.end(start); } @@ -2387,12 +2518,4 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static native long nativeGetTotalStat(int type); private static native long nativeGetIfaceStat(String iface, int type); private static native long nativeGetUidStat(int uid, int type); - - // TODO: use BpfNetMaps to delete tag data and remove this. - @VisibleForTesting - interface TagStatsDeleter { - int deleteTagData(int uid); - } - - private static native int nativeDeleteTagData(int uid); } diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java index 5bba0b17aa42..65ccd2007299 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java @@ -23,7 +23,9 @@ import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NS import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE; import android.annotation.NonNull; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.telephony.SubscriptionManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyDisplayInfo; @@ -43,6 +45,7 @@ import java.util.concurrent.Executor; /** * Helper class that watches for events that are triggered per subscription. */ +@TargetApi(Build.VERSION_CODES.TIRAMISU) public class NetworkStatsSubscriptionsMonitor extends SubscriptionManager.OnSubscriptionsChangedListener { diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java index 72230b4062e1..4117d0f07e0f 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java @@ -177,7 +177,7 @@ public class SparseInputStream extends InputStream { ret = 0; break; case SparseChunk.FILL: - ret = mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3]; + ret = Byte.toUnsignedInt(mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3]); break; default: throw new IOException("Unsupported Chunk:" + mCur.toString()); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index b44d9147c2b0..7e5c1240cc63 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -140,13 +140,6 @@ public class HeadsetProfile implements LocalBluetoothProfile { return (activeDevices.size() > 0) ? activeDevices.get(0) : null; } - public boolean isAudioOn() { - if (mService == null) { - return false; - } - return mService.isAudioOn(); - } - public int getAudioState(BluetoothDevice device) { if (mService == null) { return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/OWNERS b/packages/SettingsLib/src/com/android/settingslib/devicestate/OWNERS new file mode 100644 index 000000000000..98f41234feb1 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/OWNERS @@ -0,0 +1,3 @@ +# Default reviewers for this and subdirectories. +alexflo@google.com +chrisgollner@google.com diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java index 2c2be0394b6e..c7eb68240c5b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java +++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java @@ -70,7 +70,7 @@ class LicenseHtmlGeneratorFromXml { + "</style>\n" + "</head>" + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n" - + "<div class=\"toc\">\n"; + + "<div class=\"toc\">"; private static final String LIBRARY_HEAD_STRING = "<strong>Libraries</strong>\n<ul class=\"libraries\">"; private static final String LIBRARY_TAIL_STRING = "</ul>\n<strong>Files</strong>"; @@ -324,6 +324,8 @@ class LicenseHtmlGeneratorFromXml { writer.println(LIBRARY_TAIL_STRING); } + writer.println(FILES_HEAD_STRING); + // Prints all the file list with a link to its license file content. for (String fileName : fileNameList) { for (Map.Entry<String, Set<String>> libToContentId : diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS new file mode 100644 index 000000000000..98f41234feb1 --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/OWNERS @@ -0,0 +1,3 @@ +# Default reviewers for this and subdirectories. +alexflo@google.com +chrisgollner@google.com diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java index d7b366e60a1d..bb6b293b0c27 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java @@ -206,9 +206,8 @@ public class A2dpProfileTest { when(config.isMandatoryCodec()).thenReturn(false); when(config.getCodecType()).thenReturn(4); - when(config.getCodecName()).thenReturn("LDAC"); assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo( - String.format(KNOWN_CODEC_LABEL, config.getCodecName())); + String.format(KNOWN_CODEC_LABEL, "LDAC")); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java index d53a3e896868..298ee90d311d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.content.Context; +import android.os.Parcel; import android.os.ParcelUuid; import org.junit.Before; @@ -60,9 +61,9 @@ public class CachedBluetoothDeviceManagerTest { private final static Map<Integer, ParcelUuid> CAP_GROUP2 = Map.of(2, BluetoothUuid.CAP); private final BluetoothClass DEVICE_CLASS_1 = - new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES); + createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES); private final BluetoothClass DEVICE_CLASS_2 = - new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE); + createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE); @Mock private LocalBluetoothProfileManager mLocalProfileManager; @Mock @@ -92,6 +93,16 @@ public class CachedBluetoothDeviceManagerTest { private HearingAidDeviceManager mHearingAidDeviceManager; private Context mContext; + private BluetoothClass createBtClass(int deviceClass) { + Parcel p = Parcel.obtain(); + p.writeInt(deviceClass); + p.setDataPosition(0); // reset position of parcel before passing to constructor + + BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p); + p.recycle(); + return bluetoothClass; + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java index 30182c476855..f5ce6647e531 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java @@ -55,15 +55,6 @@ public class HeadsetProfileTest { } @Test - public void bluetoothProfile_shouldReturnTheAudioStatusFromBlueToothHeadsetService() { - when(mService.isAudioOn()).thenReturn(true); - assertThat(mProfile.isAudioOn()).isTrue(); - - when(mService.isAudioOn()).thenReturn(false); - assertThat(mProfile.isAudioOn()).isFalse(); - } - - @Test public void testHeadsetProfile_shouldReturnAudioState() { when(mService.getAudioState(mBluetoothDevice)). thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java index 7be176a37bb4..a8e607549ab1 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java @@ -28,6 +28,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.os.Parcel; import org.junit.Before; import org.junit.Test; @@ -48,7 +49,7 @@ public class HearingAidDeviceManagerTest { private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11"; private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22"; private final BluetoothClass DEVICE_CLASS = - new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE); + createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE); @Mock private LocalBluetoothProfileManager mLocalProfileManager; @Mock @@ -67,6 +68,16 @@ public class HearingAidDeviceManagerTest { private HearingAidDeviceManager mHearingAidDeviceManager; private Context mContext; + private BluetoothClass createBtClass(int deviceClass) { + Parcel p = Parcel.obtain(); + p.writeInt(deviceClass); + p.setDataPosition(0); // reset position of parcel before passing to constructor + + BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p); + p.recycle(); + return bluetoothClass; + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java index e348865019ec..09b0d7f56e18 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java @@ -120,7 +120,7 @@ public class LicenseHtmlGeneratorFromXmlTest { + "</div><!-- table of contents -->\n" + "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n" + "<tr id=\"id0\"><td class=\"same-license\">\n" - + "<div class=\"label\">Notices for file(s):</div>\n" + + "<div class=\"label\"><strong>libA</strong> used by:</div>\n" + "<div class=\"file-list\">\n" + "/file0 <br/>\n" + "/file1 <br/>\n" @@ -130,7 +130,7 @@ public class LicenseHtmlGeneratorFromXmlTest { + "</pre><!-- license-text -->\n" + "</td></tr><!-- same-license -->\n" + "<tr id=\"id1\"><td class=\"same-license\">\n" - + "<div class=\"label\">Notices for file(s):</div>\n" + + "<div class=\"label\"><strong>libB</strong> used by:</div>\n" + "<div class=\"file-list\">\n" + "/file0 <br/>\n" + "</div><!-- file-list -->\n" @@ -158,10 +158,12 @@ public class LicenseHtmlGeneratorFromXmlTest { LicenseHtmlGeneratorFromXml.parse( new InputStreamReader(new ByteArrayInputStream(VALID_OLD_XML_STRING.getBytes())), fileNameToLibraryToContentIdMap, contentIdToFileContentMap); - assertThat(fileNameToLibraryToContentIdMap.size()).isEqualTo(1); - assertThat(fileNameToLibraryToContentIdMap.get("").size()).isEqualTo(2); - assertThat(fileNameToLibraryToContentIdMap.get("").get("/file0")).containsExactly("0"); - assertThat(fileNameToLibraryToContentIdMap.get("").get("/file1")).containsExactly("0"); + + assertThat(fileNameToLibraryToContentIdMap).hasSize(2); + assertThat(fileNameToLibraryToContentIdMap.get("/file0")).hasSize(1); + assertThat(fileNameToLibraryToContentIdMap.get("/file1")).hasSize(1); + assertThat(fileNameToLibraryToContentIdMap.get("/file0").get(null)).containsExactly("0"); + assertThat(fileNameToLibraryToContentIdMap.get("/file1").get(null)).containsExactly("0"); assertThat(contentIdToFileContentMap.size()).isEqualTo(1); assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0"); } @@ -174,11 +176,12 @@ public class LicenseHtmlGeneratorFromXmlTest { LicenseHtmlGeneratorFromXml.parse( new InputStreamReader(new ByteArrayInputStream(VALID_NEW_XML_STRING.getBytes())), fileNameToLibraryToContentIdMap, contentIdToFileContentMap); - assertThat(fileNameToLibraryToContentIdMap.size()).isEqualTo(2); - assertThat(fileNameToLibraryToContentIdMap.get("libA").size()).isEqualTo(1); - assertThat(fileNameToLibraryToContentIdMap.get("libB").size()).isEqualTo(1); - assertThat(fileNameToLibraryToContentIdMap.get("libA").get("/file0")).containsExactly("0"); - assertThat(fileNameToLibraryToContentIdMap.get("libB").get("/file1")).containsExactly("0"); + + assertThat(fileNameToLibraryToContentIdMap).hasSize(2); + assertThat(fileNameToLibraryToContentIdMap.get("/file0")).hasSize(1); + assertThat(fileNameToLibraryToContentIdMap.get("/file1")).hasSize(1); + assertThat(fileNameToLibraryToContentIdMap.get("/file0").get("libA")).containsExactly("0"); + assertThat(fileNameToLibraryToContentIdMap.get("/file1").get("libB")).containsExactly("0"); assertThat(contentIdToFileContentMap.size()).isEqualTo(1); assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0"); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java index 6f7f73a3e79b..c122a37d0f79 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java @@ -31,6 +31,7 @@ import android.bluetooth.BluetoothDevice; import android.content.Context; import android.media.MediaRoute2Info; import android.media.MediaRouter2Manager; +import android.os.Parcel; import com.android.settingslib.bluetooth.A2dpProfile; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -65,9 +66,9 @@ public class MediaDeviceTest { private static final String ROUTER_ID_3 = "RouterId_3"; private static final String TEST_PACKAGE_NAME = "com.test.playmusic"; private final BluetoothClass mHeadreeClass = - new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES); + createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES); private final BluetoothClass mCarkitClass = - new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO); + createBtClass(BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO); @Mock private BluetoothDevice mDevice1; @@ -118,6 +119,16 @@ public class MediaDeviceTest { private List<MediaDevice> mMediaDevices = new ArrayList<>(); private PhoneMediaDevice mPhoneMediaDevice; + private BluetoothClass createBtClass(int deviceClass) { + Parcel p = Parcel.obtain(); + p.writeInt(deviceClass); + p.setDataPosition(0); // reset position of parcel before passing to constructor + + BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p); + p.recycle(); + return bluetoothClass; + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java index 3b7fbc73522f..c7e96bcdb856 100644 --- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java +++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java @@ -69,7 +69,7 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto } @Implementation - protected boolean removeActiveDevice(@BluetoothAdapter.ActiveDeviceUse int profiles) { + protected boolean removeActiveDevice(int profiles) { if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL && profiles != ACTIVE_DEVICE_ALL) { return false; @@ -78,8 +78,7 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto } @Implementation - protected boolean setActiveDevice(BluetoothDevice device, - @BluetoothAdapter.ActiveDeviceUse int profiles) { + protected boolean setActiveDevice(BluetoothDevice device, int profiles) { if (device == null) { return false; } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 074d85c1007e..ca4dcbbf230e 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -509,6 +509,9 @@ <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" /> <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" /> + <!-- Permission needed for CTS test - ConcurrencyTest#testP2pExternalApprover + P2P external approver API sets require MANAGE_WIFI_AUTO_JOIN permission. --> + <uses-permission android:name="android.permission.MANAGE_WIFI_AUTO_JOIN" /> <!-- Permission required for CTS tests to enable/disable rate limiting toasts. --> <uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" /> @@ -556,6 +559,9 @@ <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/> + <!-- Permission required for CTS test - MediaCodecResourceTest --> + <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" /> + <!-- Permission required for CTS test - ResourceObserverNativeTest --> <uses-permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" /> diff --git a/services/Android.bp b/services/Android.bp index 8947393849c1..e73032198343 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -42,7 +42,9 @@ system_optimized_java_defaults { SYSTEM_OPTIMIZE_JAVA: { optimize: { enabled: true, - optimize: true, + // TODO(b/210510433): Enable optimizations after improving + // retracing infra. + optimize: false, shrink: true, proguard_flags_files: ["proguard.flags"], }, @@ -176,10 +178,6 @@ cc_library_shared { name: "libandroid_servers", defaults: ["libservices.core-libs"], whole_static_libs: ["libservices.core"], - required: [ - // TODO: remove after NetworkStatsService is moved to the mainline module. - "libcom_android_net_module_util_jni", - ], } platform_compat_config { diff --git a/services/autofill/OWNERS b/services/autofill/OWNERS index c52751d79227..edfb2112198a 100644 --- a/services/autofill/OWNERS +++ b/services/autofill/OWNERS @@ -1 +1 @@ -include /core/java/android/service/autofill/OWNERS +include /core/java/android/view/autofill/OWNERS diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java index 8ea3dd64697e..1095ba31069f 100644 --- a/services/core/java/com/android/server/DiskStatsService.java +++ b/services/core/java/com/android/server/DiskStatsService.java @@ -125,6 +125,8 @@ public class DiskStatsService extends Binder { DiskStatsFreeSpaceProto.FOLDER_CACHE); reportFreeSpace(new File("/system"), "System", pw, proto, DiskStatsFreeSpaceProto.FOLDER_SYSTEM); + reportFreeSpace(Environment.getMetadataDirectory(), "Metadata", pw, proto, + DiskStatsFreeSpaceProto.FOLDER_METADATA); boolean fileBased = StorageManager.isFileEncryptedNativeOnly(); boolean blockBased = fileBased ? false : StorageManager.isBlockEncrypted(); diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index ff2308c35b9f..1e534b7c1515 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.annotation.NonNull; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.timedetector.NetworkTimeSuggestion; @@ -35,11 +36,15 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.PowerManager; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.SystemClock; import android.os.TimestampedValue; import android.provider.Settings; +import android.util.LocalLog; import android.util.Log; import android.util.NtpTrustedTime; +import android.util.NtpTrustedTime.TimeResult; import android.util.TimeUtils; import com.android.internal.util.DumpUtils; @@ -95,6 +100,13 @@ public class NetworkTimeUpdateService extends Binder { // connection to happen. private int mTryAgainCounter; + /** + * A log that records the decisions to fetch a network time update. + * This is logged in bug reports to assist with debugging issues with network time suggestions. + */ + @NonNull + private final LocalLog mLocalLog = new LocalLog(30, false /* useLocalTimestamps */); + public NetworkTimeUpdateService(Context context) { mContext = context; mTime = NtpTrustedTime.getInstance(context); @@ -143,6 +155,42 @@ public class NetworkTimeUpdateService extends Binder { }, new IntentFilter(ACTION_POLL)); } + /** + * Clears the cached NTP time. For use during tests to simulate when no NTP time is available. + * + * <p>This operation takes place in the calling thread rather than the service's handler thread. + */ + void clearTimeForTests() { + mContext.enforceCallingPermission( + android.Manifest.permission.SET_TIME, "clear latest network time"); + + mTime.clearCachedTimeResult(); + + mLocalLog.log("clearTimeForTests"); + } + + /** + * Forces the service to refresh the NTP time. + * + * <p>This operation takes place in the calling thread rather than the service's handler thread. + * This method does not affect currently scheduled refreshes. If the NTP request is successful + * it will make an (asynchronously handled) suggestion to the time detector. + */ + boolean forceRefreshForTests() { + mContext.enforceCallingPermission( + android.Manifest.permission.SET_TIME, "force network time refresh"); + + boolean success = mTime.forceRefresh(); + mLocalLog.log("forceRefreshForTests: success=" + success); + + if (success) { + makeNetworkTimeSuggestion(mTime.getCachedTimeResult(), + "Origin: NetworkTimeUpdateService: forceRefreshForTests"); + } + + return success; + } + private void onPollNetworkTime(int event) { // If we don't have any default network, don't bother. if (mDefaultNetwork == null) return; @@ -155,24 +203,37 @@ public class NetworkTimeUpdateService extends Binder { } private void onPollNetworkTimeUnderWakeLock(int event) { + long currentElapsedRealtimeMillis = SystemClock.elapsedRealtime(); // Force an NTP fix when outdated NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult(); - if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) { + if (cachedNtpResult == null || cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis) + >= mPollingIntervalMs) { if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh"); - mTime.forceRefresh(); + boolean isSuccessful = mTime.forceRefresh(); + if (isSuccessful) { + mTryAgainCounter = 0; + } else { + String logMsg = "forceRefresh() returned false: cachedNtpResult=" + cachedNtpResult + + ", currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis; + + if (DBG) { + Log.d(TAG, logMsg); + } + mLocalLog.log(logMsg); + } + cachedNtpResult = mTime.getCachedTimeResult(); } - if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) { + if (cachedNtpResult != null + && cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis) + < mPollingIntervalMs) { // Obtained fresh fix; schedule next normal update - resetAlarm(mPollingIntervalMs); - - // Suggest the time to the time detector. It may choose use it to set the system clock. - TimestampedValue<Long> timeSignal = new TimestampedValue<>( - cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis()); - NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal); - timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event); - mTimeDetector.suggestNetworkTime(timeSuggestion); + resetAlarm(mPollingIntervalMs + - cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis)); + + makeNetworkTimeSuggestion(cachedNtpResult, + "Origin: NetworkTimeUpdateService. event=" + event); } else { // No fresh fix; schedule retry mTryAgainCounter++; @@ -180,12 +241,26 @@ public class NetworkTimeUpdateService extends Binder { resetAlarm(mPollingIntervalShorterMs); } else { // Try much later + String logMsg = "mTryAgainTimesMax exceeded, cachedNtpResult=" + cachedNtpResult; + if (DBG) { + Log.d(TAG, logMsg); + } + mLocalLog.log(logMsg); mTryAgainCounter = 0; resetAlarm(mPollingIntervalMs); } } } + /** Suggests the time to the time detector. It may choose use it to set the system clock. */ + private void makeNetworkTimeSuggestion(TimeResult ntpResult, String debugInfo) { + TimestampedValue<Long> timeSignal = new TimestampedValue<>( + ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis()); + NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal); + timeSuggestion.addDebugInfo(debugInfo); + mTimeDetector.suggestNetworkTime(timeSuggestion); + } + /** * Cancel old alarm and starts a new one for the specified interval. * @@ -285,6 +360,15 @@ public class NetworkTimeUpdateService extends Binder { if (ntpResult != null) { pw.println("NTP result age: " + ntpResult.getAgeMillis()); } + pw.println("Local logs:"); + mLocalLog.dump(fd, pw, args); pw.println(); } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + new NetworkTimeUpdateServiceShellCommand(this).exec( + this, in, out, err, args, callback, resultReceiver); + } } diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java new file mode 100644 index 000000000000..dc93023d82c5 --- /dev/null +++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 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.server; + +import android.annotation.NonNull; +import android.os.ShellCommand; + +import java.io.PrintWriter; +import java.util.Objects; + +/** Implements the shell command interface for {@link NetworkTimeUpdateService}. */ +class NetworkTimeUpdateServiceShellCommand extends ShellCommand { + + /** + * The name of the service. + */ + private static final String SHELL_COMMAND_SERVICE_NAME = "network_time_update_service"; + + /** + * A shell command that clears the time signal received from the network. + */ + private static final String SHELL_COMMAND_CLEAR_TIME = "clear_time"; + + /** + * A shell command that forces the time signal to be refreshed from the network. + */ + private static final String SHELL_COMMAND_FORCE_REFRESH = "force_refresh"; + + @NonNull + private final NetworkTimeUpdateService mNetworkTimeUpdateService; + + NetworkTimeUpdateServiceShellCommand(NetworkTimeUpdateService networkTimeUpdateService) { + mNetworkTimeUpdateService = Objects.requireNonNull(networkTimeUpdateService); + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + + switch (cmd) { + case SHELL_COMMAND_CLEAR_TIME: + return runClearTime(); + case SHELL_COMMAND_FORCE_REFRESH: + return runForceRefresh(); + default: { + return handleDefaultCommands(cmd); + } + } + } + + private int runClearTime() { + mNetworkTimeUpdateService.clearTimeForTests(); + return 0; + } + + private int runForceRefresh() { + boolean success = mNetworkTimeUpdateService.forceRefreshForTests(); + getOutPrintWriter().println(success); + return 0; + } + + @Override + public void onHelp() { + final PrintWriter pw = getOutPrintWriter(); + pw.printf("Network Time Update Service (%s) commands:\n", SHELL_COMMAND_SERVICE_NAME); + pw.printf(" help\n"); + pw.printf(" Print this help text.\n"); + pw.printf(" %s\n", SHELL_COMMAND_CLEAR_TIME); + pw.printf(" Clears the latest time.\n"); + pw.printf(" %s\n", SHELL_COMMAND_FORCE_REFRESH); + pw.printf(" Refreshes the latest time. Prints whether it was successful.\n"); + pw.println(); + } +} diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 71b463a4db8c..f4b8f5ddda66 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -23,14 +23,13 @@ per-file *Alarm* = file:/apex/jobscheduler/OWNERS per-file *AppOp* = file:/core/java/android/permission/OWNERS per-file *Battery* = file:/BATTERY_STATS_OWNERS per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS -per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS per-file **IpSec* = file:/services/core/java/com/android/server/net/OWNERS per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS per-file *Storage* = file:/core/java/android/os/storage/OWNERS -per-file *TimeUpdate* = file:/core/java/android/app/timezone/OWNERS +per-file *TimeUpdate* = file:/services/core/java/com/android/server/timezonedetector/OWNERS per-file DynamicSystemService.java = file:/packages/DynamicSystemInstallationService/OWNERS per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo:/OWNERS per-file MmsServiceBroker.java = file:/telephony/OWNERS diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 3cc8e7672557..f36d11eac8f2 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.UserInfo; import android.os.Environment; import android.os.SystemClock; @@ -34,6 +36,7 @@ import com.android.internal.os.SystemServerClassLoaderFactory; import com.android.internal.util.Preconditions; import com.android.server.SystemService.TargetUser; import com.android.server.am.EventLogTags; +import com.android.server.pm.ApexManager; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.TimingsTraceAndSlog; @@ -42,6 +45,8 @@ import dalvik.system.PathClassLoader; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; /** @@ -114,12 +119,31 @@ public final class SystemServiceManager implements Dumpable { * @return The service instance. */ public SystemService startServiceFromJar(String className, String path) { - PathClassLoader pathClassLoader = SystemServerClassLoaderFactory.getOrCreateClassLoader( - path, this.getClass().getClassLoader()); + PathClassLoader pathClassLoader = + SystemServerClassLoaderFactory.getOrCreateClassLoader( + path, this.getClass().getClassLoader(), isJarInTestApex(path)); final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader); return startService(serviceClass); } + /** + * Returns true if the jar is in a test APEX. + */ + private static boolean isJarInTestApex(String pathStr) { + Path path = Paths.get(pathStr); + if (path.getNameCount() >= 2 && path.getName(0).toString().equals("apex")) { + String apexModuleName = path.getName(1).toString(); + ApexManager apexManager = ApexManager.getInstance(); + String packageName = apexManager.getActivePackageNameForApexModuleName(apexModuleName); + PackageInfo packageInfo = apexManager.getPackageInfo( + packageName, ApexManager.MATCH_ACTIVE_PACKAGE); + if (packageInfo != null) { + return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0; + } + } + return false; + } + /* * Loads and initializes a class from the given classLoader. Returns the class. */ diff --git a/services/core/java/com/android/server/am/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java index 6e39a4c802d9..651e98c602d9 100644 --- a/services/core/java/com/android/server/am/DataConnectionStats.java +++ b/services/core/java/com/android/server/am/DataConnectionStats.java @@ -73,7 +73,8 @@ public class DataConnectionStats extends BroadcastReceiver { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); - mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler); + mContext.registerReceiver(this, filter, null /* broadcastPermission */, mListenerHandler, + Context.RECEIVER_NOT_EXPORTED); } @Override diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index f813ae011fee..4485c5b6d61a 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -102,8 +102,6 @@ public class BtHelper { /*package*/ static final int SCO_MODE_UNDEFINED = -1; // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall()) /*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0; - // SCO audio mode is raw audio (BluetoothHeadset.connectAudio()) - private static final int SCO_MODE_RAW = 1; // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition()) private static final int SCO_MODE_VR = 2; // max valid SCO audio mode values @@ -122,8 +120,6 @@ public class BtHelper { return "SCO_MODE_UNDEFINED"; case SCO_MODE_VIRTUAL_CALL: return "SCO_MODE_VIRTUAL_CALL"; - case SCO_MODE_RAW: - return "SCO_MODE_RAW"; case SCO_MODE_VR: return "SCO_MODE_VR"; default: @@ -482,6 +478,8 @@ public class BtHelper { } if (profile == BluetoothProfile.A2DP) { mA2dp = (BluetoothA2dp) proxy; + } else if (profile == BluetoothProfile.HEARING_AID) { + mHearingAid = (BluetoothHearingAid) proxy; } else if (profile == BluetoothProfile.LE_AUDIO) { mLeAudio = (BluetoothLeAudio) proxy; } @@ -812,8 +810,6 @@ public class BtHelper { private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode) { switch (scoAudioMode) { - case SCO_MODE_RAW: - return bluetoothHeadset.disconnectAudio(); case SCO_MODE_VIRTUAL_CALL: return bluetoothHeadset.stopScoUsingVirtualVoiceCall(); case SCO_MODE_VR: @@ -826,8 +822,6 @@ public class BtHelper { private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode) { switch (scoAudioMode) { - case SCO_MODE_RAW: - return bluetoothHeadset.connectAudio(); case SCO_MODE_VIRTUAL_CALL: return bluetoothHeadset.startScoUsingVirtualVoiceCall(); case SCO_MODE_VR: diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java index 603f20633cfb..108e7bcb23bb 100644 --- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java +++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java @@ -25,7 +25,6 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; -import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN; @@ -191,6 +190,7 @@ public class MultipathPolicyTracker { class MultipathTracker { final Network network; final String subscriberId; + private final int mSubId; private long mQuota; /** Current multipath budget. Nonzero iff we have budget and a UsageCallback is armed. */ @@ -204,9 +204,8 @@ public class MultipathPolicyTracker { this.network = network; this.mNetworkCapabilities = new NetworkCapabilities(nc); NetworkSpecifier specifier = nc.getNetworkSpecifier(); - int subId = INVALID_SUBSCRIPTION_ID; if (specifier instanceof TelephonyNetworkSpecifier) { - subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); + mSubId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); } else { throw new IllegalStateException(String.format( "Can't get subId from mobile network %s (%s)", @@ -217,14 +216,14 @@ public class MultipathPolicyTracker { if (tele == null) { throw new IllegalStateException(String.format("Missing TelephonyManager")); } - tele = tele.createForSubscriptionId(subId); + tele = tele.createForSubscriptionId(mSubId); if (tele == null) { throw new IllegalStateException(String.format( - "Can't get TelephonyManager for subId %d", subId)); + "Can't get TelephonyManager for subId %d", mSubId)); } subscriberId = Objects.requireNonNull(tele.getSubscriberId(), - "Null subscriber Id for subId " + subId); + "Null subscriber Id for subId " + mSubId); mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE) .setSubscriberIds(Set.of(subscriberId)) .setMeteredness(NetworkStats.METERED_YES) @@ -282,6 +281,7 @@ public class MultipathPolicyTracker { .setSubscriberId(subscriberId) .setRoaming(!nc.hasCapability(NET_CAPABILITY_NOT_ROAMING)) .setMetered(!nc.hasCapability(NET_CAPABILITY_NOT_METERED)) + .setSubId(mSubId) .build(); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 3fb49e412ed2..30ae7d6b8d21 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -2006,7 +2006,8 @@ public class Vpn { .setCategory(Notification.CATEGORY_SYSTEM) .setVisibility(Notification.VISIBILITY_PUBLIC) .setOngoing(true) - .setColor(mContext.getColor(R.color.system_notification_accent_color)); + .setColor(mContext.getColor( + android.R.color.system_notification_accent_color)); notificationManager.notify(TAG, SystemMessage.NOTE_VPN_DISCONNECTED, builder.build()); } finally { Binder.restoreCallingIdentity(token); diff --git a/services/core/java/com/android/server/infra/OWNERS b/services/core/java/com/android/server/infra/OWNERS new file mode 100644 index 000000000000..0466d8a88053 --- /dev/null +++ b/services/core/java/com/android/server/infra/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 655446 + +include /core/java/android/service/cloudsearch/OWNERS diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java index a102406cc131..1203769cb72b 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java @@ -262,7 +262,7 @@ public class LockSettingsStrongAuth { long nextAlarmTime = strongAuthTime + dpm.getRequiredStrongAuthTimeout(null, userId); // schedule a new alarm listener for the user - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmTime, + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextAlarmTime, STRONG_AUTH_TIMEOUT_ALARM_TAG, alarm, mHandler); } @@ -303,7 +303,7 @@ public class LockSettingsStrongAuth { alarm = new NonStrongBiometricTimeoutAlarmListener(userId); mNonStrongBiometricTimeoutAlarmListener.put(userId, alarm); // schedule a new alarm listener for the user - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmTime, + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextAlarmTime, NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG, alarm, mHandler); } @@ -394,7 +394,7 @@ public class LockSettingsStrongAuth { } // schedule a new alarm listener for the user if (DEBUG) Slog.d(TAG, "Schedule a new alarm for non-strong biometric idle timeout"); - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmTime, + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextAlarmTime, NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG, alarm, mHandler); } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 4822d6a62ac7..b482d1869c66 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -62,6 +62,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.NoSuchElementException; /** * This is the system implementation of a Session. Apps will interact with the @@ -792,7 +793,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { try { + holder.mCallback.asBinder().unlinkToDeath(holder.mDeathMonitor, 0); holder.mCallback.onSessionDestroyed(); + } catch (NoSuchElementException e) { + logCallbackException("error unlinking to binder death", holder, e); } catch (DeadObjectException e) { logCallbackException("Removing dead callback in pushSessionDestroyed", holder, e); } catch (RemoteException e) { @@ -1375,12 +1379,22 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR return; } if (getControllerHolderIndexForCb(cb) < 0) { - mControllerCallbackHolders.add(new ISessionControllerCallbackHolder(cb, - packageName, Binder.getCallingUid())); + ISessionControllerCallbackHolder holder = new ISessionControllerCallbackHolder( + cb, packageName, Binder.getCallingUid(), () -> unregisterCallback(cb)); + mControllerCallbackHolders.add(holder); if (DEBUG) { Log.d(TAG, "registering controller callback " + cb + " from controller" + packageName); } + // Avoid callback leaks + try { + // cb is not referenced outside of the MediaSessionRecord, so the death + // handler won't prevent MediaSessionRecord to be garbage collected. + cb.asBinder().linkToDeath(holder.mDeathMonitor, 0); + } catch (RemoteException e) { + unregisterCallback(cb); + Log.w(TAG, "registerCallback failed to linkToDeath", e); + } } } } @@ -1390,6 +1404,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR synchronized (mLock) { int index = getControllerHolderIndexForCb(cb); if (index != -1) { + try { + cb.asBinder().unlinkToDeath( + mControllerCallbackHolders.get(index).mDeathMonitor, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "error unlinking to binder death", e); + } mControllerCallbackHolders.remove(index); } if (DEBUG) { @@ -1600,12 +1620,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR private final ISessionControllerCallback mCallback; private final String mPackageName; private final int mUid; + private final IBinder.DeathRecipient mDeathMonitor; ISessionControllerCallbackHolder(ISessionControllerCallback callback, String packageName, - int uid) { + int uid, IBinder.DeathRecipient deathMonitor) { mCallback = callback; mPackageName = packageName; mUid = uid; + mDeathMonitor = deathMonitor; } } diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 851ea3d01085..1b7d1ba59b06 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -293,7 +293,7 @@ public class LockdownVpnTracker { .addAction(R.drawable.ic_menu_refresh, mContext.getString(R.string.reset), mResetIntent) .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)); + android.R.color.system_notification_accent_color)); mNotificationManager.notify(null /* tag */, SystemMessage.NOTE_VPN_STATUS, builder.build()); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 6a00d06440b9..9f58f6540be0 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -61,7 +61,6 @@ import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -173,11 +172,9 @@ import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkPolicyManager.UidState; import android.net.NetworkRequest; -import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStateSnapshot; import android.net.NetworkTemplate; -import android.net.TelephonyNetworkSpecifier; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.BestClock; @@ -284,6 +281,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.IntConsumer; @@ -1006,10 +1004,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler); // listen for stats updated callbacks for interested network types. + final Executor executor = new HandlerExecutor(mHandler); mNetworkStats.registerUsageCallback(new NetworkTemplate.Builder(MATCH_MOBILE).build(), - 0 /* thresholdBytes */, new HandlerExecutor(mHandler), mStatsCallback); + 0 /* thresholdBytes */, executor, mStatsCallback); mNetworkStats.registerUsageCallback(new NetworkTemplate.Builder(MATCH_WIFI).build(), - 0 /* thresholdBytes */, new HandlerExecutor(mHandler), mStatsCallback); + 0 /* thresholdBytes */, executor, mStatsCallback); // listen for restrict background changes from notifications final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND); @@ -1237,6 +1236,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Used to determine if NetworkStatsService is ready. */ public boolean isAnyCallbackReceived() { + // Warning : threading for this member is broken. It should only be read + // and written on the handler thread ; furthermore, the constructor + // is called on a different thread, so this stops working if the default + // value is not false or if this member ever goes back to false after + // being set to true. + // TODO : fix threading for this member. return mIsAnyCallbackReceived; } }; @@ -1512,7 +1517,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .setType(TYPE_MOBILE) .setSubscriberId(subscriberId) .setMetered(true) - .setDefaultNetwork(true).build(); + .setDefaultNetwork(true) + .setSubId(subId).build(); if (template.matches(probeIdent)) { return subId; } @@ -1749,7 +1755,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .setType(TYPE_MOBILE) .setSubscriberId(subscriberId) .setMetered(true) - .setDefaultNetwork(true).build(); + .setDefaultNetwork(true) + .setSubId(subId).build(); for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { final NetworkTemplate template = mNetworkPolicy.keyAt(i); if (template.matches(probeIdent)) { @@ -1981,7 +1988,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .setType(TYPE_MOBILE) .setSubscriberId(subscriberId) .setMetered(true) - .setDefaultNetwork(true).build(); + .setDefaultNetwork(true) + .setSubId(subId).build(); // Template is matched when subscriber id matches. if (template.matches(probeIdent)) { matchingSubIds.add(subId); @@ -2083,7 +2091,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNetIdToSubId.clear(); final ArrayMap<NetworkStateSnapshot, NetworkIdentity> identified = new ArrayMap<>(); for (final NetworkStateSnapshot snapshot : snapshots) { - mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot)); + final int subId = snapshot.getSubId(); + mNetIdToSubId.put(snapshot.getNetwork().getNetId(), subId); // Policies matched by NPMS only match by subscriber ID or by network ID. final NetworkIdentity ident = new NetworkIdentity.Builder() @@ -2288,7 +2297,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .setType(TYPE_MOBILE) .setSubscriberId(subscriberId) .setMetered(true) - .setDefaultNetwork(true).build(); + .setDefaultNetwork(true) + .setSubId(subId).build(); for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { final NetworkTemplate template = mNetworkPolicy.keyAt(i); if (template.matches(probeIdent)) { @@ -5807,17 +5817,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private int parseSubId(@NonNull NetworkStateSnapshot snapshot) { - int subId = INVALID_SUBSCRIPTION_ID; - if (snapshot.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR)) { - NetworkSpecifier spec = snapshot.getNetworkCapabilities().getNetworkSpecifier(); - if (spec instanceof TelephonyNetworkSpecifier) { - subId = ((TelephonyNetworkSpecifier) spec).getSubscriptionId(); - } - } - return subId; - } - @GuardedBy("mNetworkPoliciesSecondLock") private int getSubIdLocked(Network network) { return mNetIdToSubId.get(network.getNetId(), INVALID_SUBSCRIPTION_ID); diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 31b186091f39..e7e691c9c654 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -343,6 +343,13 @@ public abstract class ApexManager { public abstract String getApexModuleNameForPackageName(String apexPackageName); /** + * Returns the package name of the active APEX whose name is {@code apexModuleName}. If not + * found, returns {@code null}. + */ + @Nullable + public abstract String getActivePackageNameForApexModuleName(String apexModuleName); + + /** * Copies the CE apex data directory for the given {@code userId} to a backup location, for use * in case of rollback. * @@ -467,6 +474,12 @@ public abstract class ApexManager { private ArrayMap<String, String> mPackageNameToApexModuleName; /** + * Reverse mapping of {@link #mPackageNameToApexModuleName}, for active packages only. + */ + @GuardedBy("mLock") + private ArrayMap<String, String> mApexModuleNameToActivePackageName; + + /** * Whether an APEX package is active or not. * * @param packageInfo the package to check @@ -534,6 +547,7 @@ public abstract class ApexManager { try { mAllPackagesCache = new ArrayList<>(); mPackageNameToApexModuleName = new ArrayMap<>(); + mApexModuleNameToActivePackageName = new ArrayMap<>(); allPkgs = waitForApexService().getAllPackages(); } catch (RemoteException re) { Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); @@ -580,6 +594,13 @@ public abstract class ApexManager { + packageInfo.packageName); } activePackagesSet.add(packageInfo.packageName); + if (mApexModuleNameToActivePackageName.containsKey(ai.moduleName)) { + throw new IllegalStateException( + "Two active packages have the same APEX module name: " + + ai.moduleName); + } + mApexModuleNameToActivePackageName.put( + ai.moduleName, packageInfo.packageName); } if (ai.isFactory) { // Don't throw when the duplicating APEX is VNDK APEX @@ -913,6 +934,16 @@ public abstract class ApexManager { } @Override + @Nullable + public String getActivePackageNameForApexModuleName(String apexModuleName) { + synchronized (mLock) { + Preconditions.checkState(mApexModuleNameToActivePackageName != null, + "APEX packages have not been scanned"); + return mApexModuleNameToActivePackageName.get(apexModuleName); + } + } + + @Override public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { String apexModuleName; synchronized (mLock) { @@ -1330,6 +1361,12 @@ public abstract class ApexManager { } @Override + @Nullable + public String getActivePackageNameForApexModuleName(String apexModuleName) { + return null; + } + + @Override public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { throw new UnsupportedOperationException(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2d3cbfe61b1a..bc6c306a109b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -12908,7 +12908,8 @@ public class PackageManagerService extends IPackageManager.Stub DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES | DexoptOptions.DEXOPT_BOOT_COMPLETE | (force ? DexoptOptions.DEXOPT_FORCE : 0); - return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags)); + return performDexOpt(new DexoptOptions(packageName, REASON_CMDLINE, + compilerFilter, /* splitName */ null, flags)); } /*package*/ boolean performDexOpt(DexoptOptions options) { diff --git a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java new file mode 100644 index 000000000000..0418afbf29ee --- /dev/null +++ b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java @@ -0,0 +1,67 @@ +/* + * 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.server.pm.parsing.library; + +import android.util.ArrayMap; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.SystemConfig; +import com.android.server.pm.parsing.pkg.ParsedPackage; + +/** + * Updates packages to add or remove dependencies on shared libraries as per attributes + * in the library declaration + * + * @hide + */ +@VisibleForTesting +public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater { + + /** + * ArrayMap like the one you find in {@link SystemConfig}. The keys are the library names. + */ + private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries; + + public ApexSharedLibraryUpdater( + ArrayMap<String, SystemConfig.SharedLibraryEntry> sharedLibraries) { + mSharedLibraries = sharedLibraries; + } + + @Override + public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) { + final int builtInLibCount = mSharedLibraries.size(); + for (int i = 0; i < builtInLibCount; i++) { + updateSharedLibraryForPackage(mSharedLibraries.valueAt(i), parsedPackage); + } + } + + private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry, + ParsedPackage parsedPackage) { + if (entry.onBootclasspathBefore != 0 + && parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) { + // this package targets an API where this library was in the BCP, so add + // the library transparently in case the package is using it + prefixRequiredLibrary(parsedPackage, entry.name); + } + + if (entry.canBeSafelyIgnored) { + // the library is now present in the BCP and always available; we don't need to add + // it a second time + removeLibrary(parsedPackage, entry.name); + } + } +} diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java index 8a8a302734b1..d81e7d05fd73 100644 --- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java +++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java @@ -25,6 +25,7 @@ import android.content.pm.PackageParser; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.ParsedPackage; import java.util.ArrayList; @@ -63,6 +64,11 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters); + // ApexSharedLibraryUpdater should be the last one, to allow modifications introduced by + // mainline after dessert release. + packageUpdaters.add(new ApexSharedLibraryUpdater( + SystemConfig.getInstance().getSharedLibraries())); + PackageSharedLibraryUpdater[] updaterArray = packageUpdaters .toArray(new PackageSharedLibraryUpdater[0]); INSTANCE = new PackageBackwardCompatibility( @@ -106,6 +112,11 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { private final PackageSharedLibraryUpdater[] mPackageUpdaters; + @VisibleForTesting + PackageSharedLibraryUpdater[] getPackageUpdaters() { + return mPackageUpdaters; + } + private PackageBackwardCompatibility( boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) { this.mBootClassPathContainsATB = bootClassPathContainsATB; 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 1fa948cef216..9cf38e3ca92f 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2609,6 +2609,7 @@ public class StatsPullAtomService extends SystemService { StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath()); StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath()); StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath()); + StatFs metadataFsSystem = new StatFs(Environment.getMetadataDirectory().getAbsolutePath()); pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__DATA, statFsData.getAvailableBytes(), @@ -2621,6 +2622,10 @@ public class StatsPullAtomService extends SystemService { pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM, statFsSystem.getAvailableBytes(), statFsSystem.getTotalBytes())); + + pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, + FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__METADATA, + metadataFsSystem.getAvailableBytes(), metadataFsSystem.getTotalBytes())); return StatsManager.PULL_SUCCESS; } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 357c23222658..ae4d46c387b9 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -69,7 +69,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { * Telephony and network suggestions older than this value are considered too old to be used. */ @VisibleForTesting - static final long MAX_UTC_TIME_AGE_MILLIS = + static final long MAX_SUGGESTION_TIME_AGE_MILLIS = TELEPHONY_BUCKET_COUNT * TELEPHONY_BUCKET_SIZE_MILLIS; /** @@ -204,9 +204,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @Override public synchronized void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion) { - final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime(); + final TimestampedValue<Long> newUnixEpochTime = timeSuggestion.getUnixEpochTime(); - if (!validateAutoSuggestionTime(newUtcTime, timeSuggestion)) { + if (!validateAutoSuggestionTime(newUnixEpochTime, timeSuggestion)) { return; } @@ -218,9 +218,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @Override public synchronized void suggestGnssTime(@NonNull GnssTimeSuggestion timeSuggestion) { - final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime(); + final TimestampedValue<Long> newUnixEpochTime = timeSuggestion.getUnixEpochTime(); - if (!validateAutoSuggestionTime(newUtcTime, timeSuggestion)) { + if (!validateAutoSuggestionTime(newUnixEpochTime, timeSuggestion)) { return; } @@ -232,19 +232,19 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @Override public synchronized boolean suggestManualTime(@NonNull ManualTimeSuggestion suggestion) { - final TimestampedValue<Long> newUtcTime = suggestion.getUtcTime(); + final TimestampedValue<Long> newUnixEpochTime = suggestion.getUnixEpochTime(); - if (!validateSuggestionTime(newUtcTime, suggestion)) { + if (!validateSuggestionTime(newUnixEpochTime, suggestion)) { return false; } String cause = "Manual time suggestion received: suggestion=" + suggestion; - return setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, cause); + return setSystemClockIfRequired(ORIGIN_MANUAL, newUnixEpochTime, cause); } @Override public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion) { - if (!validateAutoSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) { + if (!validateAutoSuggestionTime(timeSuggestion.getUnixEpochTime(), timeSuggestion)) { return; } @@ -274,11 +274,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // unlike time zone, where a user may lose connectivity when boarding a flight and where we // do want to "forget" old signals. Suggestions that are too old are discarded later in the // detection algorithm. - if (timeSuggestion.getUtcTime() == null) { + if (timeSuggestion.getUnixEpochTime() == null) { return; } - if (!validateAutoSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) { + if (!validateAutoSuggestionTime(timeSuggestion.getUnixEpochTime(), timeSuggestion)) { return; } @@ -365,14 +365,14 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @GuardedBy("this") private boolean storeTelephonySuggestion( @NonNull TelephonyTimeSuggestion suggestion) { - TimestampedValue<Long> newUtcTime = suggestion.getUtcTime(); + TimestampedValue<Long> newUnixEpochTime = suggestion.getUnixEpochTime(); int slotIndex = suggestion.getSlotIndex(); TelephonyTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex); if (previousSuggestion != null) { // We can log / discard suggestions with obvious issues with the reference time clock. - if (previousSuggestion.getUtcTime() == null - || previousSuggestion.getUtcTime().getValue() == null) { + if (previousSuggestion.getUnixEpochTime() == null + || previousSuggestion.getUnixEpochTime().getValue() == null) { // This should be impossible given we only store validated suggestions. Slog.w(LOG_TAG, "Previous suggestion is null or has a null time." + " previousSuggestion=" + previousSuggestion @@ -381,7 +381,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } long referenceTimeDifference = TimestampedValue.referenceTimeDifference( - newUtcTime, previousSuggestion.getUtcTime()); + newUnixEpochTime, previousSuggestion.getUnixEpochTime()); if (referenceTimeDifference < 0) { // The reference time is before the previously received suggestion. Ignore it. Slog.w(LOG_TAG, "Out of order telephony suggestion received." @@ -398,15 +398,15 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } private boolean validateSuggestionTime( - @NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) { - if (newUtcTime.getValue() == null) { + @NonNull TimestampedValue<Long> newUnixEpochTime, @NonNull Object suggestion) { + if (newUnixEpochTime.getValue() == null) { Slog.w(LOG_TAG, "Suggested time value is null. suggestion=" + suggestion); return false; } // We can validate the suggestion against the reference time clock. long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis(); - if (elapsedRealtimeMillis < newUtcTime.getReferenceTimeMillis()) { + if (elapsedRealtimeMillis < newUnixEpochTime.getReferenceTimeMillis()) { // elapsedRealtime clock went backwards? Slog.w(LOG_TAG, "New reference time is in the future? Ignoring." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis @@ -417,17 +417,17 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } private boolean validateAutoSuggestionTime( - @NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) { - return validateSuggestionTime(newUtcTime, suggestion) - && validateSuggestionAgainstLowerBound(newUtcTime, suggestion); + @NonNull TimestampedValue<Long> newUnixEpochTime, @NonNull Object suggestion) { + return validateSuggestionTime(newUnixEpochTime, suggestion) + && validateSuggestionAgainstLowerBound(newUnixEpochTime, suggestion); } private boolean validateSuggestionAgainstLowerBound( - @NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) { + @NonNull TimestampedValue<Long> newUnixEpochTime, @NonNull Object suggestion) { Instant lowerBound = mEnvironment.autoTimeLowerBound(); // Suggestion is definitely wrong if it comes before lower time bound. - if (lowerBound.isAfter(Instant.ofEpochMilli(newUtcTime.getValue()))) { + if (lowerBound.isAfter(Instant.ofEpochMilli(newUnixEpochTime.getValue()))) { Slog.w(LOG_TAG, "Suggestion points to time before lower bound, skipping it. " + "suggestion=" + suggestion + ", lower bound=" + lowerBound); return false; @@ -446,12 +446,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // Try the different origins one at a time. int[] originPriorities = mEnvironment.autoOriginPriorities(); for (int origin : originPriorities) { - TimestampedValue<Long> newUtcTime = null; + TimestampedValue<Long> newUnixEpochTime = null; String cause = null; if (origin == ORIGIN_TELEPHONY) { TelephonyTimeSuggestion bestTelephonySuggestion = findBestTelephonySuggestion(); if (bestTelephonySuggestion != null) { - newUtcTime = bestTelephonySuggestion.getUtcTime(); + newUnixEpochTime = bestTelephonySuggestion.getUnixEpochTime(); cause = "Found good telephony suggestion." + ", bestTelephonySuggestion=" + bestTelephonySuggestion + ", detectionReason=" + detectionReason; @@ -459,7 +459,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } else if (origin == ORIGIN_NETWORK) { NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion(); if (networkSuggestion != null) { - newUtcTime = networkSuggestion.getUtcTime(); + newUnixEpochTime = networkSuggestion.getUnixEpochTime(); cause = "Found good network suggestion." + ", networkSuggestion=" + networkSuggestion + ", detectionReason=" + detectionReason; @@ -467,7 +467,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } else if (origin == ORIGIN_GNSS) { GnssTimeSuggestion gnssTimeSuggestion = findLatestValidGnssSuggestion(); if (gnssTimeSuggestion != null) { - newUtcTime = gnssTimeSuggestion.getUtcTime(); + newUnixEpochTime = gnssTimeSuggestion.getUnixEpochTime(); cause = "Found good gnss suggestion." + ", gnssTimeSuggestion=" + gnssTimeSuggestion + ", detectionReason=" + detectionReason; @@ -475,7 +475,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } else if (origin == ORIGIN_EXTERNAL) { ExternalTimeSuggestion externalTimeSuggestion = findLatestValidExternalSuggestion(); if (externalTimeSuggestion != null) { - newUtcTime = externalTimeSuggestion.getUtcTime(); + newUnixEpochTime = externalTimeSuggestion.getUnixEpochTime(); cause = "Found good external suggestion." + ", externalTimeSuggestion=" + externalTimeSuggestion + ", detectionReason=" + detectionReason; @@ -487,8 +487,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } // Update the system clock if a good suggestion has been found. - if (newUtcTime != null) { - setSystemClockIfRequired(origin, newUtcTime, cause); + if (newUnixEpochTime != null) { + setSystemClockIfRequired(origin, newUnixEpochTime, cause); return; } } @@ -545,7 +545,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for slotIndex." + " slotIndex=" + slotIndex); continue; - } else if (candidateSuggestion.getUtcTime() == null) { + } else if (candidateSuggestion.getUnixEpochTime() == null) { // Unexpected - we do not store empty suggestions. Slog.w(LOG_TAG, "Latest suggestion unexpectedly empty. " + " candidateSuggestion=" + candidateSuggestion); @@ -579,8 +579,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { long elapsedRealtimeMillis, @NonNull TelephonyTimeSuggestion timeSuggestion) { // Validate first. - TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime(); - if (!validateSuggestionUtcTime(elapsedRealtimeMillis, utcTime)) { + TimestampedValue<Long> unixEpochTime = timeSuggestion.getUnixEpochTime(); + if (!validateSuggestionUnixEpochTime(elapsedRealtimeMillis, unixEpochTime)) { Slog.w(LOG_TAG, "Existing suggestion found to be invalid" + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + ", timeSuggestion=" + timeSuggestion); @@ -589,7 +589,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // The score is based on the age since receipt. Suggestions are bucketed so two // suggestions in the same bucket from different slotIndexs are scored the same. - long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis(); + long ageMillis = elapsedRealtimeMillis - unixEpochTime.getReferenceTimeMillis(); // Turn the age into a discrete value: 0 <= bucketIndex < TELEPHONY_BUCKET_COUNT. int bucketIndex = (int) (ageMillis / TELEPHONY_BUCKET_SIZE_MILLIS); @@ -611,9 +611,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return null; } - TimestampedValue<Long> utcTime = networkSuggestion.getUtcTime(); + TimestampedValue<Long> unixEpochTime = networkSuggestion.getUnixEpochTime(); long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis(); - if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) { + if (!validateSuggestionUnixEpochTime(elapsedRealTimeMillis, unixEpochTime)) { // The latest suggestion is not valid, usually due to its age. return null; } @@ -631,9 +631,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return null; } - TimestampedValue<Long> utcTime = gnssTimeSuggestion.getUtcTime(); + TimestampedValue<Long> unixEpochTime = gnssTimeSuggestion.getUnixEpochTime(); long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis(); - if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) { + if (!validateSuggestionUnixEpochTime(elapsedRealTimeMillis, unixEpochTime)) { // The latest suggestion is not valid, usually due to its age. return null; } @@ -651,9 +651,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return null; } - TimestampedValue<Long> utcTime = externalTimeSuggestion.getUtcTime(); + TimestampedValue<Long> unixEpochTime = externalTimeSuggestion.getUnixEpochTime(); long elapsedRealTimeMillis = mEnvironment.elapsedRealtimeMillis(); - if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) { + if (!validateSuggestionUnixEpochTime(elapsedRealTimeMillis, unixEpochTime)) { // The latest suggestion is not valid, usually due to its age. return null; } @@ -844,9 +844,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return mLastExternalSuggestion.get(); } - private static boolean validateSuggestionUtcTime( - long elapsedRealtimeMillis, TimestampedValue<Long> utcTime) { - long referenceTimeMillis = utcTime.getReferenceTimeMillis(); + private static boolean validateSuggestionUnixEpochTime( + long elapsedRealtimeMillis, TimestampedValue<Long> unixEpochTime) { + long referenceTimeMillis = unixEpochTime.getReferenceTimeMillis(); if (referenceTimeMillis > elapsedRealtimeMillis) { // Future reference times are ignored. They imply the reference time was wrong, or the // elapsed realtime clock used to derive it has gone backwards, neither of which are @@ -860,6 +860,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // made and never replaced, it could also mean that the time detection code remains // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS. long ageMillis = elapsedRealtimeMillis - referenceTimeMillis; - return ageMillis <= MAX_UTC_TIME_AGE_MILLIS; + return ageMillis <= MAX_SUGGESTION_TIME_AGE_MILLIS; } } diff --git a/services/core/java/com/android/server/timezonedetector/OWNERS b/services/core/java/com/android/server/timezonedetector/OWNERS index 029324246c91..485a0ddb06d8 100644 --- a/services/core/java/com/android/server/timezonedetector/OWNERS +++ b/services/core/java/com/android/server/timezonedetector/OWNERS @@ -3,5 +3,6 @@ # ultimately referenced by other OWNERS files for components maintained by the same team. nfuller@google.com jmorace@google.com +kanyinsola@google.com mingaleev@google.com narayan@google.com diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java index a17e79273e41..30e261725a73 100644 --- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -146,7 +146,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { filter.addAction(ACTION_CARRIER_CONFIG_CHANGED); filter.addAction(ACTION_MULTI_SIM_CONFIG_CHANGED); - mContext.registerReceiver(this, filter, null, mHandler); + mContext.registerReceiver(this, filter, null, mHandler, Context.RECEIVER_NOT_EXPORTED); mSubscriptionManager.addOnSubscriptionsChangedListener( executor, mSubscriptionChangedListener); mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener); diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 8b80b4a0b21e..597f7f284730 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -78,6 +78,7 @@ import android.os.PowerManager.WakeLock; import android.os.Process; import android.os.SystemClock; import android.provider.Settings; +import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Slog; @@ -163,6 +164,14 @@ import java.util.function.Consumer; public class VcnGatewayConnection extends StateMachine { private static final String TAG = VcnGatewayConnection.class.getSimpleName(); + // Matches DataConnection.NETWORK_TYPE private constant, and magic string from + // ConnectivityManager#getNetworkTypeName() + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final String NETWORK_INFO_NETWORK_TYPE_STRING = "MOBILE"; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final String NETWORK_INFO_EXTRA_INFO = "VCN"; + @VisibleForTesting(visibility = Visibility.PRIVATE) static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0"); @@ -1631,6 +1640,12 @@ public class VcnGatewayConnection extends StateMachine { final NetworkAgentConfig nac = new NetworkAgentConfig.Builder() .setLegacyType(ConnectivityManager.TYPE_MOBILE) + .setLegacyTypeName(NETWORK_INFO_NETWORK_TYPE_STRING) + .setLegacySubType(TelephonyManager.NETWORK_TYPE_UNKNOWN) + .setLegacySubTypeName( + TelephonyManager.getNetworkTypeName( + TelephonyManager.NETWORK_TYPE_UNKNOWN)) + .setLegacyExtraInfo(NETWORK_INFO_EXTRA_INFO) .build(); final VcnNetworkAgent agent = diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index ee4c629189dc..152b4bcffc4e 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -42,13 +42,12 @@ import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static java.lang.Integer.MIN_VALUE; - import android.annotation.ColorInt; import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.content.res.Configuration; +import android.graphics.Color; import android.os.UserHandle; import android.util.IntArray; import android.util.Slog; @@ -83,9 +82,9 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { DisplayContent mDisplayContent; /** - * A color layer that serves as a solid color background to certain animations. + * Keeps track of the last set color layer so that it can be reset during surface migrations. */ - private SurfaceControl mColorBackgroundLayer; + private @ColorInt int mBackgroundColor = 0; /** * This counter is used to make sure we don't prematurely clear the background color in the @@ -367,6 +366,14 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } @Override + void setInitialSurfaceControlProperties(SurfaceControl.Builder b) { + // We want an effect layer instead of the default container layer so that we can set a + // background color on it for task animations. + b.setEffectLayer(); + super.setInitialSurfaceControlProperties(b); + } + + @Override void addChild(WindowContainer child, int position) { if (child.asTaskDisplayArea() != null) { if (DEBUG_ROOT_TASK) { @@ -980,11 +987,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { if (getParent() != null) { super.onParentChanged(newParent, oldParent, () -> { - mColorBackgroundLayer = makeChildSurface(null) - .setColorLayer() - .setName("colorBackgroundLayer") - .setCallsite("TaskDisplayArea.onParentChanged") - .build(); mAppAnimationLayer = makeChildSurface(null) .setName("animationLayer") .setCallsite("TaskDisplayArea.onParentChanged") @@ -1011,13 +1013,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } else { super.onParentChanged(newParent, oldParent); mWmService.mTransactionFactory.get() - .remove(mColorBackgroundLayer) .remove(mAppAnimationLayer) .remove(mBoostedAppAnimationLayer) .remove(mHomeAppAnimationLayer) .remove(mSplitScreenDividerAnchor) .apply(); - mColorBackgroundLayer = null; mAppAnimationLayer = null; mBoostedAppAnimationLayer = null; mHomeAppAnimationLayer = null; @@ -1025,35 +1025,29 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } } - void setBackgroundColor(@ColorInt int color) { - if (mColorBackgroundLayer == null) { - return; - } - - float r = ((color >> 16) & 0xff) / 255.0f; - float g = ((color >> 8) & 0xff) / 255.0f; - float b = ((color >> 0) & 0xff) / 255.0f; - float a = ((color >> 24) & 0xff) / 255.0f; - + void setBackgroundColor(@ColorInt int colorInt) { + mBackgroundColor = colorInt; + Color color = Color.valueOf(colorInt); mColorLayerCounter++; - getPendingTransaction().setLayer(mColorBackgroundLayer, MIN_VALUE) - .setColor(mColorBackgroundLayer, new float[]{r, g, b}) - .setAlpha(mColorBackgroundLayer, a) - .setWindowCrop(mColorBackgroundLayer, getSurfaceWidth(), getSurfaceHeight()) - .setPosition(mColorBackgroundLayer, 0, 0) - .show(mColorBackgroundLayer); - - scheduleAnimation(); + // Only apply the background color if the TDA is actually attached and has a valid surface + // to set the background color on. We still want to keep track of the background color state + // even if we are not showing it for when/if the TDA is reattached and gets a valid surface + if (mSurfaceControl != null) { + getPendingTransaction() + .setColor(mSurfaceControl, + new float[]{color.red(), color.green(), color.blue()}); + scheduleAnimation(); + } } void clearBackgroundColor() { mColorLayerCounter--; // Only clear the color layer if we have received the same amounts of clear as set - // requests. - if (mColorLayerCounter == 0) { - getPendingTransaction().hide(mColorBackgroundLayer); + // requests and TDA has a non null surface control (i.e. is attached) + if (mColorLayerCounter == 0 && mSurfaceControl != null) { + getPendingTransaction().unsetColor(mSurfaceControl); scheduleAnimation(); } } @@ -1061,12 +1055,16 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { @Override void migrateToNewSurfaceControl(SurfaceControl.Transaction t) { super.migrateToNewSurfaceControl(t); + + if (mColorLayerCounter > 0) { + setBackgroundColor(mBackgroundColor); + } + if (mAppAnimationLayer == null) { return; } // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces. - t.reparent(mColorBackgroundLayer, mSurfaceControl); t.reparent(mAppAnimationLayer, mSurfaceControl); t.reparent(mBoostedAppAnimationLayer, mSurfaceControl); t.reparent(mHomeAppAnimationLayer, mSurfaceControl); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index a518222c3bde..71893f49481f 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -288,7 +288,7 @@ class TaskSnapshotController { } final ActivityRecord activity = result.first; final WindowState mainWindow = result.second; - final Rect contentInsets = getSystemBarInsets(task.getBounds(), + final Rect contentInsets = getSystemBarInsets(mainWindow.getFrame(), mainWindow.getInsetsStateWithVisibilityOverride()); InsetUtils.addInsets(contentInsets, activity.getLetterboxInsets()); @@ -554,7 +554,7 @@ class TaskSnapshotController { final LayoutParams attrs = mainWindow.getAttrs(); final Rect taskBounds = task.getBounds(); final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride(); - final Rect systemBarInsets = getSystemBarInsets(taskBounds, insetsState); + final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState); final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(), mHighResTaskSnapshotScale, insetsState); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index e48b5e17739b..e755b9e5c51d 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -47,7 +47,6 @@ cc_library_static { "com_android_server_lights_LightsService.cpp", "com_android_server_location_GnssLocationProvider.cpp", "com_android_server_locksettings_SyntheticPasswordManager.cpp", - "com_android_server_net_NetworkStatsService.cpp", "com_android_server_power_PowerManagerService.cpp", "com_android_server_powerstats_PowerStatsService.cpp", "com_android_server_hint_HintManagerService.cpp", @@ -73,7 +72,6 @@ cc_library_static { "com_android_server_sensor_SensorService.cpp", "onload.cpp", ":lib_cachedAppOptimizer_native", - ":lib_networkStatsFactory_native", ], include_dirs: [ @@ -85,7 +83,6 @@ cc_library_static { header_libs: [ "bionic_libc_platform_headers", - "bpf_connectivity_headers", ], } @@ -140,9 +137,6 @@ cc_defaults { "libhidlbase", "libutils", "libhwui", - "libbpf_android", - "libnetdutils", - "libnetworkstats", "libpsi", "libdataloader", "libincfs", @@ -205,13 +199,6 @@ cc_defaults { } filegroup { - name: "lib_networkStatsFactory_native", - srcs: [ - "com_android_server_net_NetworkStatsFactory.cpp", - ], -} - -filegroup { name: "lib_cachedAppOptimizer_native", srcs: [ "com_android_server_am_CachedAppOptimizer.cpp", diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index ff61abc4ff7f..9d3edd6e8b2e 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -51,8 +51,6 @@ int register_android_server_Watchdog(JNIEnv* env); int register_android_server_HardwarePropertiesManagerService(JNIEnv* env); int register_android_server_SyntheticPasswordManager(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); -int register_android_server_net_NetworkStatsFactory(JNIEnv* env); -int register_android_server_net_NetworkStatsService(JNIEnv* env); int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_am_LowMemDetector(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env); @@ -107,8 +105,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_SyntheticPasswordManager(env); register_android_graphics_GraphicsStatsService(env); register_android_hardware_display_DisplayViewport(env); - register_android_server_net_NetworkStatsFactory(env); - register_android_server_net_NetworkStatsService(env); register_android_server_am_CachedAppOptimizer(env); register_android_server_am_LowMemDetector(env); register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index ad97dd19c2e6..4825f09be4d7 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -54,7 +54,6 @@ import android.hardware.display.DisplayManagerInternal; import android.net.ConnectivityManager; import android.net.ConnectivityModuleConnector; import android.net.NetworkStackClient; -import android.net.TrafficStats; import android.os.BaseBundle; import android.os.Binder; import android.os.Build; @@ -141,7 +140,6 @@ import com.android.server.media.MediaRouterService; import com.android.server.media.metrics.MediaMetricsManagerService; import com.android.server.media.projection.MediaProjectionManagerService; import com.android.server.net.NetworkPolicyManagerService; -import com.android.server.net.NetworkStatsService; import com.android.server.net.watchlist.NetworkWatchlistService; import com.android.server.notification.NotificationManagerService; import com.android.server.oemlock.OemLockService; @@ -379,6 +377,8 @@ public final class SystemServer implements Dumpable { "com.android.server.media.MediaResourceMonitorService"; private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS = "com.android.server.ConnectivityServiceInitializer"; + private static final String NETWORK_STATS_SERVICE_INITIALIZER_CLASS = + "com.android.server.NetworkStatsServiceInitializer"; private static final String IP_CONNECTIVITY_METRICS_CLASS = "com.android.server.connectivity.IpConnectivityMetrics"; private static final String MEDIA_COMMUNICATION_SERVICE_CLASS = @@ -1336,7 +1336,6 @@ public final class SystemServer implements Dumpable { NetworkManagementService networkManagement = null; VpnManagerService vpnManager = null; VcnManagementService vcnManagement = null; - NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; WindowManagerService wm = null; SerialService serial = null; @@ -1825,13 +1824,10 @@ public final class SystemServer implements Dumpable { t.traceEnd(); t.traceBegin("StartNetworkStatsService"); - try { - networkStats = NetworkStatsService.create(context); - ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats); - TrafficStats.init(context); - } catch (Throwable e) { - reportWtf("starting NetworkStats Service", e); - } + // This has to be called before NetworkPolicyManager because NetworkPolicyManager + // needs to take NetworkStatsService to initialize. + mSystemServiceManager.startServiceFromJar(NETWORK_STATS_SERVICE_INITIALIZER_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); t.traceEnd(); t.traceBegin("StartNetworkPolicyManagerService"); @@ -2648,7 +2644,6 @@ public final class SystemServer implements Dumpable { // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; - final NetworkStatsService networkStatsF = networkStats; final NetworkPolicyManagerService networkPolicyF = networkPolicy; final CountryDetectorService countryDetectorF = countryDetector; final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; @@ -2745,15 +2740,6 @@ public final class SystemServer implements Dumpable { .networkScoreAndNetworkManagementServiceReady(); } t.traceEnd(); - t.traceBegin("MakeNetworkStatsServiceReady"); - try { - if (networkStatsF != null) { - networkStatsF.systemReady(); - } - } catch (Throwable e) { - reportWtf("making Network Stats Service ready", e); - } - t.traceEnd(); t.traceBegin("MakeConnectivityServiceReady"); try { if (connectivityF != null) { diff --git a/services/proguard.flags b/services/proguard.flags index 0e081f182d0d..425da6c11177 100644 --- a/services/proguard.flags +++ b/services/proguard.flags @@ -40,9 +40,15 @@ # Global entities normally kept through explicit Manifest entries # TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/AndroidManifest.xml, # by including that manifest with the library rule that triggers optimization. --keep,allowoptimization,allowaccessmodification class * extends android.app.backup.BackupAgent --keep,allowoptimization,allowaccessmodification class * extends android.content.BroadcastReceiver --keep,allowoptimization,allowaccessmodification class * extends android.content.ContentProvider +-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.Activity +-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.Service +-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.backup.BackupAgent +-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.content.BroadcastReceiver +-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.content.ContentProvider +-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.preference.Preference +-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.view.View { + public <init>(...); +} # Various classes subclassed in or referenced via JNI in ethernet-service -keep public class android.net.** { *; } @@ -67,6 +73,7 @@ -keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssConfiguration$HalInterfaceVersion { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssPowerStats { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.hal.GnssNative { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.pm.PackageManagerShellCommandDataLoader { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$ProximityActiveListener { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorService { *; } -keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareImpl$AudioSessionProvider$AudioSession { *; } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java index acb20edfe8d8..6de7fddf6ccd 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStrongAuthTest.java @@ -229,8 +229,8 @@ public class LockSettingsStrongAuthTest { } private void verifyAlarm(long when, String tag, AlarmManager.OnAlarmListener alarm) { - verify(mAlarmManager).set( - eq(AlarmManager.ELAPSED_REALTIME), + verify(mAlarmManager).setExact( + eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when), eq(tag), eq(alarm), diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 94cf20f9c15b..1393d39e574f 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -504,6 +504,7 @@ public class NetworkPolicyManagerServiceTest { ArgumentCaptor.forClass(NetworkStatsManager.UsageCallback.class); verify(mStatsManager, times(2)) .registerUsageCallback(any(), anyLong(), any(), usageObserver.capture()); + // It doesn't matter which of the observers is returned here. usageObserver.getValue().onThresholdReached( new NetworkTemplate.Builder(MATCH_MOBILE).build()); diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 2bda120afb9d..f99f65eaad08 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -489,6 +489,20 @@ public class ApexManagerTest { assertThat(e).hasMessageThat().contains("Failed to collect certificates from "); } + @Test + public void testGetActivePackageNameForApexModuleName() throws Exception { + final String moduleName = "com.android.module_name"; + + ApexInfo[] apexInfo = createApexInfoForTestPkg(true, false); + apexInfo[0].moduleName = moduleName; + when(mApexService.getAllPackages()).thenReturn(apexInfo); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); + + assertThat(mApexManager.getActivePackageNameForApexModuleName(moduleName)) + .isEqualTo(TEST_APEX_PKG); + } + private ApexInfo[] createApexInfoForTestPkg(boolean isActive, boolean isFactory) { File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME); ApexInfo apexInfo = new ApexInfo(); diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java new file mode 100644 index 000000000000..1d9ea4b6028c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java @@ -0,0 +1,281 @@ +/* + * 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.server.pm.parsing.library; + +import android.os.Build; +import android.platform.test.annotations.Presubmit; +import android.util.ArrayMap; + +import androidx.test.filters.SmallTest; + +import com.android.server.SystemConfig; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.PackageImpl; +import com.android.server.pm.parsing.pkg.ParsedPackage; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + + +/** + * Test for {@link ApexSharedLibraryUpdater} + */ +@Presubmit +@SmallTest +@RunWith(JUnit4.class) +public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest { + + private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries = + new ArrayMap<>(8); + + @Before + public void setUp() throws Exception { + installSharedLibraries(); + } + + private void installSharedLibraries() throws Exception { + mSharedLibraries.clear(); + insertLibrary("foo", 0, 0); + insertLibrary("fooBcpSince30", 30, 0); + insertLibrary("fooBcpBefore30", 0, 30); + insertLibrary("fooFromFuture", Build.VERSION.SDK_INT + 2, 0); + } + + private void insertLibrary(String libraryName, int onBootclasspathSince, + int onBootclasspathBefore) { + mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry( + libraryName, + "foo.jar", + new String[0] /* dependencies */, + onBootclasspathSince, + onBootclasspathBefore + ) + ); + } + + @Test + public void testRegularAppOnRPlus() { + // platform Q should have changes (tested below) + + // these should have no changes + checkNoChanges(Build.VERSION_CODES.R); + checkNoChanges(Build.VERSION_CODES.S); + checkNoChanges(Build.VERSION_CODES.TIRAMISU); + checkNoChanges(Build.VERSION_CODES.CUR_DEVELOPMENT); + } + + private void checkNoChanges(int targetSdkVersion) { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(targetSdkVersion) + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(targetSdkVersion) + .hideAsParsed()) + .hideAsFinal(); + + checkBackwardsCompatibility(before, after); + } + + @Test + public void testBcpSince30Applied() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .addUsesLibrary("fooBcpSince30") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()) + .hideAsFinal(); + + // note: target sdk is not what matters in this logic. It's the system SDK + // should be removed because on 30+ (R+) it is implicit + + checkBackwardsCompatibility(before, after); + } + + @Test + public void testBcpSince11kNotAppliedWithoutLibrary() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()) + .hideAsFinal(); + + // note: target sdk is not what matters in this logic. It's the system SDK + // nothing should change because the implicit from is only from a future platform release + checkBackwardsCompatibility(before, after); + } + + @Test + public void testBcpSince11kNotAppliedWithLibrary() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .addUsesLibrary("fooFromFuture") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .addUsesLibrary("fooFromFuture") + .hideAsParsed()) + .hideAsFinal(); + + // note: target sdk is not what matters in this logic. It's the system SDK + // nothing should change because the implicit from is only from a future platform release + checkBackwardsCompatibility(before, after); + } + + @Test + public void testBcpBefore30NotApplied() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()) + .hideAsFinal(); + + // should not be affected because it is still in the BCP in 30 / R + checkBackwardsCompatibility(before, after); + } + + @Test + public void testBcpBefore30Applied() { + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.Q) + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.Q) + .addUsesLibrary("fooBcpBefore30") + .hideAsParsed()) + .hideAsFinal(); + + // should be present because this was in BCP in 29 / Q + checkBackwardsCompatibility(before, after); + } + + /** + * Test a library that was first removed from the BCP [to a mainline module] and later was + * moved back to the BCP via a mainline module update. All of this happening before the current + * SDK. + */ + @Test + public void testBcpRemovedThenAddedPast() { + insertLibrary("fooBcpRemovedThenAdded", 30, 28); + + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.N) + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.N) + .addUsesLibrary("fooBcpBefore30") + .hideAsParsed()) + .hideAsFinal(); + + // the library is now in the BOOTCLASSPATH (for the second time) so it doesn't need to be + // listed + checkBackwardsCompatibility(before, after); + } + + /** + * Test a library that was first removed from the BCP [to a mainline module] and later was + * moved back to the BCP via a mainline module update. The first part happening before the + * current SDK and the second part after. + */ + @Test + public void testBcpRemovedThenAddedMiddle_targetQ() { + insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); + + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.Q) + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.Q) + .addUsesLibrary("fooBcpRemovedThenAdded") + .addUsesLibrary("fooBcpBefore30") + .hideAsParsed()) + .hideAsFinal(); + + // in this example, we are at the point where the library is not in the BOOTCLASSPATH. + // Because the app targets Q / 29 (when this library was in the BCP) then we need to add it + checkBackwardsCompatibility(before, after); + } + + /** + * Test a library that was first removed from the BCP [to a mainline module] and later was + * moved back to the BCP via a mainline module update. The first part happening before the + * current SDK and the second part after. + */ + @Test + public void testBcpRemovedThenAddedMiddle_targetR() { + insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); + + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .hideAsParsed()) + .hideAsFinal(); + + // in this example, we are at the point where the library is not in the BOOTCLASSPATH. + // Because the app targets R/30 (when this library was removed from the BCP) then we don't + //need to add it + checkBackwardsCompatibility(before, after); + } + + /** + * Test a library that was first removed from the BCP [to a mainline module] and later was + * moved back to the BCP via a mainline module update. The first part happening before the + * current SDK and the second part after. + */ + @Test + public void testBcpRemovedThenAddedMiddle_targetR_usingLib() { + insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30); + + ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .addUsesLibrary("fooBcpRemovedThenAdded") + .hideAsParsed()); + + AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME) + .setTargetSdkVersion(Build.VERSION_CODES.R) + .addUsesLibrary("fooBcpRemovedThenAdded") + .hideAsParsed()) + .hideAsFinal(); + + // in this example, we are at the point where the library is not in the BOOTCLASSPATH. + // Because the app wants to use the library, it needs to be present + checkBackwardsCompatibility(before, after); + } + + private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { + checkBackwardsCompatibility(before, after, + () -> new ApexSharedLibraryUpdater(mSharedLibraries)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java index 9768f176ea85..5bcd0f6bb029 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java @@ -21,6 +21,8 @@ import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_T import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER; import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; +import static com.google.common.truth.Truth.assertThat; + import android.content.pm.parsing.ParsingPackage; import android.os.Build; import android.platform.test.annotations.Presubmit; @@ -182,6 +184,22 @@ public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdate checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal()); } + /** + * Ensures that ApexSharedLibraryUpdater is the last updater in the list of package updaters + * used by PackageBackwardCompatibility. + * + * This is required so mainline can add and remove libraries installed by the platform updaters. + */ + @Test + public void testApexPackageUpdaterOrdering() { + PackageBackwardCompatibility instance = + (PackageBackwardCompatibility) PackageBackwardCompatibility.getInstance(); + PackageSharedLibraryUpdater[] updaterArray = instance.getPackageUpdaters(); + + PackageSharedLibraryUpdater lastUpdater = updaterArray[updaterArray.length - 1]; + assertThat(lastUpdater).isInstanceOf(ApexSharedLibraryUpdater.class); + } + private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) { checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance); } diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigNamedActorTest.kt b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigNamedActorTest.kt index b7199d4a2443..150822bdff6b 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigNamedActorTest.kt +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigNamedActorTest.kt @@ -17,6 +17,7 @@ package com.android.server.systemconfig import android.content.Context +import android.util.Xml import androidx.test.InstrumentationRegistry import com.android.server.SystemConfig import com.google.common.truth.Truth.assertThat @@ -227,6 +228,7 @@ class SystemConfigNamedActorTest { .writeText(this.trimIndent()) private fun assertPermissions() = SystemConfig(false).apply { - readPermissions(tempFolder.root, 0) + val parser = Xml.newPullParser() + readPermissions(parser, tempFolder.root, 0) }. let { assertThat(it.namedActors) } } diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index 5eb21a58c38e..bfdffc0e6567 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -21,10 +21,12 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.testng.Assert.expectThrows; +import android.os.Build; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Xml; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -36,9 +38,12 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import java.io.BufferedWriter; import java.io.File; +import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; @@ -59,12 +64,15 @@ public class SystemConfigTest { private static final String LOG_TAG = "SystemConfigTest"; private SystemConfig mSysConfig; + private File mFooJar; @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); @Before public void setUp() throws Exception { mSysConfig = new SystemConfigTestClass(); + mFooJar = createTempFile( + mTemporaryFolder.getRoot().getCanonicalFile(), "foo.jar", "JAR"); } /** @@ -76,6 +84,11 @@ public class SystemConfigTest { } } + private void readPermissions(File libraryDir, int permissionFlag) { + final XmlPullParser parser = Xml.newPullParser(); + mSysConfig.readPermissions(parser, libraryDir, permissionFlag); + } + /** * Tests that readPermissions works correctly for the tag: install-in-user-type */ @@ -126,16 +139,17 @@ public class SystemConfigTest { new ArraySet<>(Arrays.asList("GUEST", "PROFILE"))); final File folder1 = createTempSubfolder("folder1"); - createTempFile(folder1, "permFile1.xml", contents1); + createTempFile(folder1, "permissionFile1.xml", contents1); final File folder2 = createTempSubfolder("folder2"); - createTempFile(folder2, "permFile2.xml", contents2); + createTempFile(folder2, "permissionFile2.xml", contents2); - // Also, make a third file, but with the name folder1/permFile2.xml, to prove no conflicts. - createTempFile(folder1, "permFile2.xml", contents3); + // Also, make a third file, but with the name folder1/permissionFile2.xml, to prove no + // conflicts. + createTempFile(folder1, "permissionFile2.xml", contents3); - mSysConfig.readPermissions(folder1, /* No permission needed anyway */ 0); - mSysConfig.readPermissions(folder2, /* No permission needed anyway */ 0); + readPermissions(folder1, /* No permission needed anyway */ 0); + readPermissions(folder2, /* No permission needed anyway */ 0); Map<String, Set<String>> actualWhite = mSysConfig.getAndClearPackageToUserTypeWhitelist(); Map<String, Set<String>> actualBlack = mSysConfig.getAndClearPackageToUserTypeBlacklist(); @@ -165,7 +179,7 @@ public class SystemConfigTest { final File folder = createTempSubfolder("folder"); createTempFile(folder, "component-override.xml", contents); - mSysConfig.readPermissions(folder, /* No permission needed anyway */ 0); + readPermissions(folder, /* No permission needed anyway */ 0); final ArrayMap<String, Boolean> packageOneExpected = new ArrayMap<>(); packageOneExpected.put("com.android.package1.Full", true); @@ -197,7 +211,7 @@ public class SystemConfigTest { final File folder = createTempSubfolder("folder"); createTempFile(folder, "staged-installer-whitelist.xml", contents); - mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0); + readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(mSysConfig.getWhitelistedStagedInstallers()) .containsExactly("com.android.package1"); @@ -215,7 +229,7 @@ public class SystemConfigTest { final File folder = createTempSubfolder("folder"); createTempFile(folder, "staged-installer-whitelist.xml", contents); - mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0); + readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(mSysConfig.getWhitelistedStagedInstallers()) .containsExactly("com.android.package1"); @@ -238,7 +252,7 @@ public class SystemConfigTest { IllegalStateException e = expectThrows( IllegalStateException.class, - () -> mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0)); + () -> readPermissions(folder, /* Grant all permission flags */ ~0)); assertThat(e).hasMessageThat().contains("Multiple modules installers"); } @@ -257,7 +271,7 @@ public class SystemConfigTest { final File folder = createTempSubfolder("folder"); createTempFile(folder, "staged-installer-whitelist.xml", contents); - mSysConfig.readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08); + readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08); assertThat(mSysConfig.getWhitelistedStagedInstallers()).isEmpty(); } @@ -277,7 +291,7 @@ public class SystemConfigTest { final File folder = createTempSubfolder("folder"); createTempFile(folder, "vendor-apex-allowlist.xml", contents); - mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0); + readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(mSysConfig.getAllowedVendorApexes()) .containsExactly("com.android.apex1", "com.installer"); @@ -297,7 +311,7 @@ public class SystemConfigTest { final File folder = createTempSubfolder("folder"); createTempFile(folder, "vendor-apex-allowlist.xml", contents); - mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0); + readPermissions(folder, /* Grant all permission flags */ ~0); assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty(); } @@ -317,11 +331,273 @@ public class SystemConfigTest { final File folder = createTempSubfolder("folder"); createTempFile(folder, "vendor-apex-allowlist.xml", contents); - mSysConfig.readPermissions(folder, /* Grant all but ALLOW_VENDOR_APEX flag */ ~0x400); + readPermissions(folder, /* Grant all but ALLOW_VENDOR_APEX flag */ ~0x400); assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty(); } + @Test + public void readApexPrivAppPermissions_addAllPermissions() + throws Exception { + final String contents = + "<privapp-permissions package=\"com.android.apk_in_apex\">" + + "<permission name=\"android.permission.FOO\"/>" + + "<deny-permission name=\"android.permission.BAR\"/>" + + "</privapp-permissions>"; + File apexDir = createTempSubfolder("apex"); + File permissionFile = createTempFile( + createTempSubfolder("apex/com.android.my_module/etc/permissions"), + "permissions.xml", contents); + XmlPullParser parser = readXmlUntilStartTag(permissionFile); + + mSysConfig.readApexPrivAppPermissions(parser, permissionFile, apexDir.toPath()); + + assertThat(mSysConfig.getApexPrivAppPermissions("com.android.my_module", + "com.android.apk_in_apex")) + .containsExactly("android.permission.FOO"); + assertThat(mSysConfig.getApexPrivAppDenyPermissions("com.android.my_module", + "com.android.apk_in_apex")) + .containsExactly("android.permission.BAR"); + } + + @Test + public void pruneVendorApexPrivappAllowlists_removeVendor() + throws Exception { + File apexDir = createTempSubfolder("apex"); + + // Read non-vendor apex permission allowlists + final String allowlistNonVendorContents = + "<privapp-permissions package=\"com.android.apk_in_non_vendor_apex\">" + + "<permission name=\"android.permission.FOO\"/>" + + "<deny-permission name=\"android.permission.BAR\"/>" + + "</privapp-permissions>"; + File nonVendorPermDir = + createTempSubfolder("apex/com.android.non_vendor/etc/permissions"); + File nonVendorPermissionFile = + createTempFile(nonVendorPermDir, "permissions.xml", allowlistNonVendorContents); + XmlPullParser nonVendorParser = readXmlUntilStartTag(nonVendorPermissionFile); + mSysConfig.readApexPrivAppPermissions(nonVendorParser, nonVendorPermissionFile, + apexDir.toPath()); + + // Read vendor apex permission allowlists + final String allowlistVendorContents = + "<privapp-permissions package=\"com.android.apk_in_vendor_apex\">" + + "<permission name=\"android.permission.BAZ\"/>" + + "<deny-permission name=\"android.permission.BAT\"/>" + + "</privapp-permissions>"; + File vendorPermissionFile = + createTempFile(createTempSubfolder("apex/com.android.vendor/etc/permissions"), + "permissions.xml", allowlistNonVendorContents); + XmlPullParser vendorParser = readXmlUntilStartTag(vendorPermissionFile); + mSysConfig.readApexPrivAppPermissions(vendorParser, vendorPermissionFile, + apexDir.toPath()); + + // Read allowed vendor apex list + final String allowedVendorContents = + "<config>\n" + + " <allowed-vendor-apex package=\"com.android.vendor\" " + + "installerPackage=\"com.installer\" />\n" + + "</config>"; + final File allowedVendorFolder = createTempSubfolder("folder"); + createTempFile(allowedVendorFolder, "vendor-apex-allowlist.xml", allowedVendorContents); + readPermissions(allowedVendorFolder, /* Grant all permission flags */ ~0); + + // Finally, prune non-vendor allowlists. + // There is no guarantee in which order the above reads will be done, however pruning + // will always happen last. + mSysConfig.pruneVendorApexPrivappAllowlists(); + + assertThat(mSysConfig.getApexPrivAppPermissions("com.android.non_vendor", + "com.android.apk_in_non_vendor_apex")) + .containsExactly("android.permission.FOO"); + assertThat(mSysConfig.getApexPrivAppDenyPermissions("com.android.non_vendor", + "com.android.apk_in_non_vendor_apex")) + .containsExactly("android.permission.BAR"); + assertThat(mSysConfig.getApexPrivAppPermissions("com.android.vendor", + "com.android.apk_in_vendor_apex")) + .isNull(); + assertThat(mSysConfig.getApexPrivAppDenyPermissions("com.android.vendor", + "com.android.apk_in_vendor_apex")) + .isNull(); + } + + /** + * Tests that readPermissions works correctly for a library with on-bootclasspath-before + * and on-bootclasspath-since. + */ + @Test + public void readPermissions_allowLibs_parsesSimpleLibrary() throws IOException { + String contents = + "<permissions>\n" + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " on-bootclasspath-before=\"10\"\n" + + " on-bootclasspath-since=\"20\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertFooIsOnlySharedLibrary(); + SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); + assertThat(entry.onBootclasspathBefore).isEqualTo(10); + assertThat(entry.onBootclasspathSince).isEqualTo(20); + } + + /** + * Tests that readPermissions works correctly for a library using the new + * {@code apex-library} tag. + */ + @Test + public void readPermissions_allowLibs_parsesUpdatableLibrary() throws IOException { + String contents = + "<permissions>\n" + + " <apex-library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " on-bootclasspath-before=\"10\"\n" + + " on-bootclasspath-since=\"20\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertFooIsOnlySharedLibrary(); + SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); + assertThat(entry.onBootclasspathBefore).isEqualTo(10); + assertThat(entry.onBootclasspathSince).isEqualTo(20); + } + + /** + * Tests that readPermissions for a library with {@code min-device-sdk} lower than the current + * SDK results in the library being added to the shared libraries. + */ + @Test + public void readPermissions_allowLibs_allowsOldMinSdk() throws IOException { + String contents = + "<permissions>\n" + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " min-device-sdk=\"30\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertFooIsOnlySharedLibrary(); + } + + /** + * Tests that readPermissions for a library with {@code min-device-sdk} equal to the current + * SDK results in the library being added to the shared libraries. + */ + @Test + public void readPermissions_allowLibs_allowsCurrentMinSdk() throws IOException { + String contents = + "<permissions>\n" + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " min-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertFooIsOnlySharedLibrary(); + } + + /** + * Tests that readPermissions for a library with {@code min-device-sdk} greater than the current + * SDK results in the library being ignored. + */ + @Test + public void readPermissions_allowLibs_ignoresMinSdkInFuture() throws IOException { + String contents = + "<permissions>\n" + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " min-device-sdk=\"" + (Build.VERSION.SDK_INT + 1) + "\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertThat(mSysConfig.getSharedLibraries()).isEmpty(); + } + + /** + * Tests that readPermissions for a library with {@code max-device-sdk} less than the current + * SDK results in the library being ignored. + */ + @Test + public void readPermissions_allowLibs_ignoredOldMaxSdk() throws IOException { + String contents = + "<permissions>\n" + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " max-device-sdk=\"30\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertThat(mSysConfig.getSharedLibraries()).isEmpty(); + } + + /** + * Tests that readPermissions for a library with {@code max-device-sdk} equal to the current + * SDK results in the library being added to the shared libraries. + */ + @Test + public void readPermissions_allowLibs_allowsCurrentMaxSdk() throws IOException { + String contents = + "<permissions>\n" + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " max-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertFooIsOnlySharedLibrary(); + } + + /** + * Tests that readPermissions for a library with {@code max-device-sdk} greater than the current + * SDK results in the library being added to the shared libraries. + */ + @Test + public void readPermissions_allowLibs_allowsMaxSdkInFuture() throws IOException { + String contents = + "<permissions>\n" + + " <library \n" + + " name=\"foo\"\n" + + " file=\"" + mFooJar + "\"\n" + + " max-device-sdk=\"" + (Build.VERSION.SDK_INT + 1) + "\"\n" + + " />\n\n" + + " </permissions>"; + parseSharedLibraries(contents); + assertFooIsOnlySharedLibrary(); + } + + private void parseSharedLibraries(String contents) throws IOException { + File folder = createTempSubfolder("permissions_folder"); + createTempFile(folder, "permissions.xml", contents); + readPermissions(folder, /* permissionFlag = ALLOW_LIBS */ 0x02); + } + + /** + * Create an {@link XmlPullParser} for {@param permissionFile} and begin parsing it until + * reaching the root tag. + */ + private XmlPullParser readXmlUntilStartTag(File permissionFile) + throws IOException, XmlPullParserException { + FileReader permReader = new FileReader(permissionFile); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(permReader); + int type; + do { + type = parser.next(); + } while (type != parser.START_TAG && type != parser.END_DOCUMENT); + if (type != parser.START_TAG) { + throw new XmlPullParserException("No start tag found"); + } + return parser; + } + /** * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents. * @@ -331,7 +607,7 @@ public class SystemConfigTest { private File createTempSubfolder(String folderName) throws IOException { File folder = new File(mTemporaryFolder.getRoot(), folderName); - folder.mkdir(); + folder.mkdirs(); return folder; } @@ -341,7 +617,7 @@ public class SystemConfigTest { * @param folder pre-existing subdirectory of mTemporaryFolder to put the file * @param fileName name of the file (e.g. filename.xml) to create * @param contents contents to write to the file - * @return the folder containing the newly created file (not the file itself!) + * @return the newly created file */ private File createTempFile(File folder, String fileName, String contents) throws IOException { @@ -357,6 +633,13 @@ public class SystemConfigTest { Log.d(LOG_TAG, input.nextLine()); } - return folder; + return file; + } + + private void assertFooIsOnlySharedLibrary() { + assertThat(mSysConfig.getSharedLibraries().size()).isEqualTo(1); + SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); + assertThat(entry.name).isEqualTo("foo"); + assertThat(entry.filename).isEqualTo(mFooJar.toString()); } } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 32fed3bc3dc1..4519890e72a1 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -252,7 +252,7 @@ public class TimeDetectorServiceTest { int slotIndex = 1234; TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L); return new TelephonyTimeSuggestion.Builder(slotIndex) - .setUtcTime(timeValue) + .setUnixEpochTime(timeValue) .build(); } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 0d5b5a565d5a..2d9903f9cf60 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -52,12 +52,12 @@ import java.util.Objects; @RunWith(AndroidJUnit4.class) public class TimeDetectorStrategyImplTest { - private static final Instant TIME_LOWER_BOUND = createUtcTime(2009, 1, 1, 12, 0, 0); + private static final Instant TIME_LOWER_BOUND = createUnixEpochTime(2009, 1, 1, 12, 0, 0); private static final TimestampedValue<Instant> ARBITRARY_CLOCK_INITIALIZATION_INFO = new TimestampedValue<>( 123456789L /* realtimeClockMillis */, - createUtcTime(2010, 5, 23, 12, 0, 0)); + createUnixEpochTime(2010, 5, 23, 12, 0, 0)); // This is the traditional ordering for time detection on Android. private static final @Origin int [] PROVIDERS_PRIORITY = { ORIGIN_TELEPHONY, ORIGIN_NETWORK }; @@ -66,7 +66,7 @@ public class TimeDetectorStrategyImplTest { * An arbitrary time, very different from the {@link #ARBITRARY_CLOCK_INITIALIZATION_INFO} * time. Can be used as the basis for time suggestions. */ - private static final Instant ARBITRARY_TEST_TIME = createUtcTime(2018, 1, 1, 12, 0, 0); + private static final Instant ARBITRARY_TEST_TIME = createUnixEpochTime(2018, 1, 1, 12, 0, 0); private static final int ARBITRARY_SLOT_INDEX = 123456; @@ -91,7 +91,7 @@ public class TimeDetectorStrategyImplTest { .simulateTelephonyTimeSuggestion(timeSuggestion); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); mScript.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion); } @@ -128,7 +128,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(clockIncrementMillis); long expectedSystemClockMillis1 = - mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime()); + mScript.calculateTimeInMillisForNow(timeSuggestion1.getUnixEpochTime()); mScript.simulateTelephonyTimeSuggestion(timeSuggestion1) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) @@ -155,7 +155,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(clockIncrementMillis); long expectedSystemClockMillis3 = - mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime()); + mScript.calculateTimeInMillisForNow(timeSuggestion3.getUnixEpochTime()); mScript.simulateTelephonyTimeSuggestion(timeSuggestion3) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis3) @@ -182,8 +182,8 @@ public class TimeDetectorStrategyImplTest { mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2Time); mScript.simulateTimePassing(); - long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(slotIndex2TimeSuggestion.getUtcTime()); + long expectedSystemClockMillis = mScript.calculateTimeInMillisForNow( + slotIndex2TimeSuggestion.getUnixEpochTime()); mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) @@ -199,8 +199,8 @@ public class TimeDetectorStrategyImplTest { mScript.generateTelephonyTimeSuggestion(slotIndex1, slotIndex1Time); mScript.simulateTimePassing(); - long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(slotIndex1TimeSuggestion.getUtcTime()); + long expectedSystemClockMillis = mScript.calculateTimeInMillisForNow( + slotIndex1TimeSuggestion.getUnixEpochTime()); mScript.simulateTelephonyTimeSuggestion(slotIndex1TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) @@ -232,8 +232,8 @@ public class TimeDetectorStrategyImplTest { mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2Time); mScript.simulateTimePassing(); - long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(slotIndex2TimeSuggestion.getUtcTime()); + long expectedSystemClockMillis = mScript.calculateTimeInMillisForNow( + slotIndex2TimeSuggestion.getUnixEpochTime()); mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) @@ -267,26 +267,27 @@ public class TimeDetectorStrategyImplTest { TelephonyTimeSuggestion timeSuggestion1 = mScript.generateTelephonyTimeSuggestion(slotIndex, testTime); - TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); + TimestampedValue<Long> unixEpochTime1 = timeSuggestion1.getUnixEpochTime(); // Initialize the strategy / device with a time set from a telephony suggestion. mScript.simulateTimePassing(); - long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1); + long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(unixEpochTime1); mScript.simulateTelephonyTimeSuggestion(timeSuggestion1) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); - // The UTC time increment should be larger than the system clock update threshold so we - // know it shouldn't be ignored for other reasons. - long validUtcTimeMillis = utcTime1.getValue() + (2 * systemClockUpdateThreshold); + // The Unix epoch time increment should be larger than the system clock update threshold so + // we know it shouldn't be ignored for other reasons. + long validUnixEpochTimeMillis = unixEpochTime1.getValue() + + (2 * systemClockUpdateThreshold); // Now supply a new signal that has an obviously bogus reference time : older than the last // one. - long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1; - TimestampedValue<Long> utcTime2 = new TimestampedValue<>( - referenceTimeBeforeLastSignalMillis, validUtcTimeMillis); + long referenceTimeBeforeLastSignalMillis = unixEpochTime1.getReferenceTimeMillis() - 1; + TimestampedValue<Long> unixEpochTime2 = new TimestampedValue<>( + referenceTimeBeforeLastSignalMillis, validUnixEpochTimeMillis); TelephonyTimeSuggestion timeSuggestion2 = - createTelephonyTimeSuggestion(slotIndex, utcTime2); + createTelephonyTimeSuggestion(slotIndex, unixEpochTime2); mScript.simulateTelephonyTimeSuggestion(timeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -294,22 +295,22 @@ public class TimeDetectorStrategyImplTest { // Now supply a new signal that has an obviously bogus reference time : substantially in the // future. long referenceTimeInFutureMillis = - utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1; - TimestampedValue<Long> utcTime3 = new TimestampedValue<>( - referenceTimeInFutureMillis, validUtcTimeMillis); + unixEpochTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1; + TimestampedValue<Long> unixEpochTime3 = new TimestampedValue<>( + referenceTimeInFutureMillis, validUnixEpochTimeMillis); TelephonyTimeSuggestion timeSuggestion3 = - createTelephonyTimeSuggestion(slotIndex, utcTime3); + createTelephonyTimeSuggestion(slotIndex, unixEpochTime3); mScript.simulateTelephonyTimeSuggestion(timeSuggestion3) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); - // Just to prove validUtcTimeMillis is valid. - long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100; - TimestampedValue<Long> utcTime4 = new TimestampedValue<>( - validReferenceTimeMillis, validUtcTimeMillis); - long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(utcTime4); + // Just to prove validUnixEpochTimeMillis is valid. + long validReferenceTimeMillis = unixEpochTime1.getReferenceTimeMillis() + 100; + TimestampedValue<Long> unixEpochTime4 = new TimestampedValue<>( + validReferenceTimeMillis, validUnixEpochTimeMillis); + long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(unixEpochTime4); TelephonyTimeSuggestion timeSuggestion4 = - createTelephonyTimeSuggestion(slotIndex, utcTime4); + createTelephonyTimeSuggestion(slotIndex, unixEpochTime4); mScript.simulateTelephonyTimeSuggestion(timeSuggestion4) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion4); @@ -344,7 +345,7 @@ public class TimeDetectorStrategyImplTest { Instant testTime = ARBITRARY_TEST_TIME; TelephonyTimeSuggestion timeSuggestion1 = mScript.generateTelephonyTimeSuggestion(slotIndex, testTime); - TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); + TimestampedValue<Long> unixEpochTime1 = timeSuggestion1.getUnixEpochTime(); // Simulate time passing. mScript.simulateTimePassing(clockIncrementMillis); @@ -358,7 +359,7 @@ public class TimeDetectorStrategyImplTest { // Simulate more time passing. mScript.simulateTimePassing(clockIncrementMillis); - long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1); + long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(unixEpochTime1); // Turn on auto time detection. mScript.simulateAutoTimeDetectionToggle() @@ -379,7 +380,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(clockIncrementMillis); long expectedSystemClockMillis2 = - mScript.calculateTimeInMillisForNow(timeSuggestion2.getUtcTime()); + mScript.calculateTimeInMillisForNow(timeSuggestion2.getUnixEpochTime()); // The new time, though valid, should not be set in the system clock because auto time is // disabled. @@ -406,7 +407,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(telephonySuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(telephonySuggestion.getUnixEpochTime()); mScript.simulateTelephonyTimeSuggestion(telephonySuggestion) .verifySystemClockWasSetAndResetCallTracking( expectedSystemClockMillis /* expectedNetworkBroadcast */) @@ -416,7 +417,7 @@ public class TimeDetectorStrategyImplTest { assertEquals(telephonySuggestion, mScript.peekBestTelephonySuggestion()); // Simulate time passing, long enough that telephonySuggestion is now too old. - mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS); + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS); // Look inside and check what the strategy considers the current best telephony suggestion. // It should still be the, it's just no longer used. @@ -435,7 +436,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); mScript.simulateManualTimeSuggestion(timeSuggestion, true /* expectedResult */) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -457,7 +458,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); long expectedAutoClockMillis = - mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUnixEpochTime()); mScript.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -480,7 +481,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); long expectedManualClockMillis = - mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUnixEpochTime()); mScript.simulateManualTimeSuggestion(manualTimeSuggestion, true /* expectedResult */) .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -492,7 +493,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateAutoTimeDetectionToggle(); expectedAutoClockMillis = - mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUnixEpochTime()); mScript.verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -540,7 +541,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); mScript.simulateNetworkTimeSuggestion(timeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -586,7 +587,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); mScript.simulateGnssTimeSuggestion(timeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -617,7 +618,7 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); mScript.simulateExternalTimeSuggestion(timeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -671,7 +672,8 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateNetworkTimeSuggestion(networkTimeSuggestion1) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime())); + mScript.calculateTimeInMillisForNow( + networkTimeSuggestion1.getUnixEpochTime())); // Check internal state. mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, null) @@ -690,7 +692,8 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime())); + mScript.calculateTimeInMillisForNow( + telephonyTimeSuggestion.getUnixEpochTime())); // Check internal state. mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) @@ -700,7 +703,7 @@ public class TimeDetectorStrategyImplTest { // Simulate some significant time passing: half the time allowed before a time signal // becomes "too old to use". - mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2) + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS / 2) .verifySystemClockWasNotSetAndResetCallTracking(); // Now another network suggestion is made. Telephony suggestions are prioritized over @@ -720,7 +723,7 @@ public class TimeDetectorStrategyImplTest { // Simulate some significant time passing: half the time allowed before a time signal // becomes "too old to use". This should mean that telephonyTimeSuggestion is now too old to // be used but networkTimeSuggestion2 is not. - mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2); + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS / 2); // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last // suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle @@ -743,7 +746,7 @@ public class TimeDetectorStrategyImplTest { // Verify the latest network time now wins. mScript.verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime())); + mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUnixEpochTime())); // Check internal state. mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) @@ -774,7 +777,8 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateGnssTimeSuggestion(gnssTimeSuggestion1) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(gnssTimeSuggestion1.getUtcTime())); + mScript.calculateTimeInMillisForNow( + gnssTimeSuggestion1.getUnixEpochTime())); // Check internal state. mScript.assertLatestNetworkSuggestion(null) @@ -793,7 +797,8 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateNetworkTimeSuggestion(networkTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(networkTimeSuggestion.getUtcTime())); + mScript.calculateTimeInMillisForNow( + networkTimeSuggestion.getUnixEpochTime())); // Check internal state. mScript.assertLatestNetworkSuggestion(networkTimeSuggestion) @@ -803,7 +808,7 @@ public class TimeDetectorStrategyImplTest { // Simulate some significant time passing: half the time allowed before a time signal // becomes "too old to use". - mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2) + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS / 2) .verifySystemClockWasNotSetAndResetCallTracking(); // Now another gnss suggestion is made. Network suggestions are prioritized over @@ -823,7 +828,7 @@ public class TimeDetectorStrategyImplTest { // Simulate some significant time passing: half the time allowed before a time signal // becomes "too old to use". This should mean that telephonyTimeSuggestion is now too old to // be used but networkTimeSuggestion2 is not. - mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2); + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS / 2); // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last // suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle @@ -846,7 +851,7 @@ public class TimeDetectorStrategyImplTest { // Verify the latest gnss time now wins. mScript.verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(gnssTimeSuggestion2.getUtcTime())); + mScript.calculateTimeInMillisForNow(gnssTimeSuggestion2.getUnixEpochTime())); // Check internal state. mScript.assertLatestNetworkSuggestion(networkTimeSuggestion) @@ -877,7 +882,8 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateExternalTimeSuggestion(externalTimeSuggestion1) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(externalTimeSuggestion1.getUtcTime())); + mScript.calculateTimeInMillisForNow( + externalTimeSuggestion1.getUnixEpochTime())); // Check internal state. mScript.assertLatestNetworkSuggestion(null) @@ -896,7 +902,8 @@ public class TimeDetectorStrategyImplTest { mScript.simulateTimePassing(smallTimeIncrementMillis) .simulateNetworkTimeSuggestion(networkTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(networkTimeSuggestion.getUtcTime())); + mScript.calculateTimeInMillisForNow( + networkTimeSuggestion.getUnixEpochTime())); // Check internal state. mScript.assertLatestNetworkSuggestion(networkTimeSuggestion) @@ -906,7 +913,7 @@ public class TimeDetectorStrategyImplTest { // Simulate some significant time passing: half the time allowed before a time signal // becomes "too old to use". - mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2) + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS / 2) .verifySystemClockWasNotSetAndResetCallTracking(); // Now another external suggestion is made. Network suggestions are prioritized over @@ -926,7 +933,7 @@ public class TimeDetectorStrategyImplTest { // Simulate some significant time passing: half the time allowed before a time signal // becomes "too old to use". This should mean that networkTimeSuggestion is now too old to // be used but externalTimeSuggestion2 is not. - mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2); + mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS / 2); // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last // suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle @@ -949,7 +956,7 @@ public class TimeDetectorStrategyImplTest { // Verify the latest external time now wins. mScript.verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(externalTimeSuggestion2.getUtcTime())); + mScript.calculateTimeInMillisForNow(externalTimeSuggestion2.getUnixEpochTime())); // Check internal state. mScript.assertLatestNetworkSuggestion(networkTimeSuggestion) @@ -1438,11 +1445,11 @@ public class TimeDetectorStrategyImplTest { * reference time. */ ManualTimeSuggestion generateManualTimeSuggestion(Instant suggestedTime) { - TimestampedValue<Long> utcTime = + TimestampedValue<Long> unixEpochTime = new TimestampedValue<>( mFakeEnvironment.peekElapsedRealtimeMillis(), suggestedTime.toEpochMilli()); - return new ManualTimeSuggestion(utcTime); + return new ManualTimeSuggestion(unixEpochTime); } /** @@ -1472,11 +1479,11 @@ public class TimeDetectorStrategyImplTest { * reference time. */ NetworkTimeSuggestion generateNetworkTimeSuggestion(Instant suggestedTime) { - TimestampedValue<Long> utcTime = + TimestampedValue<Long> unixEpochTime = new TimestampedValue<>( mFakeEnvironment.peekElapsedRealtimeMillis(), suggestedTime.toEpochMilli()); - return new NetworkTimeSuggestion(utcTime); + return new NetworkTimeSuggestion(unixEpochTime); } /** @@ -1484,11 +1491,11 @@ public class TimeDetectorStrategyImplTest { * reference time. */ GnssTimeSuggestion generateGnssTimeSuggestion(Instant suggestedTime) { - TimestampedValue<Long> utcTime = + TimestampedValue<Long> unixEpochTime = new TimestampedValue<>( mFakeEnvironment.peekElapsedRealtimeMillis(), suggestedTime.toEpochMilli()); - return new GnssTimeSuggestion(utcTime); + return new GnssTimeSuggestion(unixEpochTime); } /** @@ -1504,19 +1511,19 @@ public class TimeDetectorStrategyImplTest { * Calculates what the supplied time would be when adjusted for the movement of the fake * elapsed realtime clock. */ - long calculateTimeInMillisForNow(TimestampedValue<Long> utcTime) { - return TimeDetectorStrategy.getTimeAt(utcTime, peekElapsedRealtimeMillis()); + long calculateTimeInMillisForNow(TimestampedValue<Long> unixEpochTime) { + return TimeDetectorStrategy.getTimeAt(unixEpochTime, peekElapsedRealtimeMillis()); } } private static TelephonyTimeSuggestion createTelephonyTimeSuggestion(int slotIndex, - TimestampedValue<Long> utcTime) { + TimestampedValue<Long> unixEpochTime) { return new TelephonyTimeSuggestion.Builder(slotIndex) - .setUtcTime(utcTime) + .setUnixEpochTime(unixEpochTime) .build(); } - private static Instant createUtcTime(int year, int monthInYear, int day, int hourOfDay, + private static Instant createUnixEpochTime(int year, int monthInYear, int day, int hourOfDay, int minute, int second) { return LocalDateTime.of(year, monthInYear, day, hourOfDay, minute, second) .toInstant(ZoneOffset.UTC); diff --git a/services/translation/OWNERS b/services/translation/OWNERS index a1e663aa8ff7..440f9a840057 100644 --- a/services/translation/OWNERS +++ b/services/translation/OWNERS @@ -1,8 +1,3 @@ # Bug component: 994311 -adamhe@google.com -augale@google.com -joannechung@google.com -lpeter@google.com -svetoslavganov@google.com -tymtsai@google.com +include /core/java/android/view/translation/OWNERS diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 8cb0909def5d..21f789f4e735 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -492,6 +492,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser // current USB state private boolean mHostConnected; + private boolean mUsbAccessoryConnected; private boolean mSourcePower; private boolean mSinkPower; private boolean mConfigured; @@ -961,10 +962,10 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser break; case MSG_UPDATE_HOST_STATE: Iterator devices = (Iterator) msg.obj; - boolean connected = (msg.arg1 == 1); + mUsbAccessoryConnected = (msg.arg1 == 1); if (DEBUG) { - Slog.i(TAG, "HOST_STATE connected:" + connected); + Slog.i(TAG, "HOST_STATE connected:" + mUsbAccessoryConnected); } mHideUsbNotification = false; @@ -1218,7 +1219,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser } else if (mSourcePower) { titleRes = com.android.internal.R.string.usb_supplying_notification_title; id = SystemMessage.NOTE_USB_SUPPLYING; - } else if (mHostConnected && mSinkPower && mUsbCharging) { + } else if (mHostConnected && mSinkPower && (mUsbCharging || mUsbAccessoryConnected)) { titleRes = com.android.internal.R.string.usb_charging_notification_title; id = SystemMessage.NOTE_USB_CHARGING; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 047207f2b764..4f94a6213234 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -28,6 +28,7 @@ import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; +import android.net.NetworkCapabilities; import android.net.ipsec.ike.SaProposal; import android.os.Build; import android.os.PersistableBundle; @@ -3777,30 +3778,42 @@ public class CarrierConfigManager { public static final String KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL = "opportunistic_esim_download_via_wifi_only_bool"; - /** - * Controls RSRP threshold at which OpportunisticNetworkService will decide whether +/** + * Controls RSRP threshold, in dBm, at which OpportunisticNetworkService will decide whether * the opportunistic network is good enough for internet data. + * + * <p>The value of {@link CellSignalStrengthLte#getRsrp()} will be compared with this + * threshold. */ public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT = "opportunistic_network_entry_threshold_rsrp_int"; /** - * Controls RSSNR threshold at which OpportunisticNetworkService will decide whether - * the opportunistic network is good enough for internet data. + * Controls RSSNR threshold, in dB, at which OpportunisticNetworkService will + * decide whether the opportunistic network is good enough for internet data. + * + * <p>The value of {@link CellSignalStrengthLte#getRssnr()} will be compared with this + * threshold. */ public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT = "opportunistic_network_entry_threshold_rssnr_int"; /** - * Controls RSRP threshold below which OpportunisticNetworkService will decide whether + * Controls RSRP threshold, in dBm, below which OpportunisticNetworkService will decide whether * the opportunistic network available is not good enough for internet data. + * + * <p>The value of {@link CellSignalStrengthLte#getRsrp()} will be compared with this + * threshold. */ public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT = "opportunistic_network_exit_threshold_rsrp_int"; /** - * Controls RSSNR threshold below which OpportunisticNetworkService will decide whether - * the opportunistic network available is not good enough for internet data. + * Controls RSSNR threshold, in dB, below which OpportunisticNetworkService will + * decide whether the opportunistic network available is not good enough for internet data. + * + * <p>The value of {@link CellSignalStrengthLte#getRssnr()} will be compared with this + * threshold. */ public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int"; @@ -3884,101 +3897,248 @@ public class CarrierConfigManager { public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long"; - /** - * Controls SS-RSRP threshold in dBm at which 5G opportunistic network will be considered good - * enough for internet data. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRP_INT = - "opportunistic_network_entry_threshold_ss_rsrp_int"; + /** @hide */ + public static class OpportunisticNetwork { + /** + * Prefix of all {@code OpportunisticNetwork.KEY_*} constants. + * + * @hide + */ + public static final String PREFIX = "opportunistic."; - /** - * Controls SS-RSRQ threshold in dB at which 5G opportunistic network will be considered good - * enough for internet data. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE = - "opportunistic_network_entry_threshold_ss_rsrq_double"; + /** + * Controls SS-RSRP threshold in dBm at which 5G opportunistic network will be considered + * good enough for internet data. Note other factors may be considered for the final + * decision. + * + * <p>The value of {@link CellSignalStrengthNr#getSsRsrp()} will be compared with this + * threshold. + * + * @hide + */ + public static final String KEY_ENTRY_THRESHOLD_SS_RSRP_INT = + PREFIX + "entry_threshold_ss_rsrp_int"; - /** - * Controls SS-RSRP threshold in dBm below which 5G opportunistic network available will not - * be considered good enough for internet data. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT = - "opportunistic_network_exit_threshold_ss_rsrp_int"; + /** + * Similar to {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT} but supports different + * thresholds for different 5G bands. For bands not specified here, the threshold + * will be {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT}. + * + * <p>For each key-value in the bundle: the key is the band number in string, which + * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants; + * the value is the threshold in int. + * + * @hide + */ + public static final String KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE = + PREFIX + "entry_threshold_ss_rsrp_int_bundle"; - /** - * Controls SS-RSRQ threshold in dB below which 5G opportunistic network available will not - * be considered good enough for internet data. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_DOUBLE = - "opportunistic_network_exit_threshold_ss_rsrq_double"; + /** + * Controls SS-RSRQ threshold in dB at which 5G opportunistic network will be considered + * good enough for internet data. Note other factors may be considered for the final + * decision. + * + * <p>The value of {@link CellSignalStrengthNr#getSsRsrq()} will be compared with this + * threshold. + * + * @hide + */ + public static final String KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE = + PREFIX + "entry_threshold_ss_rsrq_double"; - /** - * Controls back off time in milliseconds for switching back to - * 5G opportunistic subscription. This time will be added to - * {@link CarrierConfigManager#KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} to - * determine hysteresis time if there is ping pong situation - * (determined by system app or 1st party app) between primary and 5G opportunistic - * subscription. Ping ping situation is defined in - * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG. - * If ping pong situation continuous #KEY_OPPORTUNISTIC_5G_NETWORK_BACKOFF_TIME_LONG - * will be added to previously determined hysteresis time. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG = - "opportunistic_network_5g_backoff_time_long"; + /** + * Similar to {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE} but supports different + * thresholds for different 5G bands. For bands not specified here, the threshold + * will be {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE}. + * + * <p>For each key-value in the bundle: the key is the band number in string, which + * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants; + * the value is the threshold in double. + * + * @hide + */ + public static final String KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE = + PREFIX + "entry_threshold_ss_rsrq_double_bundle"; - /** - * Controls the max back off time in milliseconds for switching back to - * 5G opportunistic subscription. - * This time will be the max hysteresis that can be determined irrespective of there is - * continuous ping pong situation or not as described in - * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG and - * #KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_5G_MAX_BACKOFF_TIME_LONG = - "opportunistic_network_5g_max_backoff_time_long"; + /** + * Controls SS-RSRP threshold in dBm below which 5G opportunistic network available will not + * be considered good enough for internet data. Note other factors may be considered + * for the final decision. + * + * <p>The value of {@link CellSignalStrengthNr#getSsRsrp()} will be compared with this + * threshold. + * + * @hide + */ + public static final String KEY_EXIT_THRESHOLD_SS_RSRP_INT = + PREFIX + "exit_threshold_ss_rsrp_int"; - /** - * Controls the ping pong determination of 5G opportunistic network. - * If opportunistic network is determined as out of service or below - * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT or - * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_INT within - * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG of switching to opportunistic network, - * it will be determined as ping pong situation by system app or 1st party app. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG = - "opportunistic_network_5g_ping_pong_time_long"; + /** + * Similar to {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT} but supports different + * thresholds for different 5G bands. For bands not specified here, the threshold + * will be {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT}. + * + * <p>The syntax of its value is similar to + * {@link #KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE}. + * + * @hide + */ + public static final String KEY_EXIT_THRESHOLD_SS_RSRP_INT_BUNDLE = + PREFIX + "exit_threshold_ss_rsrp_int_bundle"; - /** - * Controls hysteresis time in milliseconds for which will be waited before switching - * data to a 5G opportunistic network. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG = - "opportunistic_network_5g_data_switch_hysteresis_time_long"; + /** + * Controls SS-RSRQ threshold in dB below which 5G opportunistic network available will not + * be considered good enough for internet data. Note other factors may be considered + * for the final decision. + * + * <p>The value of {@link CellSignalStrengthNr#getSsRsrq()} will be compared with this + * threshold. + * + * @hide + */ + public static final String KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE = + PREFIX + "exit_threshold_ss_rsrq_double"; + + /** + * Similar to {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE} but supports different + * thresholds for different 5G bands. For bands not specified here, the threshold + * will be {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE}. + * + * <p>The syntax of its value is similar to + * {@link #KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE}. + * + * @hide + */ + public static final String KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE = + PREFIX + "exit_threshold_ss_rsrq_double_bundle"; + + /** + * Controls hysteresis time in milliseconds for which will be waited before switching + * data to a 5G opportunistic network. + * + * @hide + */ + public static final String KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG = + PREFIX + "5g_data_switch_hysteresis_time_long"; + + /** + * Similar to {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} but supports + * different values for different 5G bands. For bands not specified here, the threshold + * will be {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG}. + * + * <p>For each key-value in the bundle: the key is the band number in string, which + * shall be a decimal integer as defined in {@code NgranBands.BAND_*} constants; + * the value is the time in long. + * + * @hide + */ + public static final String KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE = + PREFIX + "5g_data_switch_hysteresis_time_long_bundle"; + + /** + * Controls hysteresis time in milliseconds for which will be waited before switching from + * 5G opportunistic network to primary network. + * + * @hide + */ + public static final String KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG = + PREFIX + "5g_data_switch_exit_hysteresis_time_long"; + + /** + * Similar to {@link #KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG} but supports + * different values for different 5G bands. For bands not specified here, the threshold + * will be {@link #KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG}. + * + * <p>The syntax is similar to + * {@link KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE}. + * + * @hide + */ + public static final String KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG_BUNDLE = + PREFIX + "5g_data_switch_exit_hysteresis_time_long_bundle"; + + /** + * Controls back off time in milliseconds for switching back to + * 5G opportunistic subscription. This time will be added to + * {@link #KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} to + * determine hysteresis time if there is ping pong situation + * (determined by system app or 1st party app) between primary and 5G opportunistic + * subscription. Ping ping situation is defined in + * {@link #KEY_5G_PING_PONG_TIME_LONG}. + * If ping pong situation continuous {@link #KEY_5G_NETWORK_BACKOFF_TIME_LONG} + * will be added to previously determined hysteresis time. + * + * @hide + */ + public static final String KEY_5G_BACKOFF_TIME_LONG = + PREFIX + "5g_backoff_time_long"; + + /** + * Controls the max back off time in milliseconds for switching back to + * 5G opportunistic subscription. + * This time will be the max hysteresis that can be determined irrespective of there is + * continuous ping pong situation or not as described in + * {@link #KEY_5G_PING_PONG_TIME_LONG} and + * {@link #KEY_5G_BACKOFF_TIME_LONG}. + * + * @hide + */ + public static final String KEY_5G_MAX_BACKOFF_TIME_LONG = + PREFIX + "5g_max_backoff_time_long"; + + /** + * Controls the ping pong determination of 5G opportunistic network. + * If opportunistic network is determined as out of service or below + * {@link #KEY_EXIT_THRESHOLD_SS_RSRP_INT} or + * {@link #KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE} within + * {@link #KEY_5G_PING_PONG_TIME_LONG} of switching to opportunistic network, + * it will be determined as ping pong situation by system app or 1st party app. + * + * @hide + */ + public static final String KEY_5G_PING_PONG_TIME_LONG = + PREFIX + "5g_ping_pong_time_long"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + // Default value is -111 dBm for all bands. + sDefaults.putInt(KEY_ENTRY_THRESHOLD_SS_RSRP_INT, -111); + sDefaults.putPersistableBundle(KEY_ENTRY_THRESHOLD_SS_RSRP_INT_BUNDLE, + PersistableBundle.EMPTY); + // Default value is -18.5 dB for all bands. + sDefaults.putDouble(KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE, -18.5); + sDefaults.putPersistableBundle( + KEY_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE, + PersistableBundle.EMPTY); + // Default value is -120 dBm for all bands. + sDefaults.putInt(KEY_EXIT_THRESHOLD_SS_RSRP_INT, -120); + sDefaults.putPersistableBundle(KEY_EXIT_THRESHOLD_SS_RSRP_INT_BUNDLE, + PersistableBundle.EMPTY); + // Default value is -18.5 dB for all bands. + sDefaults.putDouble(KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE, -18.5); + sDefaults.putPersistableBundle( + KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE_BUNDLE, + PersistableBundle.EMPTY); + // Default value is 2 seconds for all bands. + defaults.putLong(KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG, 2000); + defaults.putPersistableBundle( + KEY_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG_BUNDLE, + PersistableBundle.EMPTY); + // Default value is 2 seconds for all bands. + defaults.putLong(KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000); + defaults.putPersistableBundle( + KEY_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG_BUNDLE, + PersistableBundle.EMPTY); + // Default value is 10 seconds. + sDefaults.putLong(KEY_5G_BACKOFF_TIME_LONG, 10000); + // Default value is 60 seconds. + sDefaults.putLong(KEY_5G_MAX_BACKOFF_TIME_LONG, 60000); + // Default value is 60 seconds. + sDefaults.putLong(KEY_5G_PING_PONG_TIME_LONG, 60000); + return defaults; + } + } - /** - * Controls hysteresis time in milliseconds for which will be waited before switching from - * 5G opportunistic network to primary network. - * - * @hide - */ - public static final String KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG = - "opportunistic_network_5g_data_switch_exit_hysteresis_time_long"; /** * Controls whether 4G opportunistic networks should be scanned for possible data switch. * @@ -5305,6 +5465,50 @@ public class CarrierConfigManager { public static final String KEY_APN_PRIORITY_STRING_ARRAY = "apn_priority_string_array"; /** + * Network capability priority for determine the satisfy order in telephony. This is used when + * the network only allows single PDN. The priority is from the lowest 0 to the highest 100. + * The long-lived network request usually has the lowest priority. This allows other short-lived + * requests like MMS requests to be established. Emergency request always has the highest + * priority. + * + * // TODO: Remove KEY_APN_PRIORITY_STRING_ARRAY + * @hide + */ + public static final String KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY = + "telephony_network_capability_priorities_string_array"; + + /** + * Defines the rules for data retry. + * + * The syntax of the retry rule: + * 1. Retry based on {@link NetworkCapabilities} + * "capabilities=[netCaps1|netCaps2|...], [retry_interval=x], [backoff=[true|false]], + * [maximum_retries=y]" + * + * 2. Retry based on {@link DataFailCause} + * "fail_causes=[cause1|cause2|cause3|...], [retry_interval=x], [backoff=[true|false]], + * [maximum_retries=y]" + * + * 3. Retry based on {@link NetworkCapabilities} and {@link DataFailCause} + * "capabilities=[netCaps1|netCaps2|...], fail_causes=[cause1|cause2|cause3|...], + * [retry_interval=x], [backoff=[true|false]], [maximum_retries=y]" + * + * For example, + * "capabilities=eims, retry_interval=1000, maximum_retries=20" means if the attached + * network request is emergency, then retry data network setup every 1 second for up to 20 + * times. + * + * "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|2254 + * , maximum_retries=0" means for those fail causes, never retry with timers. Note that + * when environment changes, retry can still happens. + * + * // TODO: remove KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS + * @hide + */ + public static final String KEY_TELEPHONY_DATA_RETRY_RULES_STRING_ARRAY = + "telephony_data_retry_rules_string_array"; + + /** * The patterns of missed incoming call sms. This is the regular expression used for * matching the missed incoming call's date, time, and caller id. The pattern should match * fields for at least month, day, hour, and minute. Year is optional although it is encouraged. @@ -5994,9 +6198,9 @@ public class CarrierConfigManager { /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118); /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */ - sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45); + sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 5); /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */ - sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10); + sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 1); /* Default value is 1024 kbps */ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT, 1024); /* Default value is 10 seconds */ @@ -6005,6 +6209,7 @@ public class CarrierConfigManager { sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG, 10000); /* Default value is 3 seconds. */ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 3000); + sDefaults.putAll(OpportunisticNetwork.getDefaults()); sDefaults.putBoolean(KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL, true); sDefaults.putBoolean(KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL, true); /* Default value is 60 seconds. */ @@ -6013,24 +6218,6 @@ public class CarrierConfigManager { sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000); /* Default value is 60 seconds. */ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000); - /* Default value is -111 dBm. */ - sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRP_INT, -111); - /* Default value is -18.5 dB. */ - sDefaults.putDouble(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE, -18.5); - /* Default value is -120 dBm. */ - sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT, -120); - /* Default value is -18.5 dB. */ - sDefaults.putDouble(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_DOUBLE, -18.5); - /* Default value is 10 seconds. */ - sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG, 10000); - /* Default value is 60 seconds. */ - sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_MAX_BACKOFF_TIME_LONG, 60000); - /* Default value is 60 seconds. */ - sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG, 60000); - /* Default value is 2 seconds. */ - sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG, 2000); - /* Default value is 2 seconds. */ - sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000); sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true); sDefaults.putLong(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000L); sDefaults.putLong( @@ -6089,6 +6276,20 @@ public class CarrierConfigManager { "enterprise:0", "default:1", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2", "ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3" }); + sDefaults.putStringArray( + KEY_TELEPHONY_NETWORK_CAPABILITY_PRIORITIES_STRING_ARRAY, new String[] { + "eims:90", "supl:80", "mms:70", "xcap:70", "cbs:50", "mcx:50", "fota:50", + "ims:40", "dun:30", "enterprise:20", "internet:20" + }); + sDefaults.putStringArray( + KEY_TELEPHONY_DATA_RETRY_RULES_STRING_ARRAY, new String[] { + "capabilities=eims, retry_interval=1000, maximum_retries=20", + "fail_causes=8|27|28|29|30|32|33|35|50|51|111|-5|-6|65537|65538|-3|2253|" + + "2254, maximum_retries=0", // No retry for those causes + "capabilities=internet|enterprise|dun|ims|fota, retry_interval=2000, " + + "backoff=true, maximum_retries=13", + "capabilities=mms|supl|cbs, retry_interval=2000" + }); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 947dc0107dba..5e902613a654 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -125,13 +125,13 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P /** * Construct a cell signal strength * - * @param rssi in dBm [-113,-51], UNKNOWN - * @param rsrp in dBm [-140,-43], UNKNOWN - * @param rsrq in dB [-34, 3], UNKNOWN - * @param rssnr in dB [-20, +30], UNKNOWN - * @param cqiTableIndex [1, 6], UNKNOWN - * @param cqi [0, 15], UNKNOWN - * @param timingAdvance [0, 1282], UNKNOWN + * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE} + * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE} + * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE} + * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE} + * @param cqiTableIndex [1, 6], {@link CellInfo#UNAVAILABLE} + * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE} + * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE} * */ /** @hide */ @@ -151,12 +151,12 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P /** * Construct a cell signal strength * - * @param rssi in dBm [-113,-51], UNKNOWN - * @param rsrp in dBm [-140,-43], UNKNOWN - * @param rsrq in dB [-34, 3], UNKNOWN - * @param rssnr in dB [-20, +30], UNKNOWN - * @param cqi [0, 15], UNKNOWN - * @param timingAdvance [0, 1282], UNKNOWN + * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE} + * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE} + * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE} + * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE} + * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE} + * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE} * */ /** @hide */ @@ -403,10 +403,11 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** - * Get reference signal signal-to-noise ratio + * Get reference signal signal-to-noise ratio in dB + * Range: -20 dB to +30 dB. * * @return the RSSNR if available or - * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable. + * {@link android.telephony.CellInfo#UNAVAILABLE} if unavailable. */ public int getRssnr() { return mRssnr; @@ -414,8 +415,10 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P /** * Get reference signal received power in dBm + * Range: -140 dBm to -43 dBm. * - * @return the RSRP of the measured cell. + * @return the RSRP of the measured cell or {@link CellInfo#UNAVAILABLE} if + * unavailable. */ public int getRsrp() { return mRsrp; diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index d6dce25d931f..8c02ffe12363 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -2158,8 +2158,7 @@ public class ApnSetting implements Parcelable { | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX | TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE)) == 0 || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) { - throw new IllegalArgumentException("mApName=" + mApnName + ", mEntryName=" - + mEntryName + ", mApnTypeBitmask=" + mApnTypeBitmask); + return null; } return new ApnSetting(this); } diff --git a/tests/TrustTests/OWNERS b/tests/TrustTests/OWNERS new file mode 100644 index 000000000000..e2c6ce15b51e --- /dev/null +++ b/tests/TrustTests/OWNERS @@ -0,0 +1 @@ +include /core/java/android/service/trust/OWNERS diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java index 978bf3ed2e92..7b1f7a599519 100644 --- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -174,7 +174,7 @@ public class TelephonySubscriptionTrackerTest { private IntentFilter getIntentFilter() { final ArgumentCaptor<IntentFilter> captor = ArgumentCaptor.forClass(IntentFilter.class); - verify(mContext).registerReceiver(any(), captor.capture(), any(), any()); + verify(mContext).registerReceiver(any(), captor.capture(), any(), any(), anyInt()); return captor.getValue(); } @@ -258,7 +258,8 @@ public class TelephonySubscriptionTrackerTest { eq(mTelephonySubscriptionTracker), any(IntentFilter.class), any(), - eq(mHandler)); + eq(mHandler), + eq(Context.RECEIVER_NOT_EXPORTED)); final IntentFilter filter = getIntentFilter(); assertEquals(2, filter.countActions()); assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED)); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index e547400fff73..4cfa93b4ecf9 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -307,7 +307,10 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection ncCaptor.capture(), lpCaptor.capture(), any(), - argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE), + // Subtype integer/name and extras do not have getters; cannot be tested. + argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE + && nac.getLegacyTypeName().equals( + VcnGatewayConnection.NETWORK_INFO_NETWORK_TYPE_STRING)), any(), any(), any()); diff --git a/tools/bit/command.cpp b/tools/bit/command.cpp index f95ea117a96e..6c68e0b0ff6b 100644 --- a/tools/bit/command.cpp +++ b/tools/bit/command.cpp @@ -192,10 +192,11 @@ exec_with_path_search(const char* prog, char const* const* argv, char const* con if (strchr(prog, '/') != NULL) { return execve(prog, (char*const*)argv, (char*const*)envp); } else { - char* pathEnv = strdup(getenv("PATH")); - if (pathEnv == NULL) { + const char* pathEnvRaw = getenv("PATH"); + if (pathEnvRaw == NULL) { return 1; } + char* pathEnv = strdup(pathEnvRaw); char* dir = pathEnv; while (dir) { char* next = strchr(dir, ':'); |