diff options
587 files changed, 24422 insertions, 6044 deletions
diff --git a/Android.bp b/Android.bp index 23168232b7a3..6a0bdc3f7fde 100644 --- a/Android.bp +++ b/Android.bp @@ -483,6 +483,7 @@ java_library { "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", "android.security.apc-java", + "android.security.authorization-java", "android.system.keystore2-java", "android.system.suspend.control.internal-java", "devicepolicyprotosnano", @@ -573,6 +574,7 @@ java_library { ], sdk_version: "core_platform", static_libs: [ + "bouncycastle-repackaged-unbundled", "framework-internal-utils", // If MimeMap ever becomes its own APEX, then this dependency would need to be removed // in favor of an API stubs dependency in java_library "framework" below. @@ -1,3 +1,4 @@ third_party { - license_type: NOTICE + # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h + license_type: RESTRICTED } diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS new file mode 100644 index 000000000000..fbc611a39d7d --- /dev/null +++ b/MULTIUSER_OWNERS @@ -0,0 +1,4 @@ +# OWNERS of Multiuser related files +bookatz@google.com +omakoto@google.com +yamasani@google.com diff --git a/StubLibraries.bp b/StubLibraries.bp index ed8781e2ff86..6afed7a78a08 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -113,13 +113,27 @@ droidstubs { last_released: { api_file: ":android-non-updatable.api.public.latest", removed_api_file: ":android-non-updatable-removed.api.public.latest", - baseline_file: ":public-api-incompatibilities-with-last-released", + baseline_file: ":android-incompatibilities.api.public.latest", }, api_lint: { enabled: true, new_since: ":android-non-updatable.api.public.latest", }, }, + dists: [ + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/public/api", + dest: "android-non-updatable.txt", + tag: ".api.txt", + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/public/api", + dest: "android-non-updatable-removed.txt", + tag: ".removed-api.txt", + }, + ], } priv_apps = @@ -151,7 +165,7 @@ droidstubs { last_released: { api_file: ":android-non-updatable.api.system.latest", removed_api_file: ":android-non-updatable-removed.api.system.latest", - baseline_file: ":system-api-incompatibilities-with-last-released" + baseline_file: ":android-incompatibilities.api.system.latest" }, api_lint: { enabled: true, @@ -159,6 +173,20 @@ droidstubs { baseline_file: "core/api/system-lint-baseline.txt", }, }, + dists: [ + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system/api", + dest: "android-non-updatable.txt", + tag: ".api.txt", + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system/api", + dest: "android-non-updatable-removed.txt", + tag: ".removed-api.txt", + }, + ], } droidstubs { @@ -175,11 +203,32 @@ droidstubs { baseline_file: "core/api/test-lint-baseline.txt", }, }, - dist: { - targets: ["sdk", "win_sdk"], - dir: "apistubs/android/test/api", - dest: "android.txt", - }, + dists: [ + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/test/api", + dest: "android.txt", + tag: ".api.txt", + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/test/api", + dest: "removed.txt", + tag: ".removed-api.txt", + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/test/api", + dest: "android-non-updatable.txt", + tag: ".api.txt", + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/test/api", + dest: "android-non-updatable-removed.txt", + tag: ".removed-api.txt", + }, + ], } droidstubs { @@ -200,6 +249,20 @@ droidstubs { new_since: ":android-non-updatable.api.module-lib.latest", }, }, + dists: [ + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/module-lib/api", + dest: "android-non-updatable.txt", + tag: ".api.txt", + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/module-lib/api", + dest: "android-non-updatable-removed.txt", + tag: ".removed-api.txt", + }, + ], } ///////////////////////////////////////////////////////////////////// diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS index a060ad9a5a7e..7e7feafdc5a5 100644 --- a/apct-tests/perftests/OWNERS +++ b/apct-tests/perftests/OWNERS @@ -1,2 +1,11 @@ -timmurray@google.com +balejs@google.com +carmenjackson@google.com +cfijalkovich@google.com +dualli@google.com +edgararriaga@google.com +jpakaravoor@google.com +kevinjeon@google.com philipcuadra@google.com +shombert@google.com +timmurray@google.com +wessam@google.com diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java index 23f025b0a759..5a04ba303b66 100644 --- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java +++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java @@ -27,7 +27,7 @@ import android.support.test.uiautomator.UiDevice; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.utils.blob.DummyBlobData; +import com.android.utils.blob.FakeBlobData; import org.junit.After; import org.junit.Before; @@ -96,7 +96,7 @@ public class BlobStorePerfTests { mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER); try { final List<Long> durations = new ArrayList<>(); - final DummyBlobData blobData = prepareDataBlob(fileSizeInMb); + final FakeBlobData blobData = prepareDataBlob(fileSizeInMb); final TraceMarkParser parser = new TraceMarkParser( line -> line.name.startsWith(ATRACE_COMPUTE_DIGEST_PREFIX)); while (mState.keepRunning(durations)) { @@ -120,15 +120,15 @@ public class BlobStorePerfTests { }); } - private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception { - final DummyBlobData blobData = new DummyBlobData.Builder(mContext) + private FakeBlobData prepareDataBlob(int fileSizeInMb) throws Exception { + final FakeBlobData blobData = new FakeBlobData.Builder(mContext) .setFileSize(fileSizeInMb * 1024 * 1024 /* bytes */) .build(); blobData.prepare(); return blobData; } - private void commitBlob(DummyBlobData blobData) throws Exception { + private void commitBlob(FakeBlobData blobData) throws Exception { final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle()); try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) { blobData.writeToSession(session); diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS new file mode 100644 index 000000000000..a1719c9c31d1 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/OWNERS @@ -0,0 +1 @@ +per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file diff --git a/apct-tests/perftests/multiuser/OWNERS b/apct-tests/perftests/multiuser/OWNERS new file mode 100644 index 000000000000..1a206cb27c3b --- /dev/null +++ b/apct-tests/perftests/multiuser/OWNERS @@ -0,0 +1 @@ +include /MULTIUSER_OWNERS
\ No newline at end of file diff --git a/apex/Android.bp b/apex/Android.bp index 0a535a8fe9b9..1876110c9355 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -12,9 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_visibility: [":__subpackages__"], +} + mainline_stubs_args = "--error UnhiddenSystemApi " + "--hide BroadcastBehavior " + + "--hide CallbackInterface " + "--hide DeprecationMismatch " + "--hide HiddenSuperclass " + "--hide HiddenTypedefConstant " + @@ -96,6 +101,10 @@ java_defaults { sdk_version: "module_current", }, + // installable implies we'll create a non-apex (platform) variant, which + // we shouldn't ordinarily need (and it can create issues), so disable that. + installable: false, + // Configure framework module specific metalava options. droiddoc_options: [mainline_stubs_args], @@ -135,6 +144,13 @@ java_defaults { enabled: true, sdk_version: "module_current", }, + defaults_visibility: [ + ":__subpackages__", + "//frameworks/base/libs/hwui", + "//frameworks/base/wifi", + "//packages/modules:__subpackages__", + "//packages/providers/MediaProvider:__subpackages__", + ], } // Defaults for mainline module system server provided java_sdk_library instances. @@ -146,6 +162,10 @@ java_defaults { enabled: true, sdk_version: "module_current", }, + defaults_visibility: [ + ":__subpackages__", + "//packages/modules:__subpackages__", + ], } stubs_defaults { diff --git a/apex/OWNERS b/apex/OWNERS index 97600135a103..bde2bec0816b 100644 --- a/apex/OWNERS +++ b/apex/OWNERS @@ -1,7 +1,8 @@ -# Shared module build rule owners -per-file *.bp=hansson@google.com -per-file *.bp=jiyong@google.com +# Mainline modularization team -# This file, and all other OWNERS files -per-file OWNERS=dariofreni@google.com -per-file OWNERS=hansson@google.com +andreionea@google.com +dariofreni@google.com +hansson@google.com +mathewi@google.com +pedroql@google.com +satayev@google.com diff --git a/apex/blobstore/framework/Android.bp b/apex/blobstore/framework/Android.bp index 24693511117c..349955368b17 100644 --- a/apex/blobstore/framework/Android.bp +++ b/apex/blobstore/framework/Android.bp @@ -19,6 +19,7 @@ filegroup { "java/**/*.aidl" ], path: "java", + visibility: ["//frameworks/base"], } java_library { diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java index 39f7526560a9..38500aff34ea 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java @@ -89,8 +89,8 @@ import java.util.function.Consumer; * <p> Before committing the session, apps can indicate which apps are allowed to access the * contributed data using one or more of the following access modes: * <ul> - * <li> {@link Session#allowPackageAccess(String, byte[])} which will allow whitelisting - * specific packages to access the blobs. + * <li> {@link Session#allowPackageAccess(String, byte[])} which will allow specific packages + * to access the blobs. * <li> {@link Session#allowSameSignatureAccess()} which will allow only apps which are signed * with the same certificate as the app which contributed the blob to access it. * <li> {@link Session#allowPublicAccess()} which will allow any app on the device to access diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java index 656749d1a8c4..bfc582623439 100644 --- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java +++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java @@ -36,7 +36,7 @@ public final class XmlTags { // For BlobAccessMode public static final String TAG_ACCESS_MODE = "am"; public static final String ATTR_TYPE = "t"; - public static final String TAG_WHITELISTED_PACKAGE = "wl"; + public static final String TAG_ALLOWED_PACKAGE = "wl"; public static final String ATTR_CERTIFICATE = "ct"; // For BlobHandle diff --git a/apex/blobstore/service/Android.bp b/apex/blobstore/service/Android.bp index 22b0cbe91e23..f6cbac1628da 100644 --- a/apex/blobstore/service/Android.bp +++ b/apex/blobstore/service/Android.bp @@ -25,4 +25,9 @@ java_library { "services.core", "services.usage", ], + visibility: [ + // These are required until blobstore is properly unbundled. + "//frameworks/base/services", + "//frameworks/base/services/tests/mockingservicestests", + ], } diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java index ba0fab6b4bc5..4a527adf9abc 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java @@ -18,7 +18,7 @@ package com.android.server.blob; import static android.app.blob.XmlTags.ATTR_CERTIFICATE; import static android.app.blob.XmlTags.ATTR_PACKAGE; import static android.app.blob.XmlTags.ATTR_TYPE; -import static android.app.blob.XmlTags.TAG_WHITELISTED_PACKAGE; +import static android.app.blob.XmlTags.TAG_ALLOWED_PACKAGE; import android.annotation.IntDef; import android.annotation.NonNull; @@ -52,21 +52,21 @@ class BlobAccessMode { ACCESS_TYPE_PRIVATE, ACCESS_TYPE_PUBLIC, ACCESS_TYPE_SAME_SIGNATURE, - ACCESS_TYPE_WHITELIST, + ACCESS_TYPE_ALLOWLIST, }) @interface AccessType {} public static final int ACCESS_TYPE_PRIVATE = 1 << 0; public static final int ACCESS_TYPE_PUBLIC = 1 << 1; public static final int ACCESS_TYPE_SAME_SIGNATURE = 1 << 2; - public static final int ACCESS_TYPE_WHITELIST = 1 << 3; + public static final int ACCESS_TYPE_ALLOWLIST = 1 << 3; private int mAccessType = ACCESS_TYPE_PRIVATE; - private final ArraySet<PackageIdentifier> mWhitelistedPackages = new ArraySet<>(); + private final ArraySet<PackageIdentifier> mAllowedPackages = new ArraySet<>(); void allow(BlobAccessMode other) { - if ((other.mAccessType & ACCESS_TYPE_WHITELIST) != 0) { - mWhitelistedPackages.addAll(other.mWhitelistedPackages); + if ((other.mAccessType & ACCESS_TYPE_ALLOWLIST) != 0) { + mAllowedPackages.addAll(other.mAllowedPackages); } mAccessType |= other.mAccessType; } @@ -80,8 +80,8 @@ class BlobAccessMode { } void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate) { - mAccessType |= ACCESS_TYPE_WHITELIST; - mWhitelistedPackages.add(PackageIdentifier.create(packageName, certificate)); + mAccessType |= ACCESS_TYPE_ALLOWLIST; + mAllowedPackages.add(PackageIdentifier.create(packageName, certificate)); } boolean isPublicAccessAllowed() { @@ -93,10 +93,10 @@ class BlobAccessMode { } boolean isPackageAccessAllowed(@NonNull String packageName, @NonNull byte[] certificate) { - if ((mAccessType & ACCESS_TYPE_WHITELIST) == 0) { + if ((mAccessType & ACCESS_TYPE_ALLOWLIST) == 0) { return false; } - return mWhitelistedPackages.contains(PackageIdentifier.create(packageName, certificate)); + return mAllowedPackages.contains(PackageIdentifier.create(packageName, certificate)); } boolean isAccessAllowedForCaller(Context context, @@ -113,9 +113,9 @@ class BlobAccessMode { } } - if ((mAccessType & ACCESS_TYPE_WHITELIST) != 0) { - for (int i = 0; i < mWhitelistedPackages.size(); ++i) { - final PackageIdentifier packageIdentifier = mWhitelistedPackages.valueAt(i); + if ((mAccessType & ACCESS_TYPE_ALLOWLIST) != 0) { + for (int i = 0; i < mAllowedPackages.size(); ++i) { + final PackageIdentifier packageIdentifier = mAllowedPackages.valueAt(i); if (packageIdentifier.packageName.equals(callingPackage) && pm.hasSigningCertificate(callingPackage, packageIdentifier.certificate, PackageManager.CERT_INPUT_SHA256)) { @@ -131,20 +131,20 @@ class BlobAccessMode { return mAccessType; } - int getNumWhitelistedPackages() { - return mWhitelistedPackages.size(); + int getAllowedPackagesCount() { + return mAllowedPackages.size(); } void dump(IndentingPrintWriter fout) { fout.println("accessType: " + DebugUtils.flagsToString( BlobAccessMode.class, "ACCESS_TYPE_", mAccessType)); - fout.print("Whitelisted pkgs:"); - if (mWhitelistedPackages.isEmpty()) { + fout.print("Explicitly allowed pkgs:"); + if (mAllowedPackages.isEmpty()) { fout.println(" (Empty)"); } else { fout.increaseIndent(); - for (int i = 0, count = mWhitelistedPackages.size(); i < count; ++i) { - fout.println(mWhitelistedPackages.valueAt(i).toString()); + for (int i = 0, count = mAllowedPackages.size(); i < count; ++i) { + fout.println(mAllowedPackages.valueAt(i).toString()); } fout.decreaseIndent(); } @@ -152,12 +152,12 @@ class BlobAccessMode { void writeToXml(@NonNull XmlSerializer out) throws IOException { XmlUtils.writeIntAttribute(out, ATTR_TYPE, mAccessType); - for (int i = 0, count = mWhitelistedPackages.size(); i < count; ++i) { - out.startTag(null, TAG_WHITELISTED_PACKAGE); - final PackageIdentifier packageIdentifier = mWhitelistedPackages.valueAt(i); + for (int i = 0, count = mAllowedPackages.size(); i < count; ++i) { + out.startTag(null, TAG_ALLOWED_PACKAGE); + final PackageIdentifier packageIdentifier = mAllowedPackages.valueAt(i); XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageIdentifier.packageName); XmlUtils.writeByteArrayAttribute(out, ATTR_CERTIFICATE, packageIdentifier.certificate); - out.endTag(null, TAG_WHITELISTED_PACKAGE); + out.endTag(null, TAG_ALLOWED_PACKAGE); } } @@ -171,7 +171,7 @@ class BlobAccessMode { final int depth = in.getDepth(); while (XmlUtils.nextElementWithin(in, depth)) { - if (TAG_WHITELISTED_PACKAGE.equals(in.getName())) { + if (TAG_ALLOWED_PACKAGE.equals(in.getName())) { final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE); final byte[] certificate = XmlUtils.readByteArrayAttribute(in, ATTR_CERTIFICATE); blobAccessMode.allowPackageAccess(packageName, certificate); diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java index 0b760a621d22..a9c5c4c9f34d 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java @@ -478,7 +478,7 @@ class BlobMetadata { proto.write(BlobStatsEventProto.BlobCommitterProto.ACCESS_MODE, committer.blobAccessMode.getAccessType()); proto.write(BlobStatsEventProto.BlobCommitterProto.NUM_WHITELISTED_PACKAGE, - committer.blobAccessMode.getNumWhitelistedPackages()); + committer.blobAccessMode.getAllowedPackagesCount()); proto.end(token); } final byte[] committersBytes = proto.getBytes(); diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java index 2f83be1e0370..fe688828997e 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java @@ -332,10 +332,10 @@ class BlobStoreSession extends IBlobStoreSession.Stub { throw new IllegalStateException("Not allowed to change access type in state: " + stateToString(mState)); } - if (mBlobAccessMode.getNumWhitelistedPackages() >= getMaxPermittedPackages()) { + if (mBlobAccessMode.getAllowedPackagesCount() >= getMaxPermittedPackages()) { throw new ParcelableException(new LimitExceededException( "Too many packages permitted to access the blob: " - + mBlobAccessMode.getNumWhitelistedPackages())); + + mBlobAccessMode.getAllowedPackagesCount())); } mBlobAccessMode.allowPackageAccess(packageName, certificate); } diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp index ec074262fb13..23f5614f018c 100644 --- a/apex/jobscheduler/framework/Android.bp +++ b/apex/jobscheduler/framework/Android.bp @@ -8,6 +8,7 @@ filegroup { "java/android/os/IDeviceIdleController.aidl", ], path: "java", + visibility: ["//frameworks/base"], } java_library { diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp index 69a9fd844729..6ddba690bd6f 100644 --- a/apex/jobscheduler/service/Android.bp +++ b/apex/jobscheduler/service/Android.bp @@ -13,4 +13,12 @@ java_library { "framework", "services.core", ], + visibility: [ + "//frameworks/base/apex/jobscheduler:__subpackages__", + // These are required until jobscheduler is properly unbundled. + "//frameworks/base/services", + "//frameworks/base/services/tests/mockingservicestests", + "//frameworks/base/services/tests/servicestests", + "//frameworks/base/tests/JobSchedulerPerfTests", + ], } diff --git a/apex/permission/tests/Android.bp b/apex/media/Android.bp index 271e328c1139..5f1bd374df00 100644 --- a/apex/permission/tests/Android.bp +++ b/apex/media/Android.bp @@ -11,27 +11,10 @@ // 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. - -android_test { - name: "PermissionApexTests", - sdk_version: "test_current", - srcs: [ - "java/**/*.kt", - ], - static_libs: [ - "service-permission.impl", - "androidx.test.rules", - "androidx.test.ext.junit", - "androidx.test.ext.truth", - "mockito-target-extended-minus-junit4", - ], - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - compile_multilib: "both", - test_suites: [ - "general-tests", - "mts", +package { + default_visibility: [ + ":__subpackages__", + "//frameworks/av/apex", + "//frameworks/av/apex/testing", ], } diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index ce4b030467a7..0ff6d4428c27 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -49,6 +49,10 @@ java_library { "test_com.android.media", ], min_sdk_version: "29", + visibility: [ + "//frameworks/av/apex:__subpackages__", + "//frameworks/base", // For framework-all + ], } filegroup { @@ -58,6 +62,7 @@ filegroup { ":mediasession2-java-srcs", ":mediasession2-aidl-srcs", ], + visibility: ["//frameworks/base"], } filegroup { diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp deleted file mode 100644 index e30df05b2340..000000000000 --- a/apex/permission/Android.bp +++ /dev/null @@ -1,43 +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. - -apex { - name: "com.android.permission", - defaults: ["com.android.permission-defaults"], - manifest: "apex_manifest.json", -} - -apex_defaults { - name: "com.android.permission-defaults", - updatable: true, - min_sdk_version: "30", - key: "com.android.permission.key", - certificate: ":com.android.permission.certificate", - java_libs: [ - "framework-permission", - "service-permission", - ], - apps: ["PermissionController"], -} - -apex_key { - name: "com.android.permission.key", - public_key: "com.android.permission.avbpubkey", - private_key: "com.android.permission.pem", -} - -android_app_certificate { - name: "com.android.permission.certificate", - certificate: "com.android.permission", -} diff --git a/apex/permission/OWNERS b/apex/permission/OWNERS deleted file mode 100644 index 957e10a582a0..000000000000 --- a/apex/permission/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -svetoslavganov@google.com -moltmann@google.com -eugenesusla@google.com -zhanghai@google.com -evanseverson@google.com -ntmyren@google.com diff --git a/apex/permission/TEST_MAPPING b/apex/permission/TEST_MAPPING deleted file mode 100644 index 6e67ce92a27e..000000000000 --- a/apex/permission/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit" : [ - { - "name" : "PermissionApexTests" - } - ] -} diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json deleted file mode 100644 index 7960598affa3..000000000000 --- a/apex/permission/apex_manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.permission", - "version": 300000000 -} diff --git a/apex/permission/com.android.permission.avbpubkey b/apex/permission/com.android.permission.avbpubkey Binary files differdeleted file mode 100644 index 9eaf85259637..000000000000 --- a/apex/permission/com.android.permission.avbpubkey +++ /dev/null diff --git a/apex/permission/com.android.permission.pem b/apex/permission/com.android.permission.pem deleted file mode 100644 index 3d584be5440d..000000000000 --- a/apex/permission/com.android.permission.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEA6snt4eqoz85xiL9Sf6w1S1b9FgSHK05zYTh2JYPvQKQ3yeZp -E6avJ6FN6XcbmkDzSd658BvUGDBSPhOlzuUO4BsoKBuLMxP6TxIQXFKidzDqY0vQ -4qkS++bdIhUjwBP3OSZ3Czu0BiihK8GC75Abr//EyCyObGIGGfHEGANiOgrpP4X5 -+OmLzQLCjk4iE1kg+U6cRSRI/XLaoWC0TvIIuzxznrQ6r5GmzgTOwyBWyIB+bj73 -bmsweHTU+w9Y7kGOx4hO3XCLIhoBWEw0EbuW9nZmQ4sZls5Jo/CbyJlCclF11yVo -SCf2LG/T+9pah5NOmDQ1kPbU+0iKZIV4YFHGTIhyGDE/aPOuUT05ziCGDifgHr0u -SG1x/RLqsVh/POvNxnvP9cQFMQ08BvbEJaTTgB785iwKsvdqCfmng/SAyxSetmzP -StXVB3fh1OoZ8vunRbQYxnmUxycVqaA96zmBx2wLvbvzKo7pZFDE6nbhnT5+MRAM -z/VIK89W26uB4gj8sBFslqZjT0jPqsAZuvDm7swOtMwIcEolyGJuFLqlhN7UwMz2 -9y8+IpYixR+HvD1TZI9NtmuCmv3kPrWgoMZg6yvaBayTIr8RdYzi6FO/C1lLiraz -48dH3sXWRa8cgw6VcSUwYrEBIc3sotdsupO1iOjcFybIwaee0YTZJfjvbqkCAwEA -AQKCAgEArRnfdpaJi1xLPGTCMDsIt9kUku0XswgN7PmxsYsKFAB+2S40/jYAIRm9 -1YjpItsMA8RgFfSOdJ77o6TctCMQyo17F8bm4+uwuic5RLfv7Cx2QmsdQF8jDfFx -y7UGPJD7znjbf76uxXOjEB2FqZX3s9TAgkzHXIUQtoQW7RVhkCWHPjxKxgd5+NY2 -FrDoUpd9xhD9CcTsw1+wbRZdGW88nL6/B50dP2AFORM2VYo8MWr6y9FEn3YLsGOC -uu7fxBk1aUrHyl81VRkTMMROB1zkuiUk1FtzrEm+5U15rXXBFYOVe9+qeLhtuOlh -wueDoz0pzvF/JLe24uTik6YL0Ae6SD0pFXQ2KDrdH3cUHLok3r76/yGzaDNTFjS2 -2WbQ8dEJV8veNHk8gjGpFTJIsBUlcZpmUCDHlfvVMb3+2ahQ+28piQUt5t3zqJdZ -NDqsOHzY6BRPc+Wm85Xii/lWiQceZSee/b1Enu+nchsyXhSenBfC6bIGZReyMI0K -KKKuVhyR6OSOiR5ZdZ/NyXGqsWy05fn/h0X9hnpETsNaNYNKWvpHLfKll+STJpf7 -AZquJPIclQyiq5NONx6kfPztoCLkKV/zOgIj3Sx5oSZq+5gpO91nXWVwkTbqK1d1 -004q2Mah6UQyAk1XGQc2pHx7ouVcWawjU30vZ4C015Hv2lm/gVkCggEBAPltATYS -OqOSL1YAtIHPiHxMjNAgUdglq8JiJFXVfkocGU9eNub3Ed3sSWu6GB9Myu/sSKje -bJ5DZqxJnvB2Fqmu9I9OunLGFSD0aXs4prwsQ1Rm5FcbImtrxcciASdkoo8Pj0z4 -vk2r2NZD3VtER5Uh+YjSDkxcS9gBStXUpCL6gj69UpOxMmWqZVjyHatVB4lEvYJl -N82uT7N7QVNL1DzcZ9z4C4r7ks1Pm7ka12s5m/oaAlAMdVeofiPJe1xA9zRToSr4 -tIbMkOeXFLVRLuji/7XsOgal5Rl59p+OwLshX5cswPVOMrH6zt+hbsJ5q8M5dqnX -VAOBK7KNQ/EKZwcCggEBAPD6KVvyCim46n5EbcEqCkO7gevwZkw/9vLwmM5YsxTh -z9FQkPO0iB7mwbX8w04I91Pre4NdfcgMG0pP1b13Sb4KHBchqW1a+TCs3kSGC6gn -1SxmXHnA9jRxAkrWlGkoAQEz+aP61cXiiy2tXpQwJ8xQCKprfoqWZwhkCtEVU6CE -S7v9cscOHIqgNxx4WoceMmq4EoihHAZzHxTcNVbByckMjb2XQJ0iNw3lDP4ddvc+ -a4HzHfHkhzeQ5ZNc8SvWU8z80aSCOKRsSD3aUTZzxhZ4O2tZSW7v7p+FpvVee7bC -g8YCfszTdpVUMlLRLjScimAcovcFLSvtyupinxWg4M8CggEAN9YGEmOsSte7zwXj -YrfhtumwEBtcFwX/2Ej+F1Tuq4p0xAa0RaoDjumJWhtTsRYQy/raHSuFpzwxbNoi -QXQ+CIhI6RfXtz/OlQ0B2/rHoJJMFEXgUfuaDfAXW0eqeHYXyezSyIlamKqipPyW -Pgsf9yue39keKEv1EorfhNTQVaA8rezV4oglXwrxGyNALw2e3UTNI7ai8mFWKDis -XAg6n9E7UwUYGGnO6DUtCBgRJ0jDOQ6/e8n+LrxiWIKPIgzNCiK6jpMUXqTGv4Fb -umdNGAdQ9RnHt5tFmRlrczaSwJFtA7uaCpAR2zPpQbiywchZAiAIB2dTwGEXNiZX -kksg2wKCAQEA6pNad3qhkgPDoK6T+Jkn7M82paoaqtcJWWwEE7oceZNnbWZz9Agl -CY+vuawXonrv5+0vCq2Tp4zBdBFLC2h3jFrjBVFrUFxifpOIukOSTVqZFON/2bWQ -9XOcu6UuSz7522Xw+UNPnZXtzcUacD6AP08ZYGvLfrTyDyTzspyED5k48ALEHCkM -d5WGkFxII4etpF0TDZVnZo/iDbhe49k4yFFEGO6Ho26PESOLBkNAb2V/2bwDxlij -l9+g21Z6HiZA5SamHPH2mXgeyrcen1cL2QupK9J6vVcqfnboE6qp2zp2c+Yx8MlY -gfy4EA44YFaSDQVTTgrn8f9Eq+zc130H2QKCAQEAqOKgv68nIPdDSngNyCVyWego -boFiDaEJoBBg8FrBjTJ6wFLrNAnXmbvfTtgNmNAzF1cUPJZlIIsHgGrMCfpehbXq -WQQIw+E+yFbTGLxseGRfsLrV0CsgnAoOVeod+yIHmqc3livaUbrWhL1V2f6Ue+sE -7YLp/iP43NaMfA4kYk2ep7+ZJoEVkCjHJJaHWgAG3RynPJHkTJlSgu7wLYvGc9uE -ZsEFUM46lX02t7rrtMfasVGrUy1c2xOxFb4v1vG6iEZ7+YWeq5o3AkxUwEGn+mG4 -/3p+k4AaTXJDXgyZ0Sv6CkGuPHenAYG4cswcUUEf/G4Ag77x6LBNMgycJBxUJA== ------END RSA PRIVATE KEY----- diff --git a/apex/permission/com.android.permission.pk8 b/apex/permission/com.android.permission.pk8 Binary files differdeleted file mode 100644 index d51673dbc2fc..000000000000 --- a/apex/permission/com.android.permission.pk8 +++ /dev/null diff --git a/apex/permission/com.android.permission.x509.pem b/apex/permission/com.android.permission.x509.pem deleted file mode 100644 index 4b146c9edd4f..000000000000 --- a/apex/permission/com.android.permission.x509.pem +++ /dev/null @@ -1,35 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGKzCCBBOgAwIBAgIUezo3fQeVZsmLpm/dkpGWJ/G/MN8wDQYJKoZIhvcNAQEL -BQAwgaMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH -DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy -b2lkMR8wHQYDVQQDDBZjb20uYW5kcm9pZC5wZXJtaXNzaW9uMSIwIAYJKoZIhvcN -AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MTAwOTIxMzExOVoYDzQ3NTcw -OTA0MjEzMTE5WjCBozELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx -FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNV -BAsMB0FuZHJvaWQxHzAdBgNVBAMMFmNvbS5hbmRyb2lkLnBlcm1pc3Npb24xIjAg -BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCxefguRJ7E6tBCTEOeU2HJEGs6AQQapLz9hMed0aaJ -Qr7aTQiYJEk+sG4+jPYbjpxa8JDDzJHp+4g7DjfSb+dvT9n84A8lWaI/yRXTZTQN -Hu5m/bgHhi0LbySpiaFyodXBKUAnOhZyGPtYjtBFywFylueub8ryc1Z6UxxU7udH -1mkIr7sE48Qkq5SyjFROE96iFmYA+vS/JXOfS0NBHiMB4GBxx4V7kXpvrTI7hhZG -HiyhKvNh7wyHIhO9nDEw1rwtAH6CsL3YkQEVBeAU98m+0Au+qStLYkKHh2l8zT4W -7sVK1VSqfB+VqOUmeIGdzlBfqMsoXD+FJz6KnIdUHIwjFDjL7Xr+hd+7xve+Q3S+ -U3Blk/U6atY8PM09wNfilG+SvwcKk5IgriDcu3rWKgIFxbUUaxLrDW7pLlu6wt/d -GGtKK+Bc0jF+9Z901Tl33i5xhc5yOktT0btkKs7lSeE6VzP/Nk5g0SuzixmuRoh9 -f5Ge41N2ZCEHNXx3wZeVZwHIIPfYrL7Yql1Xoxbfs4ETFk6ChzVQcvjfDQQuK58J -uNc+TOCoI/qflXwGCwpuHl0ier8V5Z4tpMUl5rWyVR/QGRtLPvs2lLuxczDw1OXq -wEVtCMn9aNnd4y7R9PZ52hi53HAvDjpWefrLYi+Q04J6iGFQ1qAFBClK9DquBvmR -swIDAQABo1MwUTAdBgNVHQ4EFgQULpfus5s5SrqLkoUKyPXA0D1iHPMwHwYDVR0j -BBgwFoAULpfus5s5SrqLkoUKyPXA0D1iHPMwDwYDVR0TAQH/BAUwAwEB/zANBgkq -hkiG9w0BAQsFAAOCAgEAjxQG5EFv8V/9yV2glI53VOmlWMjfEgvUjd39s/XLyPlr -OzPOKSB0NFo8To3l4l+MsManxPK8y0OyfEVKbWVz9onv0ovo5MVokBmV/2G0jmsV -B4e9yjOq+DmqIvY/Qh63Ywb97sTgcFI8620MhQDbh2IpEGv4ZNV0H6rgXmgdSCBw -1EjBoYfFpN5aMgZjeyzZcq+d1IapdWqdhuEJQkMvoYS4WIumNIJlEXPQRoq/F5Ih -nszdbKI/jVyiGFa2oeZ3rja1Y6GCRU8TYEoKx1pjS8uQDOEDTwsG/QnUe9peEj0V -SsCkIidJWTomAmq9Tub9vpBe1zuTpuRAwxwR0qwgSxozV1Mvow1dJ19oFtHX0yD6 -ZjCpRn5PW9kMvSWSlrcrFs1NJf0j1Cvf7bHpkEDqLqpMnnh9jaFQq3nzDY+MWcIR -jDcgQpI+AiE2/qtauZnFEVhbce49nCnk9+5bpTTIZJdzqeaExe5KXHwEtZLaEDh4 -atLY9LuEvPsjmDIMOR6hycD9FvwGXhJOQBjESIWFwigtSb1Yud9n6201jw3MLJ4k -+WhkbmZgWy+xc+Mdm5H3XyB1lvHaHGkxu+QB9KyQuVQKwbUVcbwZIfTFPN6Zr/dS -ZXJqAbBhG/dBgF0LazuLaPVpibi+a3Y+tb9b8eXGkz4F97PWZIEDkELQ+9KOvhc= ------END CERTIFICATE----- diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp deleted file mode 100644 index c0560f61460f..000000000000 --- a/apex/permission/framework/Android.bp +++ /dev/null @@ -1,45 +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. - -filegroup { - name: "framework-permission-sources", - srcs: [ - "java/**/*.java", - "java/**/*.aidl", - ], - path: "java", -} - -java_sdk_library { - name: "framework-permission", - defaults: ["framework-module-defaults"], - - // Restrict access to implementation library. - impl_library_visibility: ["//frameworks/base/apex/permission:__subpackages__"], - - srcs: [ - ":framework-permission-sources", - ], - - apex_available: [ - "com.android.permission", - "test_com.android.permission", - ], - permitted_packages: [ - "android.permission", - "android.app.role", - ], - hostdex: true, - installable: true, -} diff --git a/apex/permission/framework/api/current.txt b/apex/permission/framework/api/current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/module-lib-current.txt b/apex/permission/framework/api/module-lib-current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/module-lib-current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/module-lib-removed.txt b/apex/permission/framework/api/module-lib-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/module-lib-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/system-current.txt b/apex/permission/framework/api/system-current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/system-current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/system-removed.txt b/apex/permission/framework/api/system-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/system-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp deleted file mode 100644 index 6e04edfe02f1..000000000000 --- a/apex/permission/service/Android.bp +++ /dev/null @@ -1,44 +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. - -filegroup { - name: "service-permission-sources", - srcs: [ - "java/**/*.java", - ], - path: "java", -} - -java_sdk_library { - name: "service-permission", - defaults: ["framework-system-server-module-defaults"], - impl_library_visibility: [ - "//frameworks/base/apex/permission/tests", - "//frameworks/base/services/tests/mockingservicestests", - "//frameworks/base/services/tests/servicestests", - ], - srcs: [ - ":service-permission-sources", - ], - libs: [ - "framework-permission", - ], - apex_available: [ - "com.android.permission", - "test_com.android.permission", - ], - installable: true, - // We don't have last-api tracking files for the public part of this jar's API. - unsafe_ignore_missing_latest_api: true, -} diff --git a/apex/permission/service/api/current.txt b/apex/permission/service/api/current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/service/api/current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/service/api/system-server-current.txt b/apex/permission/service/api/system-server-current.txt deleted file mode 100644 index c76cc3275737..000000000000 --- a/apex/permission/service/api/system-server-current.txt +++ /dev/null @@ -1,46 +0,0 @@ -// Signature format: 2.0 -package com.android.permission.persistence { - - public interface RuntimePermissionsPersistence { - method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance(); - method public void deleteForUser(@NonNull android.os.UserHandle); - method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle); - method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle); - } - - public final class RuntimePermissionsState { - ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>); - method @Nullable public String getFingerprint(); - method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getPackagePermissions(); - method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getSharedUserPermissions(); - method public int getVersion(); - field public static final int NO_VERSION = -1; // 0xffffffff - } - - public static final class RuntimePermissionsState.PermissionState { - ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int); - method public int getFlags(); - method @NonNull public String getName(); - method public boolean isGranted(); - } - -} - -package com.android.role.persistence { - - public interface RolesPersistence { - method @NonNull public static com.android.role.persistence.RolesPersistence createInstance(); - method public void deleteForUser(@NonNull android.os.UserHandle); - method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle); - method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle); - } - - public final class RolesState { - ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>); - method @Nullable public String getPackagesHash(); - method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles(); - method public int getVersion(); - } - -} - diff --git a/apex/permission/service/api/system-server-removed.txt b/apex/permission/service/api/system-server-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/service/api/system-server-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java deleted file mode 100644 index aedba290db1f..000000000000 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java +++ /dev/null @@ -1,74 +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 com.android.permission.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; -import android.os.UserHandle; - -/** - * Persistence for runtime permissions. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -@SystemApi(client = Client.SYSTEM_SERVER) -public interface RuntimePermissionsPersistence { - - /** - * Read the runtime permissions from persistence. - * - * This will perform I/O operations synchronously. - * - * @param user the user to read for - * @return the runtime permissions read - */ - @Nullable - RuntimePermissionsState readForUser(@NonNull UserHandle user); - - /** - * Write the runtime permissions to persistence. - * - * This will perform I/O operations synchronously. - * - * @param runtimePermissions the runtime permissions to write - * @param user the user to write for - */ - void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, - @NonNull UserHandle user); - - /** - * Delete the runtime permissions from persistence. - * - * This will perform I/O operations synchronously. - * - * @param user the user to delete for - */ - void deleteForUser(@NonNull UserHandle user); - - /** - * Create a new instance of {@link RuntimePermissionsPersistence} implementation. - * - * @return the new instance. - */ - @NonNull - static RuntimePermissionsPersistence createInstance() { - return new RuntimePermissionsPersistenceImpl(); - } -} diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java deleted file mode 100644 index e43f59a3377a..000000000000 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java +++ /dev/null @@ -1,265 +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 com.android.permission.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ApexEnvironment; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.AtomicFile; -import android.util.Log; -import android.util.Xml; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Persistence implementation for runtime permissions. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPersistence { - - private static final String LOG_TAG = RuntimePermissionsPersistenceImpl.class.getSimpleName(); - - private static final String APEX_MODULE_NAME = "com.android.permission"; - - private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml"; - - private static final String TAG_PACKAGE = "package"; - private static final String TAG_PERMISSION = "permission"; - private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions"; - private static final String TAG_SHARED_USER = "shared-user"; - - private static final String ATTRIBUTE_FINGERPRINT = "fingerprint"; - private static final String ATTRIBUTE_FLAGS = "flags"; - private static final String ATTRIBUTE_GRANTED = "granted"; - private static final String ATTRIBUTE_NAME = "name"; - private static final String ATTRIBUTE_VERSION = "version"; - - @Nullable - @Override - public RuntimePermissionsState readForUser(@NonNull UserHandle user) { - File file = getFile(user); - try (FileInputStream inputStream = new AtomicFile(file).openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(inputStream, null); - return parseXml(parser); - } catch (FileNotFoundException e) { - Log.i(LOG_TAG, "runtime-permissions.xml not found"); - return null; - } catch (XmlPullParserException | IOException e) { - throw new IllegalStateException("Failed to read runtime-permissions.xml: " + file , e); - } - } - - @NonNull - private static RuntimePermissionsState parseXml(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_RUNTIME_PERMISSIONS)) { - return parseRuntimePermissions(parser); - } - } - throw new IllegalStateException("Missing <" + TAG_RUNTIME_PERMISSIONS - + "> in runtime-permissions.xml"); - } - - @NonNull - private static RuntimePermissionsState parseRuntimePermissions(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - String versionValue = parser.getAttributeValue(null, ATTRIBUTE_VERSION); - int version = versionValue != null ? Integer.parseInt(versionValue) - : RuntimePermissionsState.NO_VERSION; - String fingerprint = parser.getAttributeValue(null, ATTRIBUTE_FINGERPRINT); - - Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions = - new ArrayMap<>(); - Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions = - new ArrayMap<>(); - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - switch (parser.getName()) { - case TAG_PACKAGE: { - String packageName = parser.getAttributeValue(null, ATTRIBUTE_NAME); - List<RuntimePermissionsState.PermissionState> permissions = parsePermissions( - parser); - packagePermissions.put(packageName, permissions); - break; - } - case TAG_SHARED_USER: { - String sharedUserName = parser.getAttributeValue(null, ATTRIBUTE_NAME); - List<RuntimePermissionsState.PermissionState> permissions = parsePermissions( - parser); - sharedUserPermissions.put(sharedUserName, permissions); - break; - } - } - } - - return new RuntimePermissionsState(version, fingerprint, packagePermissions, - sharedUserPermissions); - } - - @NonNull - private static List<RuntimePermissionsState.PermissionState> parsePermissions( - @NonNull XmlPullParser parser) throws IOException, XmlPullParserException { - List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>(); - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_PERMISSION)) { - String name = parser.getAttributeValue(null, ATTRIBUTE_NAME); - boolean granted = Boolean.parseBoolean(parser.getAttributeValue(null, - ATTRIBUTE_GRANTED)); - int flags = Integer.parseInt(parser.getAttributeValue(null, - ATTRIBUTE_FLAGS), 16); - RuntimePermissionsState.PermissionState permission = - new RuntimePermissionsState.PermissionState(name, granted, flags); - permissions.add(permission); - } - } - return permissions; - } - - @Override - public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, - @NonNull UserHandle user) { - File file = getFile(user); - AtomicFile atomicFile = new AtomicFile(file); - FileOutputStream outputStream = null; - try { - outputStream = atomicFile.startWrite(); - - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(outputStream, StandardCharsets.UTF_8.name()); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - serializer.startDocument(null, true); - - serializeRuntimePermissions(serializer, runtimePermissions); - - serializer.endDocument(); - atomicFile.finishWrite(outputStream); - } catch (Exception e) { - Log.wtf(LOG_TAG, "Failed to write runtime-permissions.xml, restoring backup: " + file, - e); - atomicFile.failWrite(outputStream); - } finally { - IoUtils.closeQuietly(outputStream); - } - } - - private static void serializeRuntimePermissions(@NonNull XmlSerializer serializer, - @NonNull RuntimePermissionsState runtimePermissions) throws IOException { - serializer.startTag(null, TAG_RUNTIME_PERMISSIONS); - - int version = runtimePermissions.getVersion(); - serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version)); - String fingerprint = runtimePermissions.getFingerprint(); - if (fingerprint != null) { - serializer.attribute(null, ATTRIBUTE_FINGERPRINT, fingerprint); - } - - for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry - : runtimePermissions.getPackagePermissions().entrySet()) { - String packageName = entry.getKey(); - List<RuntimePermissionsState.PermissionState> permissions = entry.getValue(); - - serializer.startTag(null, TAG_PACKAGE); - serializer.attribute(null, ATTRIBUTE_NAME, packageName); - serializePermissions(serializer, permissions); - serializer.endTag(null, TAG_PACKAGE); - } - - for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry - : runtimePermissions.getSharedUserPermissions().entrySet()) { - String sharedUserName = entry.getKey(); - List<RuntimePermissionsState.PermissionState> permissions = entry.getValue(); - - serializer.startTag(null, TAG_SHARED_USER); - serializer.attribute(null, ATTRIBUTE_NAME, sharedUserName); - serializePermissions(serializer, permissions); - serializer.endTag(null, TAG_SHARED_USER); - } - - serializer.endTag(null, TAG_RUNTIME_PERMISSIONS); - } - - private static void serializePermissions(@NonNull XmlSerializer serializer, - @NonNull List<RuntimePermissionsState.PermissionState> permissions) throws IOException { - int permissionsSize = permissions.size(); - for (int i = 0; i < permissionsSize; i++) { - RuntimePermissionsState.PermissionState permissionState = permissions.get(i); - - serializer.startTag(null, TAG_PERMISSION); - serializer.attribute(null, ATTRIBUTE_NAME, permissionState.getName()); - serializer.attribute(null, ATTRIBUTE_GRANTED, Boolean.toString( - permissionState.isGranted() && (permissionState.getFlags() - & PackageManager.FLAG_PERMISSION_ONE_TIME) == 0)); - serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString( - permissionState.getFlags())); - serializer.endTag(null, TAG_PERMISSION); - } - } - - @Override - public void deleteForUser(@NonNull UserHandle user) { - getFile(user).delete(); - } - - @NonNull - private static File getFile(@NonNull UserHandle user) { - ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME); - File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user); - return new File(dataDirectory, RUNTIME_PERMISSIONS_FILE_NAME); - } -} diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java deleted file mode 100644 index c6bfc6d32989..000000000000 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java +++ /dev/null @@ -1,222 +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 com.android.permission.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * State of all runtime permissions. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -@SystemApi(client = Client.SYSTEM_SERVER) -public final class RuntimePermissionsState { - - /** - * Special value for {@link #mVersion} to indicate that no version was read. - */ - public static final int NO_VERSION = -1; - - /** - * The version of the runtime permissions. - */ - private final int mVersion; - - /** - * The fingerprint of the runtime permissions. - */ - @Nullable - private final String mFingerprint; - - /** - * The runtime permissions by packages. - */ - @NonNull - private final Map<String, List<PermissionState>> mPackagePermissions; - - /** - * The runtime permissions by shared users. - */ - @NonNull - private final Map<String, List<PermissionState>> mSharedUserPermissions; - - /** - * Create a new instance of this class. - * - * @param version the version of the runtime permissions - * @param fingerprint the fingerprint of the runtime permissions - * @param packagePermissions the runtime permissions by packages - * @param sharedUserPermissions the runtime permissions by shared users - */ - public RuntimePermissionsState(int version, @Nullable String fingerprint, - @NonNull Map<String, List<PermissionState>> packagePermissions, - @NonNull Map<String, List<PermissionState>> sharedUserPermissions) { - mVersion = version; - mFingerprint = fingerprint; - mPackagePermissions = packagePermissions; - mSharedUserPermissions = sharedUserPermissions; - } - - /** - * Get the version of the runtime permissions. - * - * @return the version of the runtime permissions - */ - public int getVersion() { - return mVersion; - } - - /** - * Get the fingerprint of the runtime permissions. - * - * @return the fingerprint of the runtime permissions - */ - @Nullable - public String getFingerprint() { - return mFingerprint; - } - - /** - * Get the runtime permissions by packages. - * - * @return the runtime permissions by packages - */ - @NonNull - public Map<String, List<PermissionState>> getPackagePermissions() { - return mPackagePermissions; - } - - /** - * Get the runtime permissions by shared users. - * - * @return the runtime permissions by shared users - */ - @NonNull - public Map<String, List<PermissionState>> getSharedUserPermissions() { - return mSharedUserPermissions; - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - RuntimePermissionsState that = (RuntimePermissionsState) object; - return mVersion == that.mVersion - && Objects.equals(mFingerprint, that.mFingerprint) - && Objects.equals(mPackagePermissions, that.mPackagePermissions) - && Objects.equals(mSharedUserPermissions, that.mSharedUserPermissions); - } - - @Override - public int hashCode() { - return Objects.hash(mVersion, mFingerprint, mPackagePermissions, mSharedUserPermissions); - } - - /** - * State of a single permission. - */ - public static final class PermissionState { - - /** - * The name of the permission. - */ - @NonNull - private final String mName; - - /** - * Whether the permission is granted. - */ - private final boolean mGranted; - - /** - * The flags of the permission. - */ - private final int mFlags; - - /** - * Create a new instance of this class. - * - * @param name the name of the permission - * @param granted whether the permission is granted - * @param flags the flags of the permission - */ - public PermissionState(@NonNull String name, boolean granted, int flags) { - mName = name; - mGranted = granted; - mFlags = flags; - } - - /** - * Get the name of the permission. - * - * @return the name of the permission - */ - @NonNull - public String getName() { - return mName; - } - - /** - * Get whether the permission is granted. - * - * @return whether the permission is granted - */ - public boolean isGranted() { - return mGranted; - } - - /** - * Get the flags of the permission. - * - * @return the flags of the permission - */ - public int getFlags() { - return mFlags; - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - PermissionState that = (PermissionState) object; - return mGranted == that.mGranted - && mFlags == that.mFlags - && Objects.equals(mName, that.mName); - } - - @Override - public int hashCode() { - return Objects.hash(mName, mGranted, mFlags); - } - } -} diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java deleted file mode 100644 index 2e5a28aa1d6a..000000000000 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java +++ /dev/null @@ -1,73 +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 com.android.role.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; -import android.os.UserHandle; - -/** - * Persistence for roles. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -@SystemApi(client = Client.SYSTEM_SERVER) -public interface RolesPersistence { - - /** - * Read the roles from persistence. - * - * This will perform I/O operations synchronously. - * - * @param user the user to read for - * @return the roles read - */ - @Nullable - RolesState readForUser(@NonNull UserHandle user); - - /** - * Write the roles to persistence. - * - * This will perform I/O operations synchronously. - * - * @param roles the roles to write - * @param user the user to write for - */ - void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user); - - /** - * Delete the roles from persistence. - * - * This will perform I/O operations synchronously. - * - * @param user the user to delete for - */ - void deleteForUser(@NonNull UserHandle user); - - /** - * Create a new instance of {@link RolesPersistence} implementation. - * - * @return the new instance. - */ - @NonNull - static RolesPersistence createInstance() { - return new RolesPersistenceImpl(); - } -} diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java deleted file mode 100644 index f66257f13ef6..000000000000 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java +++ /dev/null @@ -1,218 +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 com.android.role.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ApexEnvironment; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.AtomicFile; -import android.util.Log; -import android.util.Xml; - -import com.android.permission.persistence.IoUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.Set; - -/** - * Persistence implementation for roles. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -public class RolesPersistenceImpl implements RolesPersistence { - - private static final String LOG_TAG = RolesPersistenceImpl.class.getSimpleName(); - - private static final String APEX_MODULE_NAME = "com.android.permission"; - - private static final String ROLES_FILE_NAME = "roles.xml"; - - private static final String TAG_ROLES = "roles"; - private static final String TAG_ROLE = "role"; - private static final String TAG_HOLDER = "holder"; - - private static final String ATTRIBUTE_VERSION = "version"; - private static final String ATTRIBUTE_NAME = "name"; - private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash"; - - @Nullable - @Override - public RolesState readForUser(@NonNull UserHandle user) { - File file = getFile(user); - try (FileInputStream inputStream = new AtomicFile(file).openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(inputStream, null); - return parseXml(parser); - } catch (FileNotFoundException e) { - Log.i(LOG_TAG, "roles.xml not found"); - return null; - } catch (XmlPullParserException | IOException e) { - throw new IllegalStateException("Failed to read roles.xml: " + file , e); - } - } - - @NonNull - private static RolesState parseXml(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_ROLES)) { - return parseRoles(parser); - } - } - throw new IllegalStateException("Missing <" + TAG_ROLES + "> in roles.xml"); - } - - @NonNull - private static RolesState parseRoles(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - int version = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION)); - String packagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH); - - Map<String, Set<String>> roles = new ArrayMap<>(); - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_ROLE)) { - String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME); - Set<String> roleHolders = parseRoleHolders(parser); - roles.put(roleName, roleHolders); - } - } - - return new RolesState(version, packagesHash, roles); - } - - @NonNull - private static Set<String> parseRoleHolders(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - Set<String> roleHolders = new ArraySet<>(); - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_HOLDER)) { - String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME); - roleHolders.add(roleHolder); - } - } - return roleHolders; - } - - @Override - public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) { - File file = getFile(user); - AtomicFile atomicFile = new AtomicFile(file); - FileOutputStream outputStream = null; - try { - outputStream = atomicFile.startWrite(); - - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(outputStream, StandardCharsets.UTF_8.name()); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - serializer.startDocument(null, true); - - serializeRoles(serializer, roles); - - serializer.endDocument(); - atomicFile.finishWrite(outputStream); - } catch (Exception e) { - Log.wtf(LOG_TAG, "Failed to write roles.xml, restoring backup: " + file, - e); - atomicFile.failWrite(outputStream); - } finally { - IoUtils.closeQuietly(outputStream); - } - } - - private static void serializeRoles(@NonNull XmlSerializer serializer, - @NonNull RolesState roles) throws IOException { - serializer.startTag(null, TAG_ROLES); - - int version = roles.getVersion(); - serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version)); - String packagesHash = roles.getPackagesHash(); - if (packagesHash != null) { - serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash); - } - - for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) { - String roleName = entry.getKey(); - Set<String> roleHolders = entry.getValue(); - - serializer.startTag(null, TAG_ROLE); - serializer.attribute(null, ATTRIBUTE_NAME, roleName); - serializeRoleHolders(serializer, roleHolders); - serializer.endTag(null, TAG_ROLE); - } - - serializer.endTag(null, TAG_ROLES); - } - - private static void serializeRoleHolders(@NonNull XmlSerializer serializer, - @NonNull Set<String> roleHolders) throws IOException { - for (String roleHolder : roleHolders) { - serializer.startTag(null, TAG_HOLDER); - serializer.attribute(null, ATTRIBUTE_NAME, roleHolder); - serializer.endTag(null, TAG_HOLDER); - } - } - - @Override - public void deleteForUser(@NonNull UserHandle user) { - getFile(user).delete(); - } - - @NonNull - private static File getFile(@NonNull UserHandle user) { - ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME); - File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user); - return new File(dataDirectory, ROLES_FILE_NAME); - } -} diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java deleted file mode 100644 index f61efa0e840d..000000000000 --- a/apex/permission/service/java/com/android/role/persistence/RolesState.java +++ /dev/null @@ -1,115 +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 com.android.role.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; - -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * State of all roles. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -@SystemApi(client = Client.SYSTEM_SERVER) -public final class RolesState { - - /** - * The version of the roles. - */ - private final int mVersion; - - /** - * The hash of all packages in the system. - */ - @Nullable - private final String mPackagesHash; - - /** - * The roles. - */ - @NonNull - private final Map<String, Set<String>> mRoles; - - /** - * Create a new instance of this class. - * - * @param version the version of the roles - * @param packagesHash the hash of all packages in the system - * @param roles the roles - */ - public RolesState(int version, @Nullable String packagesHash, - @NonNull Map<String, Set<String>> roles) { - mVersion = version; - mPackagesHash = packagesHash; - mRoles = roles; - } - - /** - * Get the version of the roles. - * - * @return the version of the roles - */ - public int getVersion() { - return mVersion; - } - - /** - * Get the hash of all packages in the system. - * - * @return the hash of all packages in the system - */ - @Nullable - public String getPackagesHash() { - return mPackagesHash; - } - - /** - * Get the roles. - * - * @return the roles - */ - @NonNull - public Map<String, Set<String>> getRoles() { - return mRoles; - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - RolesState that = (RolesState) object; - return mVersion == that.mVersion - && Objects.equals(mPackagesHash, that.mPackagesHash) - && Objects.equals(mRoles, that.mRoles); - } - - @Override - public int hashCode() { - return Objects.hash(mVersion, mPackagesHash, mRoles); - } -} diff --git a/apex/permission/testing/test_manifest.json b/apex/permission/testing/test_manifest.json deleted file mode 100644 index bc19a9ea0172..000000000000 --- a/apex/permission/testing/test_manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.permission", - "version": 2147483647 -} diff --git a/apex/permission/tests/AndroidManifest.xml b/apex/permission/tests/AndroidManifest.xml deleted file mode 100644 index 57ee6417aeb3..000000000000 --- a/apex/permission/tests/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - ~ 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. - --> - -<manifest - xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.permission.test"> - - <!-- The application has to be debuggable for static mocking to work. --> - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation - android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.permission.test" - android:label="Permission APEX Tests" /> -</manifest> diff --git a/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt b/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt deleted file mode 100644 index 2987da087e51..000000000000 --- a/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt +++ /dev/null @@ -1,110 +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 com.android.permission.persistence - -import android.content.ApexEnvironment -import android.content.Context -import android.os.Process -import android.os.UserHandle -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations.initMocks -import org.mockito.MockitoSession -import org.mockito.quality.Strictness -import java.io.File - -@RunWith(AndroidJUnit4::class) -class RuntimePermissionsPersistenceTest { - private val context = InstrumentationRegistry.getInstrumentation().context - - private lateinit var mockDataDirectory: File - - private lateinit var mockitoSession: MockitoSession - @Mock - lateinit var apexEnvironment: ApexEnvironment - - private val persistence = RuntimePermissionsPersistence.createInstance() - private val permissionState = RuntimePermissionsState.PermissionState("permission", true, 3) - private val state = RuntimePermissionsState( - 1, "fingerprint", mapOf("package" to listOf(permissionState)), - mapOf("sharedUser" to listOf(permissionState)) - ) - private val user = Process.myUserHandle() - - @Before - fun createMockDataDirectory() { - mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE) - mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() } - } - - @Before - fun mockApexEnvironment() { - initMocks(this) - mockitoSession = mockitoSession() - .mockStatic(ApexEnvironment::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - `when`(ApexEnvironment.getApexEnvironment(eq(APEX_MODULE_NAME))).thenReturn(apexEnvironment) - `when`(apexEnvironment.getDeviceProtectedDataDirForUser(any(UserHandle::class.java))).then { - File(mockDataDirectory, it.arguments[0].toString()).also { it.mkdirs() } - } - } - - @After - fun finishMockingApexEnvironment() { - mockitoSession.finishMocking() - } - - @Test - fun testReadWrite() { - persistence.writeForUser(state, user) - val persistedState = persistence.readForUser(user) - - assertThat(persistedState).isEqualTo(state) - assertThat(persistedState!!.version).isEqualTo(state.version) - assertThat(persistedState.fingerprint).isEqualTo(state.fingerprint) - assertThat(persistedState.packagePermissions).isEqualTo(state.packagePermissions) - val persistedPermissionState = persistedState.packagePermissions.values.first().first() - assertThat(persistedPermissionState.name).isEqualTo(permissionState.name) - assertThat(persistedPermissionState.isGranted).isEqualTo(permissionState.isGranted) - assertThat(persistedPermissionState.flags).isEqualTo(permissionState.flags) - assertThat(persistedState.sharedUserPermissions).isEqualTo(state.sharedUserPermissions) - } - - @Test - fun testDelete() { - persistence.writeForUser(state, user) - persistence.deleteForUser(user) - val persistedState = persistence.readForUser(user) - - assertThat(persistedState).isNull() - } - - companion object { - private const val APEX_MODULE_NAME = "com.android.permission" - } -} diff --git a/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt b/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt deleted file mode 100644 index f9d9d5afb25d..000000000000 --- a/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt +++ /dev/null @@ -1,101 +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 com.android.role.persistence - -import android.content.ApexEnvironment -import android.content.Context -import android.os.Process -import android.os.UserHandle -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations.initMocks -import org.mockito.MockitoSession -import org.mockito.quality.Strictness -import java.io.File - -@RunWith(AndroidJUnit4::class) -class RolesPersistenceTest { - private val context = InstrumentationRegistry.getInstrumentation().context - - private lateinit var mockDataDirectory: File - - private lateinit var mockitoSession: MockitoSession - @Mock - lateinit var apexEnvironment: ApexEnvironment - - private val persistence = RolesPersistence.createInstance() - private val state = RolesState(1, "packagesHash", mapOf("role" to setOf("holder1", "holder2"))) - private val user = Process.myUserHandle() - - @Before - fun createMockDataDirectory() { - mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE) - mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() } - } - - @Before - fun mockApexEnvironment() { - initMocks(this) - mockitoSession = mockitoSession() - .mockStatic(ApexEnvironment::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - `when`(ApexEnvironment.getApexEnvironment(eq(APEX_MODULE_NAME))).thenReturn(apexEnvironment) - `when`(apexEnvironment.getDeviceProtectedDataDirForUser(any(UserHandle::class.java))).then { - File(mockDataDirectory, it.arguments[0].toString()).also { it.mkdirs() } - } - } - - @After - fun finishMockingApexEnvironment() { - mockitoSession.finishMocking() - } - - @Test - fun testReadWrite() { - persistence.writeForUser(state, user) - val persistedState = persistence.readForUser(user) - - assertThat(persistedState).isEqualTo(state) - assertThat(persistedState!!.version).isEqualTo(state.version) - assertThat(persistedState.packagesHash).isEqualTo(state.packagesHash) - assertThat(persistedState.roles).isEqualTo(state.roles) - } - - @Test - fun testDelete() { - persistence.writeForUser(state, user) - persistence.deleteForUser(user) - val persistedState = persistence.readForUser(user) - - assertThat(persistedState).isNull() - } - - companion object { - private const val APEX_MODULE_NAME = "com.android.permission" - } -} diff --git a/api/Android.bp b/api/Android.bp index 9a157b8a0578..fdfef4cb8a74 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -50,10 +50,7 @@ genrule { dest: "current.txt", }, { - targets: [ - "sdk", - "win_sdk", - ], + targets: ["sdk", "win_sdk"], dir: "apistubs/android/public/api", dest: "android.txt", }, @@ -106,6 +103,11 @@ genrule { dir: "api", dest: "removed.txt", }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/public/api", + dest: "removed.txt", + }, ], } @@ -131,10 +133,7 @@ genrule { dest: "system-current.txt", }, { - targets: [ - "sdk", - "win_sdk", - ], + targets: ["sdk", "win_sdk"], dir: "apistubs/android/system/api", dest: "android.txt", }, @@ -163,6 +162,11 @@ genrule { dir: "api", dest: "system-removed.txt", }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system/api", + dest: "removed.txt", + }, ], visibility: ["//visibility:public"], } @@ -189,10 +193,7 @@ genrule { dest: "module-lib-current.txt", }, { - targets: [ - "sdk", - "win_sdk", - ], + targets: ["sdk", "win_sdk"], dir: "apistubs/android/module-lib/api", dest: "android.txt", }, @@ -220,6 +221,11 @@ genrule { dir: "api", dest: "module-lib-removed.txt", }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/module-lib/api", + dest: "removed.txt", + }, ], } diff --git a/boot/Android.bp b/boot/Android.bp new file mode 100644 index 000000000000..dd4066a7d151 --- /dev/null +++ b/boot/Android.bp @@ -0,0 +1,18 @@ +// 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. + +boot_image { + name: "framework-boot-image", + image_name: "boot", +} diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index bdb83804d903..846a34eb41c9 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -174,10 +174,6 @@ public class Am extends BaseCommand { instrument.noWindowAnimation = true; } else if (opt.equals("--no-hidden-api-checks")) { instrument.disableHiddenApiChecks = true; - } else if (opt.equals("--no-test-api-checks")) { - // TODO(satayev): remove this option, only kept for backwards compatibility with - // cached tradefed instance - instrument.disableTestApiChecks = false; } else if (opt.equals("--no-test-api-access")) { instrument.disableTestApiChecks = false; } else if (opt.equals("--no-isolated-storage")) { @@ -198,7 +194,6 @@ public class Am extends BaseCommand { } instrument.componentNameArg = nextArgRequired(); - instrument.run(); } } diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index 07221f97c72b..14ebb713b6ae 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -62,4 +62,13 @@ cc_binary { // Create a symlink from app_process to app_process32 or 64 // depending on the target configuration. symlink_preferred_arch: true, + + // Enable ASYNC MTE in the zygote, in order to allow apps and the system + // server to use MTE. We use ASYNC because we don't expect the pre-fork + // zygote to have substantial memory corruption bugs (as it's primarily Java + // code), and we don't want to waste memory recording malloc/free stack + // traces (which happens in SYNC mode). + sanitize: { + memtag_heap: true, + }, } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 2c7ee212b7b5..854982f825dc 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -92,6 +92,8 @@ static const char SYSTEM_TIME_DIR_NAME[] = "time"; static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time"; static const char CLOCK_FONT_ASSET[] = "images/clock_font.png"; static const char CLOCK_FONT_ZIP_NAME[] = "clock_font.png"; +static const char PROGRESS_FONT_ASSET[] = "images/progress_font.png"; +static const char PROGRESS_FONT_ZIP_NAME[] = "progress_font.png"; static const char LAST_TIME_CHANGED_FILE_NAME[] = "last_time_change"; static const char LAST_TIME_CHANGED_FILE_PATH[] = "/data/system/time/last_time_change"; static const char ACCURATE_TIME_FLAG_FILE_NAME[] = "time_is_accurate"; @@ -107,6 +109,7 @@ static constexpr size_t FONT_NUM_ROWS = FONT_NUM_CHARS / FONT_NUM_COLS; static const int TEXT_CENTER_VALUE = INT_MAX; static const int TEXT_MISSING_VALUE = INT_MIN; static const char EXIT_PROP_NAME[] = "service.bootanim.exit"; +static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress"; static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays"; static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1; static constexpr size_t TEXT_POS_LEN_MAX = 16; @@ -891,6 +894,18 @@ void BootAnimation::drawClock(const Font& font, const int xPos, const int yPos) drawText(out, font, false, &x, &y); } +void BootAnimation::drawProgress(int percent, const Font& font, const int xPos, const int yPos) { + static constexpr int PERCENT_LENGTH = 5; + + char percentBuff[PERCENT_LENGTH]; + // ';' has the ascii code just after ':', and the font resource contains '%' + // for that ascii code. + sprintf(percentBuff, "%d;", percent); + int x = xPos; + int y = yPos; + drawText(percentBuff, font, false, &x, &y); +} + bool BootAnimation::parseAnimationDesc(Animation& animation) { String8 desString; @@ -910,6 +925,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { int height = 0; int count = 0; int pause = 0; + int progress = 0; int framesToFadeCount = 0; char path[ANIM_ENTRY_NAME_MAX]; char color[7] = "000000"; // default to black if unspecified @@ -919,11 +935,17 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) { int nextReadPos; - if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { - // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps); + int topLineNumbers = sscanf(l, "%d %d %d %d", &width, &height, &fps, &progress); + if (topLineNumbers == 3 || topLineNumbers == 4) { + // SLOGD("> w=%d, h=%d, fps=%d, progress=%d", width, height, fps, progress); animation.width = width; animation.height = height; animation.fps = fps; + if (topLineNumbers == 4) { + animation.progressEnabled = (progress != 0); + } else { + animation.progressEnabled = false; + } } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n", &pathType, &count, &pause, path, &nextReadPos) >= 4) { if (pathType == 'f') { @@ -1000,6 +1022,14 @@ bool BootAnimation::preloadZip(Animation& animation) { continue; } + if (entryName == PROGRESS_FONT_ZIP_NAME) { + FileMap* map = zip->createEntryFileMap(entry); + if (map) { + animation.progressFont.map = map; + } + continue; + } + for (size_t j = 0; j < pcount; j++) { if (path == animation.parts[j].path) { uint16_t method; @@ -1131,6 +1161,8 @@ bool BootAnimation::movie() { mClockEnabled = clockFontInitialized; } + initFont(&mAnimation->progressFont, PROGRESS_FONT_ASSET); + if (mClockEnabled && !updateIsTimeAccurate()) { mTimeCheckThread = new TimeCheckThread(this); mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL); @@ -1166,6 +1198,7 @@ bool BootAnimation::playAnimation(const Animation& animation) { elapsedRealtime()); int fadedFramesCount = 0; + int lastDisplayedProgress = 0; for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); const size_t fcount = part.frames.size(); @@ -1191,6 +1224,12 @@ bool BootAnimation::playAnimation(const Animation& animation) { part.backgroundColor[2], 1.0f); + // For the last animation, if we have progress indicator from + // the system, display it. + int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0); + bool displayProgress = animation.progressEnabled && + (i == (pcount -1)) && currentProgress != 0; + for (size_t j=0 ; j<fcount ; j++) { if (shouldStopPlayingPart(part, fadedFramesCount)) break; @@ -1248,6 +1287,23 @@ bool BootAnimation::playAnimation(const Animation& animation) { drawClock(animation.clockFont, part.clockPosX, part.clockPosY); } + if (displayProgress) { + int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0); + // In case the new progress jumped suddenly, still show an + // increment of 1. + if (lastDisplayedProgress != 100) { + // Artificially sleep 1/10th a second to slow down the animation. + usleep(100000); + if (lastDisplayedProgress < newProgress) { + lastDisplayedProgress++; + } + } + // Put the progress percentage right below the animation. + int posY = animation.height / 3; + int posX = TEXT_CENTER_VALUE; + drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY); + } + handleViewport(frameDuration); eglSwapBuffers(mDisplay, mSurface); diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index aee385387f57..b52222c799b0 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -100,11 +100,13 @@ public: int fps; int width; int height; + bool progressEnabled; Vector<Part> parts; String8 audioConf; String8 fileName; ZipFileRO* zip; Font clockFont; + Font progressFont; }; // All callbacks will be called from this class's internal thread. @@ -168,6 +170,7 @@ private: bool movie(); void drawText(const char* str, const Font& font, bool bold, int* x, int* y); void drawClock(const Font& font, const int xPos, const int yPos); + void drawProgress(int percent, const Font& font, const int xPos, const int yPos); void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight, const Animation::Part& part, int fadedFramesCount); bool validClock(const Animation::Part& part); diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md index f9b83c957d5b..1678053c48d9 100644 --- a/cmds/bootanimation/FORMAT.md +++ b/cmds/bootanimation/FORMAT.md @@ -22,11 +22,14 @@ The `bootanimation.zip` archive file includes: The first line defines the general parameters of the animation: - WIDTH HEIGHT FPS + WIDTH HEIGHT FPS [PROGRESS] * **WIDTH:** animation width (pixels) * **HEIGHT:** animation height (pixels) * **FPS:** frames per second, e.g. 60 + * **PROGRESS:** whether to show a progress percentage on the last part + + The percentage will be displayed with an x-coordinate of 'c', and a + y-coordinate set to 1/3 of the animation height. It is followed by a number of rows of the form: @@ -77,6 +80,11 @@ The file used to draw the time on top of the boot animation. The font format is * Each row is divided in half: regular weight glyphs on the top half, bold glyphs on the bottom * For a NxM image each character glyph will be N/16 pixels wide and M/(12*2) pixels high +## progress_font.png + +The file used to draw the boot progress in percentage on top of the boot animation. The font format +follows the same specification as the one described for clock_font.png. + ## loading and playing frames Each part is scanned and loaded directly from the zip archive. Within a part directory, every file diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index c2ee6dcd13b2..dc2868a59840 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -20,6 +20,7 @@ import android.os.IVoldTaskListener; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.storage.DiskInfo; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; @@ -30,6 +31,8 @@ import java.util.concurrent.CompletableFuture; public final class Sm { private static final String TAG = "Sm"; + private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = + "persist.sys.vold_app_data_isolation_enabled"; IStorageManager mSm; @@ -107,6 +110,8 @@ public final class Sm { runStartCheckpoint(); } else if ("supports-checkpoint".equals(op)) { runSupportsCheckpoint(); + } else if ("unmount-app-data-dirs".equals(op)) { + runDisableAppDataIsolation(); } else { throw new IllegalArgumentException(); } @@ -253,6 +258,17 @@ public final class Sm { System.out.println(result.get()); } + public void runDisableAppDataIsolation() throws RemoteException { + if (!SystemProperties.getBoolean( + ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) { + throw new IllegalStateException("Storage app data isolation is not enabled."); + } + final String pkgName = nextArg(); + final int pid = Integer.parseInt(nextArg()); + final int userId = Integer.parseInt(nextArg()); + mSm.disableAppDataIsolation(pkgName, pid, userId); + } + public void runForget() throws RemoteException { final String fsUuid = nextArg(); if ("all".equals(fsUuid)) { @@ -373,6 +389,8 @@ public final class Sm { System.err.println(""); System.err.println(" sm supports-checkpoint"); System.err.println(""); + System.err.println(" sm unmount-app-data-dirs PACKAGE_NAME PID USER_ID"); + System.err.println(""); return 1; } } diff --git a/config/OWNERS b/config/OWNERS index d59c6f2d72ba..001038d139c4 100644 --- a/config/OWNERS +++ b/config/OWNERS @@ -4,5 +4,11 @@ include /ZYGOTE_OWNERS per-file hiddenapi-* = andreionea@google.com, mathewi@google.com, satayev@google.com +# art-team@ manages the boot image profiles +per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com +per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com +per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com +per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com + # Escalations: per-file hiddenapi-* = bdc@google.com, narayan@google.com diff --git a/config/hiddenapi-temp-blocklist.txt b/config/hiddenapi-max-target-r-loprio.txt index 246eeea35a19..246eeea35a19 100644 --- a/config/hiddenapi-temp-blocklist.txt +++ b/config/hiddenapi-max-target-r-loprio.txt diff --git a/core/api/current.txt b/core/api/current.txt index 252c33c4efda..285506f0556d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -385,6 +385,7 @@ package android { field public static final int calendarViewShown = 16843596; // 0x101034c field public static final int calendarViewStyle = 16843613; // 0x101035d field public static final int canControlMagnification = 16844039; // 0x1010507 + field public static final int canPauseRecording = 16844311; // 0x1010617 field public static final int canPerformGestures = 16844045; // 0x101050d field public static final int canRecord = 16844060; // 0x101051c field @Deprecated public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8 @@ -10144,6 +10145,7 @@ package android.content { method public void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void sendStickyBroadcast(@RequiresPermission android.content.Intent); + method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public void sendStickyBroadcast(@NonNull @RequiresPermission android.content.Intent, @Nullable android.os.Bundle); method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void sendStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void sendStickyOrderedBroadcast(@RequiresPermission android.content.Intent, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void sendStickyOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); @@ -10186,6 +10188,7 @@ package android.content { field public static final String BIOMETRIC_SERVICE = "biometric"; field public static final String BLOB_STORE_SERVICE = "blob_store"; field public static final String BLUETOOTH_SERVICE = "bluetooth"; + field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CAMERA_SERVICE = "camera"; field public static final String CAPTIONING_SERVICE = "captioning"; field public static final String CARRIER_CONFIG_SERVICE = "carrier_config"; @@ -12125,6 +12128,8 @@ package android.content.pm { field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad"; field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors"; field public static final String FEATURE_HOME_SCREEN = "android.software.home_screen"; + field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE = "android.hardware.identity_credential"; + field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS = "android.hardware.identity_credential_direct_access"; field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods"; field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels"; field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris"; @@ -12211,7 +12216,7 @@ package android.content.pm { field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000 field public static final int GET_GIDS = 256; // 0x100 field public static final int GET_INSTRUMENTATION = 16; // 0x10 - field public static final int GET_INTENT_FILTERS = 32; // 0x20 + field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20 field public static final int GET_META_DATA = 128; // 0x80 field public static final int GET_PERMISSIONS = 4096; // 0x1000 field public static final int GET_PROVIDERS = 8; // 0x8 @@ -24265,6 +24270,7 @@ package android.media.tv { } public final class TvInputInfo implements android.os.Parcelable { + method public boolean canPauseRecording(); method public boolean canRecord(); method @Deprecated public android.content.Intent createSettingsIntent(); method public android.content.Intent createSetupIntent(); @@ -24298,6 +24304,7 @@ package android.media.tv { public static final class TvInputInfo.Builder { ctor public TvInputInfo.Builder(android.content.Context, android.content.ComponentName); method public android.media.tv.TvInputInfo build(); + method @NonNull public android.media.tv.TvInputInfo.Builder setCanPauseRecording(boolean); method public android.media.tv.TvInputInfo.Builder setCanRecord(boolean); method public android.media.tv.TvInputInfo.Builder setExtras(android.os.Bundle); method public android.media.tv.TvInputInfo.Builder setTunerCount(int); @@ -24389,7 +24396,9 @@ package android.media.tv { method public void notifyRecordingStopped(android.net.Uri); method public void notifyTuned(android.net.Uri); method public void onAppPrivateCommand(@NonNull String, android.os.Bundle); + method public void onPauseRecording(@NonNull android.os.Bundle); method public abstract void onRelease(); + method public void onResumeRecording(@NonNull android.os.Bundle); method public abstract void onStartRecording(@Nullable android.net.Uri); method public void onStartRecording(@Nullable android.net.Uri, @NonNull android.os.Bundle); method public abstract void onStopRecording(); @@ -24439,7 +24448,11 @@ package android.media.tv { public class TvRecordingClient { ctor public TvRecordingClient(android.content.Context, String, @NonNull android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler); + method public void pauseRecording(); + method public void pauseRecording(@NonNull android.os.Bundle); method public void release(); + method public void resumeRecording(); + method public void resumeRecording(@NonNull android.os.Bundle); method public void sendAppPrivateCommand(@NonNull String, android.os.Bundle); method public void startRecording(@Nullable android.net.Uri); method public void startRecording(@Nullable android.net.Uri, @NonNull android.os.Bundle); @@ -29587,6 +29600,24 @@ package android.os { method public boolean unlinkToDeath(@NonNull android.os.IBinder.DeathRecipient, int); } + public final class BugreportManager { + method public void cancelBugreport(); + method public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); + } + + public abstract static class BugreportManager.BugreportCallback { + ctor public BugreportManager.BugreportCallback(); + method public void onEarlyReportFinished(); + method public void onError(int); + method public void onFinished(); + method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float); + field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5 + field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1 + field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2 + field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4 + field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3 + } + public class Build { ctor public Build(); method @NonNull public static java.util.List<android.os.Build.Partition> getFingerprintedPartitions(); @@ -29610,6 +29641,8 @@ package android.os { field @Deprecated public static final String RADIO; field @Deprecated public static final String SERIAL; field @NonNull public static final String SKU; + field @NonNull public static final String SOC_MANUFACTURER; + field @NonNull public static final String SOC_MODEL; field public static final String[] SUPPORTED_32_BIT_ABIS; field public static final String[] SUPPORTED_64_BIT_ABIS; field public static final String[] SUPPORTED_ABIS; @@ -33918,6 +33951,7 @@ package android.provider { field public static final String ACTION_LOCATION_SOURCE_SETTINGS = "android.settings.LOCATION_SOURCE_SETTINGS"; field public static final String ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS = "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS"; field public static final String ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION"; + field public static final String ACTION_MANAGE_ALL_SUBSCRIPTIONS_SETTINGS = "android.settings.MANAGE_ALL_SUBSCRIPTIONS_SETTINGS"; field public static final String ACTION_MANAGE_APPLICATIONS_SETTINGS = "android.settings.MANAGE_APPLICATIONS_SETTINGS"; field public static final String ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION"; field public static final String ACTION_MANAGE_DEFAULT_APPS_SETTINGS = "android.settings.MANAGE_DEFAULT_APPS_SETTINGS"; @@ -35859,6 +35893,9 @@ package android.se.omapi { method @NonNull public String getVersion(); method public boolean isConnected(); method public void shutdown(); + field public static final String ACTION_SECURE_ELEMENT_STATE_CHANGED = "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED"; + field public static final String EXTRA_READER_NAME = "android.se.omapi.extra.READER_NAME"; + field public static final String EXTRA_READER_STATE = "android.se.omapi.extra.READER_STATE"; } public static interface SEService.OnConnectedListener { @@ -36041,15 +36078,20 @@ package android.security.identity { public abstract class IdentityCredential { method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair(); method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException; + method @NonNull public byte[] delete(@NonNull byte[]); method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]); method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification(); method @NonNull public abstract int[] getAuthenticationDataUsageCount(); method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain(); method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException; + method @NonNull public byte[] proveOwnership(@NonNull byte[]); method public abstract void setAllowUsingExhaustedKeys(boolean); + method public void setAllowUsingExpiredKeys(boolean); method public abstract void setAvailableAuthenticationKeys(int, int); method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException; - method public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException; + method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException; + method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException; + method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData); } public class IdentityCredentialException extends java.lang.Exception { @@ -36059,7 +36101,7 @@ package android.security.identity { public abstract class IdentityCredentialStore { method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException; - method @Nullable public abstract byte[] deleteCredentialByName(@NonNull String); + method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String); method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException; method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context); method @Nullable public static android.security.identity.IdentityCredentialStore getInstance(@NonNull android.content.Context); @@ -36135,9 +36177,10 @@ package android.security.identity { package android.security.keystore { public class BackendBusyException extends java.security.ProviderException { - ctor public BackendBusyException(); - ctor public BackendBusyException(@NonNull String); - ctor public BackendBusyException(@NonNull String, @NonNull Throwable); + ctor public BackendBusyException(long); + ctor public BackendBusyException(long, @NonNull String); + ctor public BackendBusyException(long, @NonNull String, @NonNull Throwable); + method public long getBackOffHintMillis(); } public class KeyExpiredException extends java.security.InvalidKeyException { @@ -36275,6 +36318,7 @@ package android.security.keystore { field public static final int ORIGIN_IMPORTED = 2; // 0x2 field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8 field public static final int ORIGIN_UNKNOWN = 4; // 0x4 + field public static final int PURPOSE_AGREE_KEY = 64; // 0x40 field public static final int PURPOSE_DECRYPT = 2; // 0x2 field public static final int PURPOSE_ENCRYPT = 1; // 0x1 field public static final int PURPOSE_SIGN = 4; // 0x4 @@ -38454,7 +38498,9 @@ package android.telecom { method public void onStateChanged(int); method public void onStopDtmfTone(); method public void onStopRtt(); + method public void onTrackedByNonUiService(boolean); method public void onUnhold(); + method public void onUsingAlternativeUi(boolean); method public static String propertiesToString(int); method public final void putExtras(@NonNull android.os.Bundle); method public final void removeExtras(java.util.List<java.lang.String>); @@ -38792,6 +38838,7 @@ package android.telecom { field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8 field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100 field @NonNull public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR; + field public static final String EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE = "android.telecom.extra.ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE"; field public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE = "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE"; field public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING"; field public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH"; @@ -39217,6 +39264,7 @@ package android.telephony { field public static final int BAND_25 = 25; // 0x19 field public static final int BAND_257 = 257; // 0x101 field public static final int BAND_258 = 258; // 0x102 + field public static final int BAND_26 = 26; // 0x1a field public static final int BAND_260 = 260; // 0x104 field public static final int BAND_261 = 261; // 0x105 field public static final int BAND_28 = 28; // 0x1c @@ -39228,10 +39276,12 @@ package android.telephony { field public static final int BAND_39 = 39; // 0x27 field public static final int BAND_40 = 40; // 0x28 field public static final int BAND_41 = 41; // 0x29 + field public static final int BAND_46 = 46; // 0x2e field public static final int BAND_48 = 48; // 0x30 field public static final int BAND_5 = 5; // 0x5 field public static final int BAND_50 = 50; // 0x32 field public static final int BAND_51 = 51; // 0x33 + field public static final int BAND_53 = 53; // 0x35 field public static final int BAND_65 = 65; // 0x41 field public static final int BAND_66 = 66; // 0x42 field public static final int BAND_7 = 7; // 0x7 @@ -39257,6 +39307,7 @@ package android.telephony { field public static final int BAND_93 = 93; // 0x5d field public static final int BAND_94 = 94; // 0x5e field public static final int BAND_95 = 95; // 0x5f + field public static final int BAND_96 = 96; // 0x60 } public static final class AccessNetworkConstants.UtranBand { @@ -39544,6 +39595,7 @@ package android.telephony { field public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool"; field public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool"; field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool"; + field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool"; field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool"; field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool"; field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool"; @@ -40219,6 +40271,7 @@ package android.telephony { field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22 field public static final int SIGNAL_LOST = -3; // 0xfffffffd field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb + field public static final int SLICE_REJECTED = 2252; // 0x8cc field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888 field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894 field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa @@ -40486,6 +40539,12 @@ package android.telephony { field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1 } + public final class PhoneCapability implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR; + } + public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher { ctor public PhoneNumberFormattingTextWatcher(); ctor public PhoneNumberFormattingTextWatcher(String); @@ -40497,12 +40556,13 @@ package android.telephony { public class PhoneNumberUtils { ctor public PhoneNumberUtils(); method public static void addTtsSpan(android.text.Spannable, int, int); + method public static boolean areSamePhoneNumber(@NonNull String, @NonNull String, @NonNull String); method @Deprecated public static String calledPartyBCDFragmentToString(byte[], int, int); method public static String calledPartyBCDFragmentToString(byte[], int, int, int); method @Deprecated public static String calledPartyBCDToString(byte[], int, int); method public static String calledPartyBCDToString(byte[], int, int, int); - method public static boolean compare(String, String); - method public static boolean compare(android.content.Context, String, String); + method @Deprecated public static boolean compare(String, String); + method @Deprecated public static boolean compare(android.content.Context, String, String); method public static String convertKeypadLettersToDigits(String); method public static android.text.style.TtsSpan createTtsSpan(String); method public static CharSequence createTtsSpannable(CharSequence); @@ -40554,10 +40614,10 @@ package android.telephony { public class PhoneStateListener { ctor public PhoneStateListener(); - ctor public PhoneStateListener(@NonNull java.util.concurrent.Executor); + ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor); method public void onActiveDataSubscriptionIdChanged(int); method public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); - method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); method public void onCallForwardingIndicatorChanged(boolean); method public void onCallStateChanged(int, String); method public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>); @@ -40565,36 +40625,150 @@ package android.telephony { method public void onDataActivity(int); method public void onDataConnectionStateChanged(int); method public void onDataConnectionStateChanged(int, int); - method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); - method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); method public void onMessageWaitingIndicatorChanged(boolean); - method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); method public void onServiceStateChanged(android.telephony.ServiceState); method @Deprecated public void onSignalStrengthChanged(int); method public void onSignalStrengthsChanged(android.telephony.SignalStrength); method public void onUserMobileDataStateChanged(boolean); - field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 - field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8 - field public static final int LISTEN_CALL_STATE = 32; // 0x20 - field public static final int LISTEN_CELL_INFO = 1024; // 0x400 - field public static final int LISTEN_CELL_LOCATION = 16; // 0x10 - field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80 - field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40 - field public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000 - field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 - field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 + field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 + field @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8 + field @Deprecated public static final int LISTEN_CALL_STATE = 32; // 0x20 + field @Deprecated public static final int LISTEN_CELL_INFO = 1024; // 0x400 + field @Deprecated public static final int LISTEN_CELL_LOCATION = 16; // 0x10 + field @Deprecated public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80 + field @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40 + field @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000 + field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 + field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 field public static final int LISTEN_NONE = 0; // 0x0 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 - field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000 - field public static final int LISTEN_SERVICE_STATE = 1; // 0x1 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000 + field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1 field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2 - field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100 - field public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000 + field @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100 + field @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 524288; // 0x80000 + } + + public static interface PhoneStateListener.ActiveDataSubscriptionIdChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int); + } + + public static interface PhoneStateListener.AlwaysReportedSignalStrengthChangedListener { + method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); + } + + public static interface PhoneStateListener.BarringInfoChangedListener { + method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo); + } + + public static interface PhoneStateListener.CallDisconnectCauseChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int); + } + + public static interface PhoneStateListener.CallForwardingIndicatorChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean); + } + + public static interface PhoneStateListener.CallStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public void onCallStateChanged(int, @Nullable String); + } + + public static interface PhoneStateListener.CarrierNetworkChangeListener { + method public void onCarrierNetworkChange(boolean); + } + + public static interface PhoneStateListener.CellInfoChangedListener { + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>); + } + + public static interface PhoneStateListener.CellLocationChangedListener { + method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(@NonNull android.telephony.CellLocation); + } + + public static interface PhoneStateListener.DataActivationStateChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int); + } + + public static interface PhoneStateListener.DataActivityListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int); + } + + public static interface PhoneStateListener.DataConnectionStateChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int); + } + + public static interface PhoneStateListener.DisplayInfoChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo); + } + + public static interface PhoneStateListener.EmergencyNumberListChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>); + } + + public static interface PhoneStateListener.ImsCallDisconnectCauseChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); + } + + public static interface PhoneStateListener.MessageWaitingIndicatorChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean); + } + + public static interface PhoneStateListener.PhoneCapabilityChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability); + } + + public static interface PhoneStateListener.PreciseDataConnectionStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); + } + + public static interface PhoneStateListener.RegistrationFailedListener { + method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int); + } + + public static interface PhoneStateListener.ServiceStateChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState); + } + + public static interface PhoneStateListener.SignalStrengthsChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength); + } + + public static interface PhoneStateListener.UserMobileDataStateChangedListener { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean); + } + + public final class PhysicalChannelConfig implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=1, to=261) public int getBand(); + method @IntRange(from=1) public int getCellBandwidthDownlinkKhz(); + method @IntRange(from=1) public int getCellBandwidthUplinkKhz(); + method @Deprecated public int getChannelNumber(); + method public int getConnectionStatus(); + method @IntRange(from=0) public int getDownlinkChannelNumber(); + method @IntRange(from=0) public int getDownlinkFrequencyKhz(); + method public int getNetworkType(); + method @IntRange(from=0, to=1007) public int getPhysicalCellId(); + method @IntRange(from=0) public int getUplinkChannelNumber(); + method @IntRange(from=0) public int getUplinkFrequencyKhz(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int BAND_UNKNOWN = 0; // 0x0 + field public static final int CELL_BANDWIDTH_UNKNOWN = 0; // 0x0 + field public static final int CHANNEL_NUMBER_UNKNOWN = -1; // 0xffffffff + field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1 + field public static final int CONNECTION_SECONDARY_SERVING = 2; // 0x2 + field public static final int CONNECTION_UNKNOWN = -1; // 0xffffffff + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhysicalChannelConfig> CREATOR; + field public static final int FREQUENCY_UNKNOWN = -1; // 0xffffffff + field public static final int PHYSICAL_CELL_ID_MAXIMUM_VALUE = 1007; // 0x3ef + field public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; // 0xffffffff } public final class PreciseDataConnectionState implements android.os.Parcelable { @@ -40676,6 +40850,50 @@ package android.telephony { field public static final int INVALID = 2147483647; // 0x7fffffff } + public final class SignalStrengthUpdateRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.os.IBinder getLiveToken(); + method @NonNull public java.util.Collection<android.telephony.SignalThresholdInfo> getSignalThresholdInfos(); + method public boolean isReportingRequestedWhileIdle(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalStrengthUpdateRequest> CREATOR; + } + + public static final class SignalStrengthUpdateRequest.Builder { + ctor public SignalStrengthUpdateRequest.Builder(); + method @NonNull public android.telephony.SignalStrengthUpdateRequest build(); + method @NonNull public android.telephony.SignalStrengthUpdateRequest.Builder setReportingRequestedWhileIdle(boolean); + method @NonNull public android.telephony.SignalStrengthUpdateRequest.Builder setSignalThresholdInfos(@NonNull java.util.Collection<android.telephony.SignalThresholdInfo>); + } + + public final class SignalThresholdInfo implements android.os.Parcelable { + method public int describeContents(); + method public static int getMaximumNumberOfThresholdsAllowed(); + method public static int getMinimumNumberOfThresholdsAllowed(); + method public int getRadioAccessNetworkType(); + method public int getSignalMeasurementType(); + method @NonNull public int[] getThresholds(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalThresholdInfo> CREATOR; + field public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2; // 0x2 + field public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3; // 0x3 + field public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4; // 0x4 + field public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1; // 0x1 + field public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5; // 0x5 + field public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6; // 0x6 + field public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7; // 0x7 + field public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8; // 0x8 + field public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0; // 0x0 + } + + public static final class SignalThresholdInfo.Builder { + ctor public SignalThresholdInfo.Builder(); + method @NonNull public android.telephony.SignalThresholdInfo build(); + method @NonNull public android.telephony.SignalThresholdInfo.Builder setRadioAccessNetworkType(int); + method @NonNull public android.telephony.SignalThresholdInfo.Builder setSignalMeasurementType(int); + method @NonNull public android.telephony.SignalThresholdInfo.Builder setThresholds(@NonNull int[]); + } + public final class SmsManager { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); @@ -41012,6 +41230,7 @@ package android.telephony { public class TelephonyManager { method public boolean canChangeDtmfToneLength(); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void clearSignalStrengthUpdateRequest(@NonNull android.telephony.SignalStrengthUpdateRequest); method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle); method public android.telephony.TelephonyManager createForSubscriptionId(int); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot(); @@ -41090,7 +41309,7 @@ package android.telephony { method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String); method public boolean isConcurrentVoiceAndDataSupported(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed(); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled(); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled(); method public boolean isEmergencyNumber(@NonNull String); @@ -41105,7 +41324,8 @@ package android.telephony { method public boolean isVoiceCapable(); method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle); method public boolean isWorldPhone(); - method public void listen(android.telephony.PhoneStateListener, int); + method @Deprecated public void listen(android.telephony.PhoneStateListener, int); + method public void registerPhoneStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.PhoneStateListener); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(String); @@ -41123,11 +41343,13 @@ package android.telephony { method public boolean setOperatorBrandOverride(String); method public boolean setPreferredNetworkTypeToGlobal(); method public void setPreferredOpportunisticDataSubscription(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSignalStrengthUpdateRequest(@NonNull android.telephony.SignalStrengthUpdateRequest); method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings); method public boolean setVoiceMailNumber(String, String); method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri); method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int); + method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener); method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE"; field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL"; @@ -41149,6 +41371,7 @@ package android.telephony { field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80 field public static final int CALL_COMPOSER_STATUS_OFF = 0; // 0x0 field public static final int CALL_COMPOSER_STATUS_ON = 1; // 0x1 + field public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2; // 0x2 field public static final int CALL_STATE_IDLE = 0; // 0x0 field public static final int CALL_STATE_OFFHOOK = 2; // 0x2 field public static final int CALL_STATE_RINGING = 1; // 0x1 @@ -41489,6 +41712,14 @@ package android.telephony.euicc { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.euicc.DownloadableSubscription> CREATOR; } + public static final class DownloadableSubscription.Builder { + ctor public DownloadableSubscription.Builder(@NonNull android.telephony.euicc.DownloadableSubscription); + ctor public DownloadableSubscription.Builder(@NonNull String); + method @NonNull public android.telephony.euicc.DownloadableSubscription build(); + method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(@NonNull String); + method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(@NonNull String); + } + public final class EuiccInfo implements android.os.Parcelable { ctor public EuiccInfo(@Nullable String); method public int describeContents(); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 9349770ee4fd..854e8fd8a6f6 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -10,6 +10,31 @@ package android.app { package android.net { + public final class ConnectivityFrameworkInitializer { + method public static void registerServiceWrappers(); + } + + public class ConnectivityManager { + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); + } + + public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { + method public int getResourceId(); + } + + public final class NetworkAgentConfig implements android.os.Parcelable { + method @Nullable public String getSubscriberId(); + } + + public static final class NetworkAgentConfig.Builder { + method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String); + } + + public final class NetworkCapabilities implements android.os.Parcelable { + field public static final int TRANSPORT_TEST = 7; // 0x7 + } + public final class TcpRepairWindow { ctor public TcpRepairWindow(int, int, int, int, int, int); field public final int maxWindow; @@ -20,6 +45,22 @@ package android.net { field public final int sndWnd; } + public final class TestNetworkInterface implements android.os.Parcelable { + ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String); + method public int describeContents(); + method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor(); + method @NonNull public String getInterfaceName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR; + } + + public class TestNetworkManager { + method @NonNull public android.net.TestNetworkInterface createTapInterface(); + method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>); + method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder); + method public void teardownTestNetwork(@NonNull android.net.Network); + } + } package android.os { @@ -28,6 +69,10 @@ package android.os { method public final void markVintfStability(); } + public static class Build.VERSION { + field public static final int FIRST_SDK_INT; + } + public interface Parcelable { method public default int getStability(); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 8b15c64a982c..72fcc72169c3 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -44,6 +44,7 @@ package android { field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"; field public static final String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"; field public static final String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE"; + field public static final String BIND_RESUME_ON_REBOOT_SERVICE = "android.permission.BIND_RESUME_ON_REBOOT_SERVICE"; field public static final String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"; field public static final String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"; field public static final String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE"; @@ -126,6 +127,7 @@ package android { field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY"; field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER"; field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS"; + field public static final String MANAGE_TEST_NETWORKS = "android.permission.MANAGE_TEST_NETWORKS"; field public static final String MANAGE_USB = "android.permission.MANAGE_USB"; field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS"; field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE"; @@ -199,6 +201,7 @@ package android { field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES"; field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE"; field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD"; + field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM"; field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS"; field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS"; field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT"; @@ -1409,11 +1412,28 @@ package android.app.usage { } +package android.apphibernation { + + public final class AppHibernationManager { + method public boolean isHibernatingForUser(@NonNull String); + method public boolean isHibernatingGlobally(@NonNull String); + method public void setHibernatingForUser(@NonNull String, boolean); + method public void setHibernatingGlobally(@NonNull String, boolean); + } + +} + package android.bluetooth { public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile { + method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public android.bluetooth.BufferConstraints getBufferConstraints(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferMillis(int, int); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); + field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1 + field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2 + field public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0; // 0x0 field public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; // 0x0 field public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; // 0x0 field public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; // 0x1 @@ -1595,6 +1615,25 @@ package android.bluetooth { field public static final int UUID_BYTES_32_BIT = 4; // 0x4 } + public final class BufferConstraint implements android.os.Parcelable { + ctor public BufferConstraint(int, int, int); + method public int describeContents(); + method public int getDefaultMillis(); + method public int getMaxMillis(); + method public int getMinMillis(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraint> CREATOR; + } + + public final class BufferConstraints implements android.os.Parcelable { + ctor public BufferConstraints(@NonNull java.util.List<android.bluetooth.BufferConstraint>); + method public int describeContents(); + method @Nullable public android.bluetooth.BufferConstraint getCodec(int); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int BUFFER_CODEC_MAX_NUM = 32; // 0x20 + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR; + } + } package android.bluetooth.le { @@ -1680,11 +1719,11 @@ package android.content { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle); method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle); + field public static final String APP_HIBERNATION_SERVICE = "app_hibernation"; field public static final String APP_INTEGRITY_SERVICE = "app_integrity"; field public static final String APP_PREDICTION_SERVICE = "app_prediction"; field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; - field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; field public static final String ETHERNET_SERVICE = "ethernet"; @@ -5965,6 +6004,7 @@ package android.net { method public long getExpiryTimeMillis(); method public long getRefreshTimeMillis(); method @Nullable public android.net.Uri getUserPortalUrl(); + method @Nullable public String getVenueFriendlyName(); method @Nullable public android.net.Uri getVenueInfoUrl(); method public boolean isCaptive(); method public boolean isSessionExtendable(); @@ -5982,6 +6022,7 @@ package android.net { method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); } @@ -5992,6 +6033,7 @@ package android.net { method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); + method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor); method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); @@ -6001,6 +6043,7 @@ package android.net { method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider); + method public void unregisterQosCallback(@NonNull android.net.QosCallback); method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; @@ -6194,6 +6237,8 @@ package android.net { method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData); method public void onAutomaticReconnectDisabled(); method public void onNetworkUnwanted(); + method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter); + method public void onQosCallbackUnregistered(int); method public void onRemoveKeepalivePacketFilter(int); method public void onSaveAcceptUnvalidated(boolean); method public void onSignalStrengthThresholdsUpdated(@NonNull int[]); @@ -6204,6 +6249,9 @@ package android.net { method public final void sendLinkProperties(@NonNull android.net.LinkProperties); method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); method public final void sendNetworkScore(@IntRange(from=0, to=99) int); + method public final void sendQosCallbackError(int, int); + method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes); + method public final void sendQosSessionLost(int, int); method public final void sendSocketKeepaliveEvent(int, int); method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); method public void unregister(); @@ -6233,13 +6281,16 @@ package android.net { } public final class NetworkCapabilities implements android.os.Parcelable { + ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean); method @NonNull public int[] getAdministratorUids(); method @Nullable public String getSsid(); method @NonNull public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); + field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18 + field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b } public static final class NetworkCapabilities.Builder { @@ -6288,6 +6339,9 @@ package android.net { method public abstract void onRequestScores(android.net.NetworkKey[]); } + public class NetworkReleasedException extends java.lang.Exception { + } + public class NetworkRequest implements android.os.Parcelable { method @Nullable public String getRequestorPackageName(); method public int getRequestorUid(); @@ -6360,6 +6414,47 @@ package android.net { ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long); } + public abstract class QosCallback { + ctor public QosCallback(); + method public void onError(@NonNull android.net.QosCallbackException); + method public void onQosSessionAvailable(@NonNull android.net.QosSession, @NonNull android.net.QosSessionAttributes); + method public void onQosSessionLost(@NonNull android.net.QosSession); + } + + public static class QosCallback.QosCallbackRegistrationException extends java.lang.RuntimeException { + } + + public final class QosCallbackException extends java.lang.Exception { + } + + public abstract class QosFilter { + method @NonNull public abstract android.net.Network getNetwork(); + method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int); + } + + public final class QosSession implements android.os.Parcelable { + ctor public QosSession(int, int); + method public int describeContents(); + method public int getSessionId(); + method public int getSessionType(); + method public long getUniqueId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSession> CREATOR; + field public static final int TYPE_EPS_BEARER = 1; // 0x1 + } + + public interface QosSessionAttributes { + } + + public final class QosSocketInfo implements android.os.Parcelable { + ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.Socket) throws java.io.IOException; + method public int describeContents(); + method @NonNull public java.net.InetSocketAddress getLocalSocketAddress(); + method @NonNull public android.net.Network getNetwork(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR; + } + public final class RouteInfo implements android.os.Parcelable { ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int); ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int); @@ -6405,6 +6500,12 @@ package android.net { field public static final int SUCCESS = 0; // 0x0 } + public class SocketLocalAddressChangedException extends java.lang.Exception { + } + + public class SocketNotBoundException extends java.lang.Exception { + } + public final class StaticIpConfiguration implements android.os.Parcelable { ctor public StaticIpConfiguration(); ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); @@ -6454,6 +6555,11 @@ package android.net { field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00 } + public interface TransportInfo { + method public default boolean hasLocationSensitiveFields(); + method @NonNull public default android.net.TransportInfo makeCopy(boolean); + } + public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method @NonNull public String toSafeString(); } @@ -6496,157 +6602,157 @@ package android.net.apf { package android.net.metrics { - public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event { - } - - public static final class ApfProgramEvent.Builder { - ctor public ApfProgramEvent.Builder(); - method @NonNull public android.net.metrics.ApfProgramEvent build(); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setActualLifetime(long); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setCurrentRas(int); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setFilteredRas(int); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setFlags(boolean, boolean); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setLifetime(long); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setProgramLength(int); - } - - public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event { - } - - public static final class ApfStats.Builder { - ctor public ApfStats.Builder(); - method @NonNull public android.net.metrics.ApfStats build(); - method @NonNull public android.net.metrics.ApfStats.Builder setDroppedRas(int); - method @NonNull public android.net.metrics.ApfStats.Builder setDurationMs(long); - method @NonNull public android.net.metrics.ApfStats.Builder setMatchingRas(int); - method @NonNull public android.net.metrics.ApfStats.Builder setMaxProgramSize(int); - method @NonNull public android.net.metrics.ApfStats.Builder setParseErrors(int); - method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdates(int); - method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAll(int); - method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAllowingMulticast(int); - method @NonNull public android.net.metrics.ApfStats.Builder setReceivedRas(int); - method @NonNull public android.net.metrics.ApfStats.Builder setZeroLifetimeRas(int); - } - - public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event { - } - - public static final class DhcpClientEvent.Builder { - ctor public DhcpClientEvent.Builder(); - method @NonNull public android.net.metrics.DhcpClientEvent build(); - method @NonNull public android.net.metrics.DhcpClientEvent.Builder setDurationMs(int); - method @NonNull public android.net.metrics.DhcpClientEvent.Builder setMsg(String); - } - - public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event { - ctor public DhcpErrorEvent(int); - method public static int errorCodeWithOption(int, int); - field public static final int BOOTP_TOO_SHORT = 67174400; // 0x4010000 - field public static final int BUFFER_UNDERFLOW = 83951616; // 0x5010000 - field public static final int DHCP_BAD_MAGIC_COOKIE = 67239936; // 0x4020000 - field public static final int DHCP_ERROR = 4; // 0x4 - field public static final int DHCP_INVALID_OPTION_LENGTH = 67305472; // 0x4030000 - field public static final int DHCP_NO_COOKIE = 67502080; // 0x4060000 - field public static final int DHCP_NO_MSG_TYPE = 67371008; // 0x4040000 - field public static final int DHCP_UNKNOWN_MSG_TYPE = 67436544; // 0x4050000 - field public static final int L2_ERROR = 1; // 0x1 - field public static final int L2_TOO_SHORT = 16842752; // 0x1010000 - field public static final int L2_WRONG_ETH_TYPE = 16908288; // 0x1020000 - field public static final int L3_ERROR = 2; // 0x2 - field public static final int L3_INVALID_IP = 33751040; // 0x2030000 - field public static final int L3_NOT_IPV4 = 33685504; // 0x2020000 - field public static final int L3_TOO_SHORT = 33619968; // 0x2010000 - field public static final int L4_ERROR = 3; // 0x3 - field public static final int L4_NOT_UDP = 50397184; // 0x3010000 - field public static final int L4_WRONG_PORT = 50462720; // 0x3020000 - field public static final int MISC_ERROR = 5; // 0x5 - field public static final int PARSING_ERROR = 84082688; // 0x5030000 - field public static final int RECEIVE_ERROR = 84017152; // 0x5020000 - } - - public class IpConnectivityLog { - ctor public IpConnectivityLog(); - method public boolean log(long, @NonNull android.net.metrics.IpConnectivityLog.Event); - method public boolean log(@NonNull String, @NonNull android.net.metrics.IpConnectivityLog.Event); - method public boolean log(@NonNull android.net.Network, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event); - method public boolean log(int, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event); - method public boolean log(@NonNull android.net.metrics.IpConnectivityLog.Event); - } - - public static interface IpConnectivityLog.Event extends android.os.Parcelable { - } - - public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event { - ctor public IpManagerEvent(int, long); - field public static final int COMPLETE_LIFECYCLE = 3; // 0x3 - field public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8 - field public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7 - field public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; // 0x6 - field public static final int ERROR_STARTING_IPV4 = 4; // 0x4 - field public static final int ERROR_STARTING_IPV6 = 5; // 0x5 - field public static final int PROVISIONING_FAIL = 2; // 0x2 - field public static final int PROVISIONING_OK = 1; // 0x1 - } - - public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event { - ctor public IpReachabilityEvent(int); - field public static final int NUD_FAILED = 512; // 0x200 - field public static final int NUD_FAILED_ORGANIC = 1024; // 0x400 - field public static final int PROBE = 256; // 0x100 - field public static final int PROVISIONING_LOST = 768; // 0x300 - field public static final int PROVISIONING_LOST_ORGANIC = 1280; // 0x500 - } - - public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event { - ctor public NetworkEvent(int, long); - ctor public NetworkEvent(int); - field public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4 - field public static final int NETWORK_CONNECTED = 1; // 0x1 - field public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc - field public static final int NETWORK_DISCONNECTED = 7; // 0x7 - field public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa - field public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8 - field public static final int NETWORK_LINGER = 5; // 0x5 - field public static final int NETWORK_PARTIAL_CONNECTIVITY = 13; // 0xd - field public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb - field public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9 - field public static final int NETWORK_UNLINGER = 6; // 0x6 - field public static final int NETWORK_VALIDATED = 2; // 0x2 - field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3 - } - - public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event { - } - - public static final class RaEvent.Builder { - ctor public RaEvent.Builder(); - method @NonNull public android.net.metrics.RaEvent build(); - method @NonNull public android.net.metrics.RaEvent.Builder updateDnsslLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updatePrefixPreferredLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updatePrefixValidLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updateRdnssLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updateRouteInfoLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updateRouterLifetime(long); - } - - public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event { - method @NonNull public static String getProbeName(int); - field public static final int DNS_FAILURE = 0; // 0x0 - field public static final int DNS_SUCCESS = 1; // 0x1 - field public static final int PROBE_DNS = 0; // 0x0 - field public static final int PROBE_FALLBACK = 4; // 0x4 - field public static final int PROBE_HTTP = 1; // 0x1 - field public static final int PROBE_HTTPS = 2; // 0x2 - field public static final int PROBE_PAC = 3; // 0x3 - field public static final int PROBE_PRIVDNS = 5; // 0x5 - } - - public static final class ValidationProbeEvent.Builder { - ctor public ValidationProbeEvent.Builder(); - method @NonNull public android.net.metrics.ValidationProbeEvent build(); - method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setDurationMs(long); - method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setProbeType(int, boolean); - method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setReturnCode(int); + @Deprecated public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event { + } + + @Deprecated public static final class ApfProgramEvent.Builder { + ctor @Deprecated public ApfProgramEvent.Builder(); + method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent build(); + method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setActualLifetime(long); + method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setCurrentRas(int); + method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setFilteredRas(int); + method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setFlags(boolean, boolean); + method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setLifetime(long); + method @Deprecated @NonNull public android.net.metrics.ApfProgramEvent.Builder setProgramLength(int); + } + + @Deprecated public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event { + } + + @Deprecated public static final class ApfStats.Builder { + ctor @Deprecated public ApfStats.Builder(); + method @Deprecated @NonNull public android.net.metrics.ApfStats build(); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setDroppedRas(int); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setDurationMs(long); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setMatchingRas(int); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setMaxProgramSize(int); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setParseErrors(int); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdates(int); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAll(int); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAllowingMulticast(int); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setReceivedRas(int); + method @Deprecated @NonNull public android.net.metrics.ApfStats.Builder setZeroLifetimeRas(int); + } + + @Deprecated public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event { + } + + @Deprecated public static final class DhcpClientEvent.Builder { + ctor @Deprecated public DhcpClientEvent.Builder(); + method @Deprecated @NonNull public android.net.metrics.DhcpClientEvent build(); + method @Deprecated @NonNull public android.net.metrics.DhcpClientEvent.Builder setDurationMs(int); + method @Deprecated @NonNull public android.net.metrics.DhcpClientEvent.Builder setMsg(String); + } + + @Deprecated public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event { + ctor @Deprecated public DhcpErrorEvent(int); + method @Deprecated public static int errorCodeWithOption(int, int); + field @Deprecated public static final int BOOTP_TOO_SHORT = 67174400; // 0x4010000 + field @Deprecated public static final int BUFFER_UNDERFLOW = 83951616; // 0x5010000 + field @Deprecated public static final int DHCP_BAD_MAGIC_COOKIE = 67239936; // 0x4020000 + field @Deprecated public static final int DHCP_ERROR = 4; // 0x4 + field @Deprecated public static final int DHCP_INVALID_OPTION_LENGTH = 67305472; // 0x4030000 + field @Deprecated public static final int DHCP_NO_COOKIE = 67502080; // 0x4060000 + field @Deprecated public static final int DHCP_NO_MSG_TYPE = 67371008; // 0x4040000 + field @Deprecated public static final int DHCP_UNKNOWN_MSG_TYPE = 67436544; // 0x4050000 + field @Deprecated public static final int L2_ERROR = 1; // 0x1 + field @Deprecated public static final int L2_TOO_SHORT = 16842752; // 0x1010000 + field @Deprecated public static final int L2_WRONG_ETH_TYPE = 16908288; // 0x1020000 + field @Deprecated public static final int L3_ERROR = 2; // 0x2 + field @Deprecated public static final int L3_INVALID_IP = 33751040; // 0x2030000 + field @Deprecated public static final int L3_NOT_IPV4 = 33685504; // 0x2020000 + field @Deprecated public static final int L3_TOO_SHORT = 33619968; // 0x2010000 + field @Deprecated public static final int L4_ERROR = 3; // 0x3 + field @Deprecated public static final int L4_NOT_UDP = 50397184; // 0x3010000 + field @Deprecated public static final int L4_WRONG_PORT = 50462720; // 0x3020000 + field @Deprecated public static final int MISC_ERROR = 5; // 0x5 + field @Deprecated public static final int PARSING_ERROR = 84082688; // 0x5030000 + field @Deprecated public static final int RECEIVE_ERROR = 84017152; // 0x5020000 + } + + @Deprecated public class IpConnectivityLog { + ctor @Deprecated public IpConnectivityLog(); + method @Deprecated public boolean log(long, @NonNull android.net.metrics.IpConnectivityLog.Event); + method @Deprecated public boolean log(@NonNull String, @NonNull android.net.metrics.IpConnectivityLog.Event); + method @Deprecated public boolean log(@NonNull android.net.Network, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event); + method @Deprecated public boolean log(int, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event); + method @Deprecated public boolean log(@NonNull android.net.metrics.IpConnectivityLog.Event); + } + + @Deprecated public static interface IpConnectivityLog.Event extends android.os.Parcelable { + } + + @Deprecated public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event { + ctor @Deprecated public IpManagerEvent(int, long); + field @Deprecated public static final int COMPLETE_LIFECYCLE = 3; // 0x3 + field @Deprecated public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8 + field @Deprecated public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7 + field @Deprecated public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; // 0x6 + field @Deprecated public static final int ERROR_STARTING_IPV4 = 4; // 0x4 + field @Deprecated public static final int ERROR_STARTING_IPV6 = 5; // 0x5 + field @Deprecated public static final int PROVISIONING_FAIL = 2; // 0x2 + field @Deprecated public static final int PROVISIONING_OK = 1; // 0x1 + } + + @Deprecated public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event { + ctor @Deprecated public IpReachabilityEvent(int); + field @Deprecated public static final int NUD_FAILED = 512; // 0x200 + field @Deprecated public static final int NUD_FAILED_ORGANIC = 1024; // 0x400 + field @Deprecated public static final int PROBE = 256; // 0x100 + field @Deprecated public static final int PROVISIONING_LOST = 768; // 0x300 + field @Deprecated public static final int PROVISIONING_LOST_ORGANIC = 1280; // 0x500 + } + + @Deprecated public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event { + ctor @Deprecated public NetworkEvent(int, long); + ctor @Deprecated public NetworkEvent(int); + field @Deprecated public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4 + field @Deprecated public static final int NETWORK_CONNECTED = 1; // 0x1 + field @Deprecated public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc + field @Deprecated public static final int NETWORK_DISCONNECTED = 7; // 0x7 + field @Deprecated public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa + field @Deprecated public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8 + field @Deprecated public static final int NETWORK_LINGER = 5; // 0x5 + field @Deprecated public static final int NETWORK_PARTIAL_CONNECTIVITY = 13; // 0xd + field @Deprecated public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb + field @Deprecated public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9 + field @Deprecated public static final int NETWORK_UNLINGER = 6; // 0x6 + field @Deprecated public static final int NETWORK_VALIDATED = 2; // 0x2 + field @Deprecated public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3 + } + + @Deprecated public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event { + } + + @Deprecated public static final class RaEvent.Builder { + ctor @Deprecated public RaEvent.Builder(); + method @Deprecated @NonNull public android.net.metrics.RaEvent build(); + method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updateDnsslLifetime(long); + method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updatePrefixPreferredLifetime(long); + method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updatePrefixValidLifetime(long); + method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updateRdnssLifetime(long); + method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updateRouteInfoLifetime(long); + method @Deprecated @NonNull public android.net.metrics.RaEvent.Builder updateRouterLifetime(long); + } + + @Deprecated public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event { + method @Deprecated @NonNull public static String getProbeName(int); + field @Deprecated public static final int DNS_FAILURE = 0; // 0x0 + field @Deprecated public static final int DNS_SUCCESS = 1; // 0x1 + field @Deprecated public static final int PROBE_DNS = 0; // 0x0 + field @Deprecated public static final int PROBE_FALLBACK = 4; // 0x4 + field @Deprecated public static final int PROBE_HTTP = 1; // 0x1 + field @Deprecated public static final int PROBE_HTTPS = 2; // 0x2 + field @Deprecated public static final int PROBE_PAC = 3; // 0x3 + field @Deprecated public static final int PROBE_PRIVDNS = 5; // 0x5 + } + + @Deprecated public static final class ValidationProbeEvent.Builder { + ctor @Deprecated public ValidationProbeEvent.Builder(); + method @Deprecated @NonNull public android.net.metrics.ValidationProbeEvent build(); + method @Deprecated @NonNull public android.net.metrics.ValidationProbeEvent.Builder setDurationMs(long); + method @Deprecated @NonNull public android.net.metrics.ValidationProbeEvent.Builder setProbeType(int, boolean); + method @Deprecated @NonNull public android.net.metrics.ValidationProbeEvent.Builder setReturnCode(int); } } @@ -6917,7 +7023,10 @@ package android.nfc { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable(); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush(); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnEnabled(); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAlwaysOnSupported(); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setAlwaysOn(boolean); method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int); field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1 } @@ -7037,24 +7146,10 @@ package android.os { } public final class BugreportManager { - method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport(); method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence); method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); } - public abstract static class BugreportManager.BugreportCallback { - ctor public BugreportManager.BugreportCallback(); - method public void onEarlyReportFinished(); - method public void onError(int); - method public void onFinished(); - method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float); - field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5 - field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1 - field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2 - field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4 - field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3 - } - public final class BugreportParams { ctor public BugreportParams(int); method public int getMode(); @@ -7507,11 +7602,13 @@ package android.os { } public class UserManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean canHaveRestrictedProfile(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException; method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles(); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getRestrictedProfileParent(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType(); @@ -7917,6 +8014,7 @@ package android.provider { field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager"; field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot"; field public static final String NAMESPACE_APP_COMPAT = "app_compat"; + field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation"; field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service"; field public static final String NAMESPACE_AUTOFILL = "autofill"; field public static final String NAMESPACE_BIOMETRICS = "biometrics"; @@ -8980,6 +9078,18 @@ package android.service.resolver { } +package android.service.resumeonreboot { + + public abstract class ResumeOnRebootService extends android.app.Service { + ctor public ResumeOnRebootService(); + method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent); + method @NonNull public abstract byte[] onUnwrap(@NonNull byte[]) throws java.io.IOException; + method @NonNull public abstract byte[] onWrap(@NonNull byte[], long) throws java.io.IOException; + field public static final String SERVICE_INTERFACE = "android.service.resumeonreboot.ResumeOnRebootService"; + } + +} + package android.service.settings.suggestions { public final class Suggestion implements android.os.Parcelable { @@ -9828,17 +9938,87 @@ package android.telephony { method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber); method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); - method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); method public void onRadioPowerStateChanged(int); method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 - field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000 - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17 + field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa + field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4 + field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6 + field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11 + field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb + field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5 + field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13 + field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; // 0xe + field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22 + field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15 + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c + field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; // 0x3 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_OEM_HOOK_RAW = 15; // 0xf + field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; // 0x1d + field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; // 0x1e + field public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; // 0x16 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; // 0x21 + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; // 0xc + field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; // 0xd + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; // 0x18 + field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_REGISTRATION_FAILURE = 31; // 0x1f + field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1 + field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9 + field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10 + field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14 + field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000 + field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 + } + + public static interface PhoneStateListener.CallAttributesChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); + } + + public static interface PhoneStateListener.DataEnabledChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); + } + + public static interface PhoneStateListener.OutgoingEmergencyCallListener { + method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); + } + + public static interface PhoneStateListener.OutgoingEmergencySmsListener { + method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); + } + + public static interface PhoneStateListener.PhysicalChannelConfigChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPhysicalChannelConfigChanged(@NonNull java.util.List<android.telephony.PhysicalChannelConfig>); + } + + public static interface PhoneStateListener.PreciseCallStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + } + + public static interface PhoneStateListener.RadioPowerStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int); + } + + public static interface PhoneStateListener.SrvccStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int); + } + + public static interface PhoneStateListener.VoiceActivationStateChangedListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int); } public final class PinResult implements android.os.Parcelable { @@ -10274,6 +10454,7 @@ package android.telephony { method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String); + method public boolean isRadioInterfaceCapabilitySupported(@NonNull String); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired(); @@ -10347,6 +10528,7 @@ package android.telephony { field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1 field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4 field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3 + field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE"; field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 @@ -10568,6 +10750,7 @@ package android.telephony.data { method public int getPduSessionId(); method public int getProtocolType(); method public long getRetryDurationMillis(); + method @Nullable public android.telephony.data.SliceInfo getSliceInfo(); method @Deprecated public int getSuggestedRetryTime(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; @@ -10602,6 +10785,7 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long); + method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo); method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); } @@ -10674,7 +10858,7 @@ package android.telephony.data { method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback); - method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @NonNull android.telephony.data.DataServiceCallback); + method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @NonNull android.telephony.data.DataServiceCallback); method public void startHandover(int, @NonNull android.telephony.data.DataServiceCallback); } @@ -10695,6 +10879,19 @@ package android.telephony.data { field public static final int RESULT_SUCCESS = 0; // 0x0 } + public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes { + method @NonNull public static android.telephony.data.EpsBearerQosSessionAttributes create(@NonNull android.os.Parcel); + method public int describeContents(); + method public long getGuaranteedDownlinkBitRate(); + method public long getGuaranteedUplinkBitRate(); + method public long getMaxDownlinkBitRate(); + method public long getMaxUplinkBitRate(); + method public int getQci(); + method @NonNull public java.util.List<java.net.InetSocketAddress> getRemoteAddresses(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR; + } + public abstract class QualifiedNetworksService extends android.app.Service { ctor public QualifiedNetworksService(); method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int); @@ -10709,6 +10906,32 @@ package android.telephony.data { method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>); } + public final class SliceInfo implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator(); + method public int getMappedHplmnSliceServiceType(); + method @IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator(); + method public int getSliceServiceType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SliceInfo> CREATOR; + field public static final int MAX_SLICE_DIFFERENTIATOR = 16777214; // 0xfffffe + field public static final int MIN_SLICE_DIFFERENTIATOR = -1; // 0xffffffff + field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff + field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1 + field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3 + field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0 + field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2 + } + + public static final class SliceInfo.Builder { + ctor public SliceInfo.Builder(); + method @NonNull public android.telephony.data.SliceInfo build(); + method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int); + method @NonNull public android.telephony.data.SliceInfo.Builder setMappedHplmnSliceServiceType(int); + method @NonNull public android.telephony.data.SliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.SliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.SliceInfo.MAX_SLICE_DIFFERENTIATOR) int); + method @NonNull public android.telephony.data.SliceInfo.Builder setSliceServiceType(int); + } + } package android.telephony.euicc { @@ -10720,12 +10943,8 @@ package android.telephony.euicc { public static final class DownloadableSubscription.Builder { ctor public DownloadableSubscription.Builder(); - ctor public DownloadableSubscription.Builder(android.telephony.euicc.DownloadableSubscription); - method public android.telephony.euicc.DownloadableSubscription build(); - method public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(java.util.List<android.telephony.UiccAccessRule>); - method public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(String); - method public android.telephony.euicc.DownloadableSubscription.Builder setConfirmationCode(String); - method public android.telephony.euicc.DownloadableSubscription.Builder setEncodedActivationCode(String); + method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setAccessRules(@NonNull java.util.List<android.telephony.UiccAccessRule>); + method @NonNull public android.telephony.euicc.DownloadableSubscription.Builder setCarrierName(@NonNull String); } public class EuiccCardManager { @@ -11595,8 +11814,140 @@ package android.telephony.ims { field public static final String RCS_PROFILE_2_3 = "UP_2.3"; } + public final class RcsContactPresenceTuple implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.net.Uri getContactUri(); + method @Nullable public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities getServiceCapabilities(); + method @Nullable public String getServiceDescription(); + method @NonNull public String getServiceId(); + method @NonNull public String getServiceVersion(); + method @NonNull public String getStatus(); + method @Nullable public String getTimestamp(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR; + field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer"; + field public static final String SERVICE_ID_CHATBOT = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot"; + field public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot"; + field public static final String SERVICE_ID_CHATBOT_STANDALONE = " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa"; + field public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session"; + field public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession"; + field public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP"; + field public static final String SERVICE_ID_FT_OVER_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms"; + field public static final String SERVICE_ID_GEO_PUSH = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush"; + field public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms"; + field public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; + field public static final String SERVICE_ID_POST_CALL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered"; + field public static final String SERVICE_ID_SHARED_MAP = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap"; + field public static final String SERVICE_ID_SHARED_SKETCH = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch"; + field public static final String TUPLE_BASIC_STATUS_CLOSED = "closed"; + field public static final String TUPLE_BASIC_STATUS_OPEN = "open"; + } + + public static final class RcsContactPresenceTuple.Builder { + ctor public RcsContactPresenceTuple.Builder(@NonNull String, @NonNull String, @NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple build(); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String); + } + + public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getSupportedDuplexModes(); + method @NonNull public java.util.List<java.lang.String> getUnsupportedDuplexModes(); + method public boolean isAudioCapable(); + method public boolean isVideoCapable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities> CREATOR; + field public static final String DUPLEX_MODE_FULL = "full"; + field public static final String DUPLEX_MODE_HALF = "half"; + field public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only"; + field public static final String DUPLEX_MODE_SEND_ONLY = "send-only"; + } + + public static final class RcsContactPresenceTuple.ServiceCapabilities.Builder { + ctor public RcsContactPresenceTuple.ServiceCapabilities.Builder(boolean, boolean); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addSupportedDuplexMode(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addUnsupportedDuplexMode(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities build(); + } + + public final class RcsContactUceCapability implements android.os.Parcelable { + method public int describeContents(); + method public int getCapabilityMechanism(); + method @Nullable public android.telephony.ims.RcsContactPresenceTuple getCapabilityTuple(@NonNull String); + method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getCapabilityTuples(); + method @NonNull public android.net.Uri getContactUri(); + method public int getRequestResult(); + method public int getSourceType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPABILITY_MECHANISM_OPTIONS = 2; // 0x2 + field public static final int CAPABILITY_MECHANISM_PRESENCE = 1; // 0x1 + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR; + field public static final int REQUEST_RESULT_FOUND = 3; // 0x3 + field public static final int REQUEST_RESULT_NOT_FOUND = 2; // 0x2 + field public static final int REQUEST_RESULT_NOT_ONLINE = 1; // 0x1 + field public static final int REQUEST_RESULT_UNKNOWN = 0; // 0x0 + field public static final int SOURCE_TYPE_CACHED = 1; // 0x1 + field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0 + } + + public static final class RcsContactUceCapability.PresenceBuilder { + ctor public RcsContactUceCapability.PresenceBuilder(@NonNull android.net.Uri, int, int); + method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuple(@NonNull android.telephony.ims.RcsContactPresenceTuple); + method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuples(@NonNull java.util.List<android.telephony.ims.RcsContactPresenceTuple>); + method @NonNull public android.telephony.ims.RcsContactUceCapability build(); + } + public class RcsUceAdapter { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; + field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 + field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; // 0x5 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; // 0x9 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; // 0x2 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; // 0x3 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8 + field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0 + field public static final int ERROR_FORBIDDEN = 6; // 0x6 + field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1 + field public static final int ERROR_INSUFFICIENT_MEMORY = 10; // 0xa + field public static final int ERROR_LOST_NETWORK = 11; // 0xb + field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5 + field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3 + field public static final int ERROR_NOT_ENABLED = 2; // 0x2 + field public static final int ERROR_NOT_FOUND = 7; // 0x7 + field public static final int ERROR_NOT_REGISTERED = 4; // 0x4 + field public static final int ERROR_REQUEST_TIMEOUT = 9; // 0x9 + field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8 + field public static final int ERROR_SERVER_UNAVAILABLE = 12; // 0xc + field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2 + field public static final int PUBLISH_STATE_OK = 1; // 0x1 + field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6 + field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4 + field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5 + field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3 + } + + public static interface RcsUceAdapter.CapabilitiesCallback { + method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>); + method public void onComplete(); + method public void onError(int, long); + } + + public static interface RcsUceAdapter.OnPublishStateChangedListener { + method public void onPublishStateChange(int); } public final class RtpHeaderExtension implements android.os.Parcelable { @@ -11638,6 +11989,7 @@ package android.telephony.ims { field public static final String IPTYPE_IPV6 = "IPV6"; field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; @@ -11670,6 +12022,7 @@ package android.telephony.ims { field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; field public static final String SIP_TRANSPORT_TCP = "TCP"; field public static final String SIP_TRANSPORT_UDP = "UDP"; } @@ -11716,6 +12069,7 @@ package android.telephony.ims { ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]); method public int describeContents(); method @NonNull public byte[] getContent(); + method @NonNull public byte[] getEncodedMessage(); method @NonNull public String getHeaderSection(); method @NonNull public String getStartLine(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -11803,16 +12157,24 @@ package android.telephony.ims.feature { } public class RcsFeature extends android.telephony.ims.feature.ImsFeature { - ctor public RcsFeature(); + ctor @Deprecated public RcsFeature(); + ctor public RcsFeature(@NonNull java.util.concurrent.Executor); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener); method public void onFeatureReady(); method public void onFeatureRemoved(); + method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase); } } package android.telephony.ims.stub { + public interface CapabilityExchangeEventListener { + method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException; + method public void onUnpublish() throws android.telephony.ims.ImsException; + } + public interface DelegateConnectionMessageCallback { method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage); method public void onMessageSendFailure(@NonNull String, int); @@ -11993,6 +12355,36 @@ package android.telephony.ims.stub { method public int updateColr(int); } + public class RcsCapabilityExchangeImplBase { + ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor); + method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback); + method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback); + field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3 + field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1 + field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5 + field public static final int COMMAND_CODE_INVALID_PARAM = 2; // 0x2 + field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; // 0x6 + field public static final int COMMAND_CODE_NOT_FOUND = 8; // 0x8 + field public static final int COMMAND_CODE_NOT_SUPPORTED = 7; // 0x7 + field public static final int COMMAND_CODE_NO_CHANGE = 10; // 0xa + field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4; // 0x4 + field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9; // 0x9 + field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0 + } + + public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback { + method public void onCommandError(int) throws android.telephony.ims.ImsException; + method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException; + } + + public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback { + method public void onCommandError(int) throws android.telephony.ims.ImsException; + method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException; + method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException; + method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException; + method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException; + } + public interface SipDelegate { method public void closeDialog(@NonNull String); method public void notifyMessageReceiveError(@NonNull String, int); @@ -12124,6 +12516,164 @@ package android.util { } +package android.uwb { + + public final class AngleMeasurement implements android.os.Parcelable { + method public int describeContents(); + method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel(); + method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians(); + method @FloatRange(from=-3.141592653589793, to=3.141592653589793) public double getRadians(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleMeasurement> CREATOR; + } + + public static final class AngleMeasurement.Builder { + ctor public AngleMeasurement.Builder(); + method @NonNull public android.uwb.AngleMeasurement build(); + method @NonNull public android.uwb.AngleMeasurement.Builder setConfidenceLevel(double); + method @NonNull public android.uwb.AngleMeasurement.Builder setErrorRadians(double); + method @NonNull public android.uwb.AngleMeasurement.Builder setRadians(double); + } + + public final class AngleOfArrivalMeasurement implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.uwb.AngleMeasurement getAltitude(); + method @NonNull public android.uwb.AngleMeasurement getAzimuth(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleOfArrivalMeasurement> CREATOR; + } + + public static final class AngleOfArrivalMeasurement.Builder { + ctor public AngleOfArrivalMeasurement.Builder(); + method @NonNull public android.uwb.AngleOfArrivalMeasurement build(); + method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAltitude(@NonNull android.uwb.AngleMeasurement); + method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAzimuth(@NonNull android.uwb.AngleMeasurement); + } + + public final class DistanceMeasurement implements android.os.Parcelable { + method public int describeContents(); + method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel(); + method public double getErrorMeters(); + method public double getMeters(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.uwb.DistanceMeasurement> CREATOR; + } + + public static final class DistanceMeasurement.Builder { + ctor public DistanceMeasurement.Builder(); + method @NonNull public android.uwb.DistanceMeasurement build(); + method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(double); + method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(double); + method @NonNull public android.uwb.DistanceMeasurement.Builder setMeters(double); + } + + public final class RangingMeasurement implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.uwb.AngleOfArrivalMeasurement getAngleOfArrivalMeasurement(); + method @Nullable public android.uwb.DistanceMeasurement getDistanceMeasurement(); + method public long getElapsedRealtimeNanos(); + method @NonNull public android.uwb.UwbAddress getRemoteDeviceAddress(); + method public int getStatus(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingMeasurement> CREATOR; + field public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1; // 0x1 + field public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1; // 0xffffffff + field public static final int RANGING_STATUS_SUCCESS = 0; // 0x0 + } + + public static final class RangingMeasurement.Builder { + ctor public RangingMeasurement.Builder(); + method @NonNull public android.uwb.RangingMeasurement build(); + method @NonNull public android.uwb.RangingMeasurement.Builder setAngleOfArrivalMeasurement(@NonNull android.uwb.AngleOfArrivalMeasurement); + method @NonNull public android.uwb.RangingMeasurement.Builder setDistanceMeasurement(@NonNull android.uwb.DistanceMeasurement); + method @NonNull public android.uwb.RangingMeasurement.Builder setElapsedRealtimeNanos(long); + method @NonNull public android.uwb.RangingMeasurement.Builder setRemoteDeviceAddress(@NonNull android.uwb.UwbAddress); + method @NonNull public android.uwb.RangingMeasurement.Builder setStatus(int); + } + + public final class RangingReport implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.uwb.RangingMeasurement> getMeasurements(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingReport> CREATOR; + } + + public static final class RangingReport.Builder { + ctor public RangingReport.Builder(); + method @NonNull public android.uwb.RangingReport.Builder addMeasurement(@NonNull android.uwb.RangingMeasurement); + method @NonNull public android.uwb.RangingReport.Builder addMeasurements(@NonNull java.util.List<android.uwb.RangingMeasurement>); + method @NonNull public android.uwb.RangingReport build(); + } + + public final class RangingSession implements java.lang.AutoCloseable { + method public void close(); + method public void reconfigure(@NonNull android.os.PersistableBundle); + method public void start(@NonNull android.os.PersistableBundle); + method public void stop(); + } + + public static interface RangingSession.Callback { + method public void onClosed(int, @NonNull android.os.PersistableBundle); + method public void onOpenFailed(int, @NonNull android.os.PersistableBundle); + method public void onOpened(@NonNull android.uwb.RangingSession); + method public void onReconfigureFailed(int, @NonNull android.os.PersistableBundle); + method public void onReconfigured(@NonNull android.os.PersistableBundle); + method public void onReportReceived(@NonNull android.uwb.RangingReport); + method public void onStartFailed(int, @NonNull android.os.PersistableBundle); + method public void onStarted(@NonNull android.os.PersistableBundle); + method public void onStopFailed(int, @NonNull android.os.PersistableBundle); + method public void onStopped(); + field public static final int REASON_BAD_PARAMETERS = 3; // 0x3 + field public static final int REASON_GENERIC_ERROR = 4; // 0x4 + field public static final int REASON_LOCAL_REQUEST = 1; // 0x1 + field public static final int REASON_MAX_SESSIONS_REACHED = 5; // 0x5 + field public static final int REASON_PROTOCOL_SPECIFIC_ERROR = 7; // 0x7 + field public static final int REASON_REMOTE_REQUEST = 2; // 0x2 + field public static final int REASON_SYSTEM_POLICY = 6; // 0x6 + field public static final int REASON_UNKNOWN = 0; // 0x0 + } + + public final class UwbAddress implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.uwb.UwbAddress fromBytes(@NonNull byte[]); + method public int size(); + method @NonNull public byte[] toBytes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.uwb.UwbAddress> CREATOR; + field public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8; // 0x8 + field public static final int SHORT_ADDRESS_BYTE_LENGTH = 2; // 0x2 + } + + public final class UwbManager { + method public long elapsedRealtimeResolutionNanos(); + method public int getAngleOfArrivalSupport(); + method public int getMaxRemoteDevicesPerInitiatorSession(); + method public int getMaxRemoteDevicesPerResponderSession(); + method public int getMaxSimultaneousSessions(); + method @NonNull public android.os.PersistableBundle getSpecificationInfo(); + method @NonNull public java.util.List<java.lang.Integer> getSupportedChannelNumbers(); + method @NonNull public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices(); + method public boolean isRangingSupported(); + method @NonNull public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback); + method public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback); + method public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback); + field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; // 0x2 + field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; // 0x3 + field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; // 0x4 + field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; // 0x1 + } + + public static interface UwbManager.AdapterStateCallback { + method public void onStateChanged(boolean, int); + field public static final int STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED = 1; // 0x1 + field public static final int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4; // 0x4 + field public static final int STATE_CHANGED_REASON_SESSION_STARTED = 0; // 0x0 + field public static final int STATE_CHANGED_REASON_SYSTEM_BOOT = 3; // 0x3 + field public static final int STATE_CHANGED_REASON_SYSTEM_POLICY = 2; // 0x2 + } + +} + package android.view { public abstract class Window { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9cf9ce45602b..546e72b8f834 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -980,10 +980,6 @@ package android.media.tv { package android.net { - public class ConnectivityManager { - method @RequiresPermission(anyOf={"android.permission.MANAGE_TEST_NETWORKS", android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); - } - public class EthernetManager { method public void setIncludeTestInterfaces(boolean); } @@ -992,31 +988,10 @@ package android.net { field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 } - public final class NetworkCapabilities implements android.os.Parcelable { - method public int[] getCapabilities(); - field public static final int TRANSPORT_TEST = 7; // 0x7 - } - public class NetworkStack { method public static void setServiceForTest(@Nullable android.os.IBinder); } - public final class TestNetworkInterface implements android.os.Parcelable { - ctor public TestNetworkInterface(android.os.ParcelFileDescriptor, String); - method public int describeContents(); - method public android.os.ParcelFileDescriptor getFileDescriptor(); - method public String getInterfaceName(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR; - } - - public class TestNetworkManager { - method public android.net.TestNetworkInterface createTapInterface(); - method public android.net.TestNetworkInterface createTunInterface(@NonNull android.net.LinkAddress[]); - method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder); - method public void teardownTestNetwork(@NonNull android.net.Network); - } - public class TrafficStats { method public static long getLoopbackRxBytes(); method public static long getLoopbackRxPackets(); @@ -1751,7 +1726,6 @@ package android.util { method public static java.util.Map<java.lang.String,java.lang.String> getAllFeatureFlags(); method public static boolean isEnabled(android.content.Context, String); method public static void setEnabled(android.content.Context, String, boolean); - field public static final String DYNAMIC_SYSTEM = "settings_dynamic_system"; field public static final String FFLAG_OVERRIDE_PREFIX = "sys.fflag.override."; field public static final String FFLAG_PREFIX = "sys.fflag."; field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS index c6f42f719caa..a31cfae995b2 100644 --- a/core/java/android/accessibilityservice/OWNERS +++ b/core/java/android/accessibilityservice/OWNERS @@ -1,4 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com -qasid@google.com +ryanlwlin@google.com diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java index fc93f03d76cf..08861d42be39 100644 --- a/core/java/android/annotation/RequiresFeature.java +++ b/core/java/android/annotation/RequiresFeature.java @@ -30,7 +30,6 @@ import java.lang.annotation.Target; * Denotes that the annotated element requires one or more device features. This * is used to auto-generate documentation. * - * @see PackageManager#hasSystemFeature(String) * @hide */ @Retention(SOURCE) @@ -38,8 +37,16 @@ import java.lang.annotation.Target; public @interface RequiresFeature { /** * The name of the device feature that is required. - * - * @see PackageManager#hasSystemFeature(String) */ String value(); + + /** + * Defines the name of the method that should be called to check whether the feature is + * available, using the same signature format as javadoc. The feature checking method can have + * multiple parameters, but the feature name parameter must be of type String and must also be + * the first String-type parameter. + * <p> + * By default, the enforcement is {@link PackageManager#hasSystemFeature(String)}. + */ + String enforcement() default("android.content.pm.PackageManager#hasSystemFeature"); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 23787ebffefb..bfde2d5d3d29 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2284,7 +2284,7 @@ public final class ActivityThread extends ClientTransactionHandler { return null; } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, int flags) { boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index e7b3e14bfda7..69d5c8d0d2ff 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -50,7 +50,7 @@ import java.util.zip.GZIPInputStream; * * <p> * Application process could die for many reasons, for example {@link #REASON_LOW_MEMORY} - * when it was killed by the ystem because it was running low on memory. Reason + * when it was killed by the system because it was running low on memory. Reason * of the death can be retrieved via {@link #getReason}. Besides the reason, there are a few other * auxiliary APIs like {@link #getStatus} and {@link #getImportance} to help the caller with * additional diagnostic information. diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 602b835a03f8..12c9cd90222a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1428,6 +1428,45 @@ class ContextImpl extends Context { } } + /** + * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the + * Intent you are sending stays around after the broadcast is complete, + * so that others can quickly retrieve that data through the return + * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In + * all other ways, this behaves the same as + * {@link #sendBroadcast(Intent)}. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast, and the Intent will be held to + * be re-broadcast to future receivers. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * + * @see #sendBroadcast(Intent) + * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) + */ + @Override + @Deprecated + public void sendStickyBroadcast(@NonNull Intent intent, @Nullable Bundle options) { + warnIfCallingFromSystemProcess(); + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + try { + intent.prepareToLeaveProcess(this); + ActivityManager.getService().broadcastIntentWithFeature( + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, + false, true, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @Override @Deprecated public void sendStickyOrderedBroadcast(Intent intent, diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index be1681bc7cc6..66a832505ead 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -223,7 +223,7 @@ interface IActivityTaskManager { */ IBinder requestStartActivityPermissionToken(in IBinder delegatorToken); - void releaseSomeActivities(in IApplicationThread app); + oneway void releaseSomeActivities(in IApplicationThread app); Bitmap getTaskDescriptionIcon(in String filename, int userId); void registerTaskStackListener(in ITaskStackListener listener); void unregisterTaskStackListener(in ITaskStackListener listener); diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index 06ad9c99c301..e6aa7a77357c 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -2,6 +2,33 @@ # Remain no owner because multiple modules may touch this file. per-file ContextImpl.java = * +# ActivityManager +per-file ActivityManager* = file:/services/core/java/com/android/server/am/OWNERS +per-file ApplicationErrorReport* = file:/services/core/java/com/android/server/am/OWNERS +per-file ApplicationExitInfo* = file:/services/core/java/com/android/server/am/OWNERS +per-file Application.java = file:/services/core/java/com/android/server/am/OWNERS +per-file ApplicationLoaders.java = file:/services/core/java/com/android/server/am/OWNERS +per-file ApplicationThreadConstants.java = file:/services/core/java/com/android/server/am/OWNERS +per-file BroadcastOptions.java = file:/services/core/java/com/android/server/am/OWNERS +per-file ContentProviderHolder* = file:/services/core/java/com/android/server/am/OWNERS +per-file IActivityController.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IAppTraceRetriever.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IInstrumentationWatcher.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IntentService.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IServiceConnection.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IStopUserCallback.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file IUidObserver.aidl = file:/services/core/java/com/android/server/am/OWNERS +per-file LoadedApk.java = file:/services/core/java/com/android/server/am/OWNERS +per-file LocalActivityManager.java = file:/services/core/java/com/android/server/am/OWNERS +per-file PendingIntent* = file:/services/core/java/com/android/server/am/OWNERS +per-file *Process* = file:/services/core/java/com/android/server/am/OWNERS +per-file ProfilerInfo* = file:/services/core/java/com/android/server/am/OWNERS +per-file Service* = file:/services/core/java/com/android/server/am/OWNERS +per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS +per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS + # ActivityThread per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS per-file ActivityThread.java = file:/services/core/java/com/android/server/wm/OWNERS @@ -12,11 +39,30 @@ per-file *Alarm* = file:/apex/jobscheduler/OWNERS # AppOps per-file *AppOp* = file:/core/java/android/permission/OWNERS -# Notification +# Multiuser +per-file *User* = file:/MULTIUSER_OWNERS + +# Notification, DND, Status bar per-file *Notification* = file:/packages/SystemUI/OWNERS +per-file *Zen* = file:/packages/SystemUI/OWNERS +per-file *StatusBar* = file:/packages/SystemUI/OWNERS + +# PackageManager +per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file InstantAppResolverService.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file LoadedApk.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file PackageDeleteObserver.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file PackageInstallObserver.java = file:/services/core/java/com/android/server/pm/OWNERS +per-file EphemeralResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS +per-file IEphemeralResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS +per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS +per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS # ResourcesManager -per-file ResourcesManager = rtmitchell@google.com, toddke@google.com +per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com + +# VoiceInteraction +per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS # Wallpaper per-file *Wallpaper* = file:/core/java/android/service/wallpaper/OWNERS diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 82e48bfb61a7..d151526612f0 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -111,21 +111,16 @@ import android.media.tv.ITvInputManager; import android.media.tv.TvInputManager; import android.media.tv.tunerresourcemanager.ITunerResourceManager; import android.media.tv.tunerresourcemanager.TunerResourceManager; -import android.net.ConnectivityDiagnosticsManager; -import android.net.ConnectivityManager; +import android.net.ConnectivityFrameworkInitializer; import android.net.EthernetManager; -import android.net.IConnectivityManager; import android.net.IEthernetManager; import android.net.IIpSecService; import android.net.INetworkPolicyManager; -import android.net.ITestNetworkManager; import android.net.IpSecManager; import android.net.NetworkPolicyManager; import android.net.NetworkScoreManager; import android.net.NetworkWatchlistManager; -import android.net.TestNetworkManager; import android.net.TetheringManager; -import android.net.VpnManager; import android.net.lowpan.ILowpanManager; import android.net.lowpan.LowpanManager; import android.net.nsd.INsdManager; @@ -154,7 +149,6 @@ import android.os.IUserManager; import android.os.IncidentManager; import android.os.PowerManager; import android.os.RecoverySystem; -import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.StatsFrameworkInitializer; @@ -349,15 +343,6 @@ public final class SystemServiceRegistry { // (which extends it). SYSTEM_SERVICE_NAMES.put(android.text.ClipboardManager.class, Context.CLIPBOARD_SERVICE); - registerService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class, - new StaticApplicationContextServiceFetcher<ConnectivityManager>() { - @Override - public ConnectivityManager createService(Context context) throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE); - IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); - return new ConnectivityManager(context, service); - }}); - registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() { @Override public IBinder createService() throws ServiceNotFoundException { @@ -391,50 +376,6 @@ public final class SystemServiceRegistry { return new IpSecManager(ctx, service); }}); - registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class, - new CachedServiceFetcher<VpnManager>() { - @Override - public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); - IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); - return new VpnManager(ctx, service); - }}); - - registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE, - ConnectivityDiagnosticsManager.class, - new CachedServiceFetcher<ConnectivityDiagnosticsManager>() { - @Override - public ConnectivityDiagnosticsManager createService(ContextImpl ctx) - throws ServiceNotFoundException { - // ConnectivityDiagnosticsManager is backed by ConnectivityService - IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE); - IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); - return new ConnectivityDiagnosticsManager(ctx, service); - }}); - - registerService( - Context.TEST_NETWORK_SERVICE, - TestNetworkManager.class, - new StaticApplicationContextServiceFetcher<TestNetworkManager>() { - @Override - public TestNetworkManager createService(Context context) - throws ServiceNotFoundException { - IBinder csBinder = - ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE); - IConnectivityManager csMgr = - IConnectivityManager.Stub.asInterface(csBinder); - - final IBinder tnBinder; - try { - tnBinder = csMgr.startOrGetTestNetworkService(); - } catch (RemoteException e) { - throw new ServiceNotFoundException(Context.TEST_NETWORK_SERVICE); - } - ITestNetworkManager tnMgr = ITestNetworkManager.Stub.asInterface(tnBinder); - return new TestNetworkManager(tnMgr); - } - }); - registerService(Context.COUNTRY_DETECTOR, CountryDetector.class, new StaticServiceFetcher<CountryDetector>() { @Override @@ -1355,6 +1296,7 @@ public final class SystemServiceRegistry { try { // Note: the following functions need to be @SystemApis, once they become mainline // modules. + ConnectivityFrameworkInitializer.registerServiceWrappers(); JobSchedulerFrameworkInitializer.registerServiceWrappers(); BlobStoreManagerFrameworkInitializer.initialize(); TelephonyFrameworkInitializer.registerServiceWrappers(); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 9acf675615a6..69d387994568 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -85,8 +85,10 @@ import android.security.keystore.StrongBoxUnavailableException; import android.service.restrictions.RestrictionsReceiver; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.net.NetworkUtilsInternal; @@ -4524,30 +4526,10 @@ public class DevicePolicyManager { if (!proxySpec.type().equals(Proxy.Type.HTTP)) { throw new IllegalArgumentException(); } - InetSocketAddress sa = (InetSocketAddress)proxySpec.address(); - String hostName = sa.getHostName(); - int port = sa.getPort(); - StringBuilder hostBuilder = new StringBuilder(); - hostSpec = hostBuilder.append(hostName) - .append(":").append(Integer.toString(port)).toString(); - if (exclusionList == null) { - exclSpec = ""; - } else { - StringBuilder listBuilder = new StringBuilder(); - boolean firstDomain = true; - for (String exclDomain : exclusionList) { - if (!firstDomain) { - listBuilder = listBuilder.append(","); - } else { - firstDomain = false; - } - listBuilder = listBuilder.append(exclDomain.trim()); - } - exclSpec = listBuilder.toString(); - } - if (android.net.Proxy.validate(hostName, Integer.toString(port), exclSpec) - != android.net.Proxy.PROXY_VALID) - throw new IllegalArgumentException(); + final Pair<String, String> proxyParams = + getProxyParameters(proxySpec, exclusionList); + hostSpec = proxyParams.first; + exclSpec = proxyParams.second; } return mService.setGlobalProxy(admin, hostSpec, exclSpec); } catch (RemoteException e) { @@ -4558,6 +4540,35 @@ public class DevicePolicyManager { } /** + * Build HTTP proxy parameters for {@link IDevicePolicyManager#setGlobalProxy}. + * @throws IllegalArgumentException Invalid proxySpec + * @hide + */ + @VisibleForTesting + public Pair<String, String> getProxyParameters(Proxy proxySpec, List<String> exclusionList) { + InetSocketAddress sa = (InetSocketAddress) proxySpec.address(); + String hostName = sa.getHostName(); + int port = sa.getPort(); + final List<String> trimmedExclList; + if (exclusionList == null) { + trimmedExclList = Collections.emptyList(); + } else { + trimmedExclList = new ArrayList<>(exclusionList.size()); + for (String exclDomain : exclusionList) { + trimmedExclList.add(exclDomain.trim()); + } + } + final ProxyInfo info = ProxyInfo.buildDirectProxy(hostName, port, trimmedExclList); + // The hostSpec is built assuming that there is a specified port and hostname, + // but ProxyInfo.isValid() accepts 0 / empty as unspecified: also reject them. + if (port == 0 || TextUtils.isEmpty(hostName) || !info.isValid()) { + throw new IllegalArgumentException(); + } + + return new Pair<>(hostName + ":" + port, TextUtils.join(",", trimmedExclList)); + } + + /** * Set a network-independent global HTTP proxy. This is not normally what you want for typical * HTTP proxies - they are generally network dependent. However if you're doing something * unusual like general internal filtering this may be useful. On a private network where the diff --git a/core/java/android/app/search/OWNERS b/core/java/android/app/search/OWNERS new file mode 100644 index 000000000000..92835c2b0626 --- /dev/null +++ b/core/java/android/app/search/OWNERS @@ -0,0 +1,2 @@ +hyunyoungs@google.com +sfufa@google.com diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java new file mode 100644 index 000000000000..7281d50a33a5 --- /dev/null +++ b/core/java/android/apphibernation/AppHibernationManager.java @@ -0,0 +1,109 @@ +/* + * 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 android.apphibernation; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; + +/** + * This class provides an API surface for system apps to manipulate the app hibernation + * state of a package for the user provided in the context. + * @hide + */ +@SystemApi +@SystemService(Context.APP_HIBERNATION_SERVICE) +public final class AppHibernationManager { + private static final String TAG = "AppHibernationManager"; + private final Context mContext; + private final IAppHibernationService mIAppHibernationService; + + /** + * Creates a new instance. + * + * @param context The current context associated with the user + * + * @hide + */ + public AppHibernationManager(@NonNull Context context) { + mContext = context; + mIAppHibernationService = IAppHibernationService.Stub.asInterface( + ServiceManager.getService(Context.APP_HIBERNATION_SERVICE)); + } + + /** + * Returns true if the package is hibernating for this context's user, false otherwise. + * + * @hide + */ + @SystemApi + public boolean isHibernatingForUser(@NonNull String packageName) { + try { + return mIAppHibernationService.isHibernatingForUser(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether the package is hibernating for this context's user. + * + * @hide + */ + @SystemApi + public void setHibernatingForUser(@NonNull String packageName, boolean isHibernating) { + try { + mIAppHibernationService.setHibernatingForUser(packageName, mContext.getUserId(), + isHibernating); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns true if app is hibernating globally / at the package level. + * + * @hide + */ + @SystemApi + public boolean isHibernatingGlobally(@NonNull String packageName) { + try { + return mIAppHibernationService.isHibernatingGlobally(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether a package should be globally hibernating. This hibernates the package at a + * package level. User-level hibernation (e.g.. {@link #isHibernatingForUser} is independent + * from global hibernation. + * + * @hide + */ + @SystemApi + public void setHibernatingGlobally(@NonNull String packageName, boolean isHibernating) { + try { + mIAppHibernationService.setHibernatingGlobally(packageName, isHibernating); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl new file mode 100644 index 000000000000..6a068ee2b147 --- /dev/null +++ b/core/java/android/apphibernation/IAppHibernationService.aidl @@ -0,0 +1,28 @@ +/* + * 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 android.apphibernation; + +/** + * Binder interface to communicate with AppHibernationService. + * @hide + */ +interface IAppHibernationService { + boolean isHibernatingForUser(String packageName, int userId); + void setHibernatingForUser(String packageName, int userId, boolean isHibernating); + boolean isHibernatingGlobally(String packageName); + void setHibernatingGlobally(String packageName, boolean isHibernating); +}
\ No newline at end of file diff --git a/core/java/android/appwidget/OWNERS b/core/java/android/appwidget/OWNERS new file mode 100644 index 000000000000..439df4b86cf0 --- /dev/null +++ b/core/java/android/appwidget/OWNERS @@ -0,0 +1,3 @@ +pinyaoting@google.com +suprabh@google.com +sunnygoyal@google.com diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index c0cb32346821..cd91aa9b16b7 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -225,6 +225,39 @@ public final class BluetoothA2dp implements BluetoothProfile { @SystemApi public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; + /** @hide */ + @IntDef(prefix = "DYNAMIC_BUFFER_SUPPORT_", value = { + DYNAMIC_BUFFER_SUPPORT_NONE, + DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD, + DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + /** + * Indicates the supported type of Dynamic Audio Buffer is not supported. + * + * @hide + */ + @SystemApi + public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0; + + /** + * Indicates the supported type of Dynamic Audio Buffer is A2DP offload. + * + * @hide + */ + @SystemApi + public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; + + /** + * Indicates the supported type of Dynamic Audio Buffer is A2DP software encoding. + * + * @hide + */ + @SystemApi + public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; + private BluetoothAdapter mAdapter; private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp", @@ -409,7 +442,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -433,7 +466,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { @@ -845,6 +878,87 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** + * Get the supported type of the Dynamic Audio Buffer. + * <p>Possible return values are + * {@link #DYNAMIC_BUFFER_SUPPORT_NONE}, + * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD}, + * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}. + * + * @return supported type of Dynamic Audio Buffer feature + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @Type int getDynamicBufferSupport() { + if (VDBG) log("getDynamicBufferSupport()"); + try { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getDynamicBufferSupport(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return DYNAMIC_BUFFER_SUPPORT_NONE; + } catch (RemoteException e) { + Log.e(TAG, "failed to get getDynamicBufferSupport, error: ", e); + return DYNAMIC_BUFFER_SUPPORT_NONE; + } + } + + /** + * Return the record of {@link BufferConstraints} object that + * has the default/maximum/minimum audio buffer. This can be used to inform what the controller + * has support for the audio buffer. + * + * @return a record with {@link BufferConstraints} or null if report is unavailable + * or unsupported + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @Nullable BufferConstraints getBufferConstraints() { + if (VDBG) log("getBufferConstraints()"); + try { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.getBufferConstraints(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return null; + } + } + + /** + * Set Dynamic Audio Buffer Size. + * + * @param codec audio codec + * @param value buffer millis + * @return true to indicate success, or false on immediate error + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { + if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")"); + try { + final IBluetoothA2dp service = getService(); + if (service != null && isEnabled()) { + return service.setBufferMillis(codec, value); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** * Helper for converting a state to a string. * * For debug use only - strings are not internationalized. diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index e4b2d7075d47..b7203e3e36bf 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1174,7 +1174,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean disable(boolean persist) { try { diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index 7a6ff79623af..381318b26dad 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -824,6 +824,25 @@ public final class BluetoothGatt implements BluetoothProfile { * error */ private boolean registerApp(BluetoothGattCallback callback, Handler handler) { + return registerApp(callback, handler, false); + } + + /** + * Register an application callback to start using GATT. + * + * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} + * is used to notify success or failure if the function returns true. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param eatt_support indicate to allow for eatt support + * @return If true, the callback will be called to notify success or failure, false on immediate + * error + * @hide + */ + private boolean registerApp(BluetoothGattCallback callback, Handler handler, + boolean eatt_support) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; @@ -833,7 +852,7 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); try { - mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback, eatt_support); } catch (RemoteException e) { Log.e(TAG, "", e); return false; diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 13b1b4f93cf0..088b0169b631 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -443,6 +443,25 @@ public final class BluetoothGattServer implements BluetoothProfile { * error */ /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { + return registerCallback(callback, false); + } + + /** + * Register an application callback to start using GattServer. + * + * <p>This is an asynchronous call. The callback is used to notify + * success or failure if the function returns true. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param eatt_support indicates if server can use eatt + * @return true, the callback will be called to notify success or failure, false on immediate + * error + * @hide + */ + /*package*/ boolean registerCallback(BluetoothGattServerCallback callback, + boolean eatt_support) { if (DBG) Log.d(TAG, "registerCallback()"); if (mService == null) { Log.e(TAG, "GATT service not available"); @@ -459,7 +478,7 @@ public final class BluetoothGattServer implements BluetoothProfile { mCallback = callback; try { - mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback, eatt_support); } catch (RemoteException e) { Log.e(TAG, "", e); mCallback = null; diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index f59ae338ee2f..4fb557780d04 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -80,7 +80,7 @@ public final class BluetoothHeadset implements BluetoothProfile { /** * Intent used to broadcast the change in the Audio Connection state of the - * A2DP profile. + * HFP profile. * * <p>This intent will have 3 extras: * <ul> @@ -113,7 +113,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -1172,7 +1172,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1198,7 +1198,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 3b4fe0a30b80..d5c1c3e2d61e 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -225,6 +225,24 @@ public final class BluetoothManager { * * @param context App context * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param eatt_support idicates if server should use eatt channel for notifications. + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback, boolean eatt_support) { + return (openGattServer(context, callback, BluetoothDevice.TRANSPORT_AUTO, eatt_support)); + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. * @param transport preferred transport for GATT connections to remote dual-mode devices {@link * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link * BluetoothDevice#TRANSPORT_LE} @@ -233,6 +251,27 @@ public final class BluetoothManager { */ public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback, int transport) { + return (openGattServer(context, callback, transport, false)); + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param transport preferred transport for GATT connections to remote dual-mode devices {@link + * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link + * BluetoothDevice#TRANSPORT_LE} + * @param eatt_support idicates if server should use eatt channel for notifications. + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback, int transport, boolean eatt_support) { if (context == null || callback == null) { throw new IllegalArgumentException("null parameter: " + context + " " + callback); } @@ -248,7 +287,7 @@ public final class BluetoothManager { return null; } BluetoothGattServer mGattServer = new BluetoothGattServer(iGatt, transport); - Boolean regStatus = mGattServer.registerCallback(callback); + Boolean regStatus = mGattServer.registerCallback(callback, eatt_support); return regStatus ? mGattServer : null; } catch (RemoteException e) { Log.e(TAG, "", e); diff --git a/core/java/android/bluetooth/BufferConstraint.java b/core/java/android/bluetooth/BufferConstraint.java new file mode 100644 index 000000000000..cbffc788c35d --- /dev/null +++ b/core/java/android/bluetooth/BufferConstraint.java @@ -0,0 +1,105 @@ +/* + * 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.bluetooth; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Stores a codec's constraints on buffering length in milliseconds. + * + * {@hide} + */ +@SystemApi +public final class BufferConstraint implements Parcelable { + + private static final String TAG = "BufferConstraint"; + private int mDefaultMillis; + private int mMaxMillis; + private int mMinMillis; + + public BufferConstraint(int defaultMillis, int maxMillis, + int minMillis) { + mDefaultMillis = defaultMillis; + mMaxMillis = maxMillis; + mMinMillis = minMillis; + } + + BufferConstraint(Parcel in) { + mDefaultMillis = in.readInt(); + mMaxMillis = in.readInt(); + mMinMillis = in.readInt(); + } + + public static final @NonNull Parcelable.Creator<BufferConstraint> CREATOR = + new Parcelable.Creator<BufferConstraint>() { + public BufferConstraint createFromParcel(Parcel in) { + return new BufferConstraint(in); + } + + public BufferConstraint[] newArray(int size) { + return new BufferConstraint[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mDefaultMillis); + out.writeInt(mMaxMillis); + out.writeInt(mMinMillis); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Get the default buffer millis + * + * @return default buffer millis + * @hide + */ + @SystemApi + public int getDefaultMillis() { + return mDefaultMillis; + } + + /** + * Get the maximum buffer millis + * + * @return maximum buffer millis + * @hide + */ + @SystemApi + public int getMaxMillis() { + return mMaxMillis; + } + + /** + * Get the minimum buffer millis + * + * @return minimum buffer millis + * @hide + */ + @SystemApi + public int getMinMillis() { + return mMinMillis; + } +} diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java new file mode 100644 index 000000000000..7e5ec1e78435 --- /dev/null +++ b/core/java/android/bluetooth/BufferConstraints.java @@ -0,0 +1,96 @@ +/* + * 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.bluetooth; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * A parcelable collection of buffer constraints by codec type. + * + * {@hide} + */ +@SystemApi +public final class BufferConstraints implements Parcelable { + public static final int BUFFER_CODEC_MAX_NUM = 32; + + private static final String TAG = "BufferConstraints"; + + private Map<Integer, BufferConstraint> mBufferConstraints; + private List<BufferConstraint> mBufferConstraintList; + + public BufferConstraints(@NonNull List<BufferConstraint> + bufferConstraintList) { + + mBufferConstraintList = new ArrayList<BufferConstraint>(bufferConstraintList); + mBufferConstraints = new HashMap<Integer, BufferConstraint>(); + for (int i = 0; i < BUFFER_CODEC_MAX_NUM; i++) { + mBufferConstraints.put(i, bufferConstraintList.get(i)); + } + } + + BufferConstraints(Parcel in) { + mBufferConstraintList = new ArrayList<BufferConstraint>(); + mBufferConstraints = new HashMap<Integer, BufferConstraint>(); + in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader()); + for (int i = 0; i < mBufferConstraintList.size(); i++) { + mBufferConstraints.put(i, mBufferConstraintList.get(i)); + } + } + + public static final @NonNull Parcelable.Creator<BufferConstraints> CREATOR = + new Parcelable.Creator<BufferConstraints>() { + public BufferConstraints createFromParcel(Parcel in) { + return new BufferConstraints(in); + } + + public BufferConstraints[] newArray(int size) { + return new BufferConstraints[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeList(mBufferConstraintList); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Get the buffer constraints by codec type. + * + * @param codec Audio codec + * @return buffer constraints by codec type. + * @hide + */ + @SystemApi + public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) { + return mBufferConstraints.get(codec); + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 92ede1ca45fb..9c8856650ae0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2602,6 +2602,36 @@ public abstract class Context { public abstract void sendStickyBroadcast(@RequiresPermission Intent intent); /** + * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the + * Intent you are sending stays around after the broadcast is complete, + * so that others can quickly retrieve that data through the return + * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In + * all other ways, this behaves the same as + * {@link #sendBroadcast(Intent)}. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast, and the Intent will be held to + * be re-broadcast to future receivers. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * + * @see #sendBroadcast(Intent) + * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) + public void sendStickyBroadcast(@RequiresPermission @NonNull Intent intent, + @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * <p>Version of {@link #sendStickyBroadcast} that allows you to * receive data back from the broadcast. This is accomplished by * supplying your own BroadcastReceiver when calling, which will be @@ -4509,6 +4539,17 @@ public abstract class Context { public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller"; /** + * Use with {@link #getSystemService(String) to retrieve an + * {@link android.apphibernation.AppHibernationManager}} for + * communicating with the hibernation service. + * @hide + * + * @see #getSystemService(String) + */ + @SystemApi + public static final String APP_HIBERNATION_SERVICE = "app_hibernation"; + + /** * Use with {@link #getSystemService(String)} to retrieve an * {@link android.app.backup.IBackupManager IBackupManager} for communicating * with the backup mechanism. @@ -4999,9 +5040,7 @@ public abstract class Context { * Service to capture a bugreport. * @see #getSystemService(String) * @see android.os.BugreportManager - * @hide */ - @SystemApi public static final String BUGREPORT_SERVICE = "bugreport"; /** diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 5bdd521e92dd..e351c244b04c 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -617,6 +617,35 @@ public class ContextWrapper extends Context { mBase.sendStickyBroadcast(intent); } + /** + * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the + * Intent you are sending stays around after the broadcast is complete, + * so that others can quickly retrieve that data through the return + * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In + * all other ways, this behaves the same as + * {@link #sendBroadcast(Intent)}. + * + * @deprecated Sticky broadcasts should not be used. They provide no security (anyone + * can access them), no protection (anyone can modify them), and many other problems. + * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em> + * has changed, with another mechanism for apps to retrieve the current value whenever + * desired. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast, and the Intent will be held to + * be re-broadcast to future receivers. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * + * @see #sendBroadcast(Intent) + * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) + */ + @Override + @Deprecated + public void sendStickyBroadcast(@NonNull Intent intent, @Nullable Bundle options) { + mBase.sendStickyBroadcast(intent, options); + } + @Override @Deprecated public void sendStickyOrderedBroadcast( diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS index c1e7e41972ba..144856b68e7f 100644 --- a/core/java/android/content/OWNERS +++ b/core/java/android/content/OWNERS @@ -1,3 +1,7 @@ # Remain no owner because multiple modules may touch this file. per-file Context.java = * per-file ContextWrapper.java = * +per-file IntentFilter.java = toddke@google.com +per-file IntentFilter.java = patb@google.com +per-file Intent.java = toddke@google.com +per-file Intent.java = patb@google.com
\ No newline at end of file diff --git a/core/java/android/content/integrity/OWNERS b/core/java/android/content/integrity/OWNERS new file mode 100644 index 000000000000..20c758aedd67 --- /dev/null +++ b/core/java/android/content/integrity/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 722021 + +toddke@android.com +toddke@google.com +patb@google.com diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index bd6edb4f4431..5f8754efb47f 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -64,7 +64,7 @@ import android.content.IntentSender; */ interface IPackageManager { void checkPackageStartable(String packageName, int userId); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) boolean isPackageAvailable(String packageName, int userId); @UnsupportedAppUsage PackageInfo getPackageInfo(String packageName, int flags, int userId); diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS index f88df958d827..f0def80505ce 100644 --- a/core/java/android/content/pm/OWNERS +++ b/core/java/android/content/pm/OWNERS @@ -6,4 +6,6 @@ patb@google.com per-file PackageParser.java = chiuwinson@google.com per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS +per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS +per-file UserInfo* = file:/MULTIUSER_OWNERS diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 00f5fb95768f..31beb6e6a565 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -299,7 +299,10 @@ public abstract class PackageManager { /** * {@link PackageInfo} flag: return information about the * intent filters supported by the activity. + * + * @deprecated The platform does not support getting {@link IntentFilter}s for the package. */ + @Deprecated public static final int GET_INTENT_FILTERS = 0x00000020; /** @@ -2122,6 +2125,35 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports + * {@link android.security.identity.IdentityCredentialStore} implemented in secure hardware + * at the given feature version. + * + * <p>Known feature versions include: + * <ul> + * <li><code>202009</code>: corresponds to the features included in the Identity Credential + * API shipped in Android 11. + * <li><code>202101</code>: corresponds to the features included in the Identity Credential + * API shipped in Android 12. + * </ul> + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE = + "android.hardware.identity_credential"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports + * {@link android.security.identity.IdentityCredentialStore} implemented in secure hardware + * with direct access at the given feature version. + * See {@link #FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known feature versions. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS = + "android.hardware.identity_credential_direct_access"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports one or more methods of * reporting current location. */ diff --git a/core/java/android/graphics/fonts/OWNERS b/core/java/android/graphics/fonts/OWNERS new file mode 100644 index 000000000000..18486af9d12c --- /dev/null +++ b/core/java/android/graphics/fonts/OWNERS @@ -0,0 +1 @@ +include /graphics/java/android/graphics/fonts/OWNERS diff --git a/core/java/android/hardware/face/OWNERS b/core/java/android/hardware/face/OWNERS index 33527f824827..be10df1099ed 100644 --- a/core/java/android/hardware/face/OWNERS +++ b/core/java/android/hardware/face/OWNERS @@ -1,3 +1,7 @@ # Bug component: 879035 +curtislb@google.com +ilyamaty@google.com jaggies@google.com +joshmccloskey@google.com +kchyn@google.com diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 0ef55f4a60f9..3730790b92e0 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -954,7 +954,10 @@ public class UsbManager { /** * Returns whether the given functions are valid inputs to UsbManager. - * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI are accepted. + * Currently the empty functions or any of MTP, PTP, RNDIS, MIDI, NCM are accepted. + * + * Only one function may be set at a time, except for RNDIS and NCM, which can be set together + * because from a user perspective they are the same function (tethering). * * @return Whether the mask is settable. * @@ -962,7 +965,9 @@ public class UsbManager { */ public static boolean areSettableFunctions(long functions) { return functions == FUNCTION_NONE - || ((~SETTABLE_FUNCTIONS & functions) == 0 && Long.bitCount(functions) == 1); + || ((~SETTABLE_FUNCTIONS & functions) == 0 + && ((Long.bitCount(functions) == 1) + || (functions == (FUNCTION_RNDIS | FUNCTION_NCM)))); } /** diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl index aeaf09d8fafe..aa3682d92105 100644 --- a/core/java/android/net/IIpConnectivityMetrics.aidl +++ b/core/java/android/net/IIpConnectivityMetrics.aidl @@ -19,6 +19,9 @@ package android.net; import android.os.Parcelable; import android.net.ConnectivityMetricsEvent; import android.net.INetdEventCallback; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; /** {@hide} */ interface IIpConnectivityMetrics { @@ -29,6 +32,11 @@ interface IIpConnectivityMetrics { */ int logEvent(in ConnectivityMetricsEvent event); + void logDefaultNetworkValidity(boolean valid); + void logDefaultNetworkEvent(in Network defaultNetwork, int score, boolean validated, + in LinkProperties lp, in NetworkCapabilities nc, in Network previousDefaultNetwork, + int previousScore, in LinkProperties previousLp, in NetworkCapabilities previousNc); + /** * Callback can be registered by DevicePolicyManager or NetworkWatchlistService only. * @return status {@code true} if registering/unregistering of the callback was successful, diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl index 37813ce11a5f..0a6be20226b8 100644 --- a/core/java/android/net/INetworkManagementEventObserver.aidl +++ b/core/java/android/net/INetworkManagementEventObserver.aidl @@ -85,14 +85,14 @@ oneway interface INetworkManagementEventObserver { /** * Interface data activity status is changed. * - * @param networkType The legacy network type of the data activity change. + * @param transportType The transport type of the data activity change. * @param active True if the interface is actively transmitting data, false if it is idle. * @param tsNanos Elapsed realtime in nanos when the state of the network interface changed. * @param uid Uid of this event. It represents the uid that was responsible for waking the * radio. For those events that are reported by system itself, not from specific uid, * use -1 for the events which means no uid. */ - void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos, int uid); + void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid); /** * Information about available DNS servers has been received. diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 792e5b410afc..29a3fdf59e8b 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -81,4 +81,5 @@ interface INetworkPolicyManager { void factoryReset(String subscriber); boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork); + boolean isUidRestrictedOnMeteredNetworks(int uid); } diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 1a3dc974480c..0baf11e850c7 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -23,11 +23,11 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; +import android.net.UnderlyingNetworkInfo; import android.net.netstats.provider.INetworkStatsProvider; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.IBinder; import android.os.Messenger; -import com.android.internal.net.VpnInfo; /** {@hide} */ interface INetworkStatsService { @@ -70,7 +70,7 @@ interface INetworkStatsService { in Network[] defaultNetworks, in NetworkState[] networkStates, in String activeIface, - in VpnInfo[] vpnInfos); + in UnderlyingNetworkInfo[] underlyingNetworkInfos); /** Force update of statistics. */ @UnsupportedAppUsage void forceUpdate(); diff --git a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java b/core/java/android/net/IQosCallback.aidl index 569a78c0ab41..91c75759f85c 100644 --- a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java +++ b/core/java/android/net/IQosCallback.aidl @@ -14,27 +14,21 @@ * limitations under the License. */ -package com.android.permission.persistence; +package android.net; -import android.annotation.NonNull; +import android.os.Bundle; +import android.net.QosSession; +import android.telephony.data.EpsBearerQosSessionAttributes; /** - * Utility class for IO. + * AIDL interface for QosCallback * * @hide */ -public class IoUtils { - - private IoUtils() {} - - /** - * Close 'closeable' ignoring any exceptions. - */ - public static void closeQuietly(@NonNull AutoCloseable closeable) { - try { - closeable.close(); - } catch (Exception ignored) { - // Ignored. - } - } +oneway interface IQosCallback +{ + void onQosEpsBearerSessionAvailable(in QosSession session, + in EpsBearerQosSessionAttributes attributes); + void onQosSessionLost(in QosSession session); + void onError(in int type); } diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index d83715c692f7..70bca3019818 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -15,6 +15,8 @@ */ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; @@ -628,7 +630,7 @@ public final class IpSecManager { } /** @hide */ - @VisibleForTesting + @SystemApi(client = MODULE_LIBRARIES) public int getResourceId() { return mResourceId; } diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java index 70c4a7235b9d..011a04c26137 100644 --- a/core/java/android/net/MatchAllNetworkSpecifier.java +++ b/core/java/android/net/MatchAllNetworkSpecifier.java @@ -31,17 +31,6 @@ import android.os.Parcelable; */ @SystemApi public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements Parcelable { - /** - * Utility method which verifies that the ns argument is not a MatchAllNetworkSpecifier and - * throws an IllegalArgumentException if it is. - * @hide - */ - public static void checkNotMatchAllNetworkSpecifier(NetworkSpecifier ns) { - if (ns instanceof MatchAllNetworkSpecifier) { - throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted"); - } - } - /** @hide */ @Override public boolean canBeSatisfiedBy(NetworkSpecifier other) { diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index a0dc72d4adbf..b644ed56ad8b 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -194,13 +194,15 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { subscriberId = state.subscriberId; if (type == TYPE_WIFI) { - if (state.networkId != null) { - networkId = state.networkId; - } else { - final WifiManager wifi = (WifiManager) context.getSystemService( - Context.WIFI_SERVICE); - final WifiInfo info = wifi.getConnectionInfo(); - networkId = info != null ? info.getSSID() : null; + if (state.networkCapabilities.getSsid() != null) { + networkId = state.networkCapabilities.getSsid(); + if (networkId == null) { + // TODO: Figure out if this code path never runs. If so, remove them. + final WifiManager wifi = (WifiManager) context.getSystemService( + Context.WIFI_SERVICE); + final WifiInfo info = wifi.getConnectionInfo(); + networkId = info != null ? info.getSSID() : null; + } } } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index ce16a7835179..82b035b08428 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -32,8 +32,8 @@ import android.content.pm.Signature; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.os.Build; +import android.os.Process; import android.os.RemoteException; -import android.os.UserHandle; import android.telephony.SubscriptionPlan; import android.util.DebugUtils; import android.util.Pair; @@ -122,17 +122,26 @@ public class NetworkPolicyManager { * @hide */ public static final int RULE_REJECT_ALL = 1 << 6; + /** + * Reject traffic on all networks for restricted networking mode. + */ + public static final int RULE_REJECT_RESTRICTED_MODE = 1 << 10; /** * Mask used to get the {@code RULE_xxx_METERED} rules * @hide */ - public static final int MASK_METERED_NETWORKS = 0b00001111; + public static final int MASK_METERED_NETWORKS = 0b000000001111; /** * Mask used to get the {@code RULE_xxx_ALL} rules * @hide */ - public static final int MASK_ALL_NETWORKS = 0b11110000; + public static final int MASK_ALL_NETWORKS = 0b000011110000; + /** + * Mask used to get the {@code RULE_xxx_RESTRICTED_MODE} rules + * @hide + */ + public static final int MASK_RESTRICTED_MODE_NETWORKS = 0b111100000000; /** @hide */ public static final int FIREWALL_RULE_DEFAULT = 0; @@ -433,6 +442,40 @@ public class NetworkPolicyManager { } /** + * Check that networking is blocked for the given uid. + * + * @param uid The target uid. + * @param meteredNetwork True if the network is metered. + * @return true if networking is blocked for the given uid according to current networking + * policies. + * + * @hide + */ + public boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork) { + try { + return mService.isUidNetworkingBlocked(uid, meteredNetwork); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check that the given uid is restricted from doing networking on metered networks. + * + * @param uid The target uid. + * @return true if the given uid is restricted from doing networking on metered networks. + * + * @hide + */ + public boolean isUidRestrictedOnMeteredNetworks(int uid) { + try { + return mService.isUidRestrictedOnMeteredNetworks(uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Get multipath preference for the given network. */ public int getMultipathPreference(Network network) { @@ -473,7 +516,7 @@ public class NetworkPolicyManager { @Deprecated public static boolean isUidValidForPolicy(Context context, int uid) { // first, quick-reject non-applications - if (!UserHandle.isApp(uid)) { + if (!Process.isApplicationUid(uid)) { return false; } diff --git a/core/java/android/net/NetworkReleasedException.java b/core/java/android/net/NetworkReleasedException.java new file mode 100644 index 000000000000..0629b7563aea --- /dev/null +++ b/core/java/android/net/NetworkReleasedException.java @@ -0,0 +1,32 @@ +/* + * 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 android.net; + +import android.annotation.SystemApi; + +/** + * Indicates that the {@link Network} was released and is no longer available. + * + * @hide + */ +@SystemApi +public class NetworkReleasedException extends Exception { + /** @hide */ + public NetworkReleasedException() { + super("The network was released and is no longer available"); + } +} diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java index 713b6888376e..e1ef8b5ea5c9 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -30,7 +31,8 @@ import android.util.Slog; public class NetworkState implements Parcelable { private static final boolean VALIDATE_ROAMING_STATE = false; - public static final NetworkState EMPTY = new NetworkState(null, null, null, null, null, null); + // TODO: remove and make members @NonNull. + public static final NetworkState EMPTY = new NetworkState(); public final NetworkInfo networkInfo; public final LinkProperties linkProperties; @@ -40,9 +42,18 @@ public class NetworkState implements Parcelable { public final String subscriberId; public final String networkId; - public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties, - NetworkCapabilities networkCapabilities, Network network, String subscriberId, - String networkId) { + private NetworkState() { + networkInfo = null; + linkProperties = null; + networkCapabilities = null; + network = null; + subscriberId = null; + networkId = null; + } + + public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties, + @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network, + String subscriberId, String networkId) { this.networkInfo = networkInfo; this.linkProperties = linkProperties; this.networkCapabilities = networkCapabilities; diff --git a/core/java/android/net/QosCallback.java b/core/java/android/net/QosCallback.java new file mode 100644 index 000000000000..22f06bc0e690 --- /dev/null +++ b/core/java/android/net/QosCallback.java @@ -0,0 +1,91 @@ +/* + * 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.net; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +import java.util.concurrent.Executor; + +/** + * Receives Qos information given a {@link Network}. The callback is registered with + * {@link ConnectivityManager#registerQosCallback}. + * + * <p> + * <br/> + * The callback will no longer receive calls if any of the following takes place: + * <ol> + * <li>{@link ConnectivityManager#unregisterQosCallback(QosCallback)} is called with the same + * callback instance.</li> + * <li>{@link QosCallback#onError(QosCallbackException)} is called.</li> + * <li>A network specific issue occurs. eg. Congestion on a carrier network.</li> + * <li>The network registered with the callback has no associated QoS providers</li> + * </ul> + * {@hide} + */ +@SystemApi +public abstract class QosCallback { + /** + * Invoked after an error occurs on a registered callback. Once called, the callback is + * automatically unregistered and the callback will no longer receive calls. + * + * <p>The underlying exception can either be a runtime exception or a custom exception made for + * {@link QosCallback}. see: {@link QosCallbackException}. + * + * @param exception wraps the underlying cause + */ + public void onError(@NonNull final QosCallbackException exception) { + } + + /** + * Called when a Qos Session first becomes available to the callback or if its attributes have + * changed. + * <p> + * Note: The callback may be called multiple times with the same attributes. + * + * @param session the available session + * @param sessionAttributes the attributes of the session + */ + public void onQosSessionAvailable(@NonNull final QosSession session, + @NonNull final QosSessionAttributes sessionAttributes) { + } + + /** + * Called after a Qos Session is lost. + * <p> + * At least one call to + * {@link QosCallback#onQosSessionAvailable(QosSession, QosSessionAttributes)} + * with the same {@link QosSession} will precede a call to lost. + * + * @param session the lost session + */ + public void onQosSessionLost(@NonNull final QosSession session) { + } + + /** + * Thrown when there is a problem registering {@link QosCallback} with + * {@link ConnectivityManager#registerQosCallback(QosSocketInfo, QosCallback, Executor)}. + */ + public static class QosCallbackRegistrationException extends RuntimeException { + /** + * @hide + */ + public QosCallbackRegistrationException() { + super(); + } + } +} diff --git a/core/java/android/net/QosCallbackConnection.java b/core/java/android/net/QosCallbackConnection.java new file mode 100644 index 000000000000..bdb4ad68cd7b --- /dev/null +++ b/core/java/android/net/QosCallbackConnection.java @@ -0,0 +1,128 @@ +/* + * 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.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.telephony.data.EpsBearerQosSessionAttributes; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * Sends messages from {@link com.android.server.ConnectivityService} to the registered + * {@link QosCallback}. + * <p/> + * This is a satellite class of {@link ConnectivityManager} and not meant + * to be used in other contexts. + * + * @hide + */ +class QosCallbackConnection extends android.net.IQosCallback.Stub { + + @NonNull private final ConnectivityManager mConnectivityManager; + @Nullable private volatile QosCallback mCallback; + @NonNull private final Executor mExecutor; + + @VisibleForTesting + @Nullable + public QosCallback getCallback() { + return mCallback; + } + + /** + * The constructor for the connection + * + * @param connectivityManager the mgr that created this connection + * @param callback the callback to send messages back to + * @param executor The executor on which the callback will be invoked. The provided + * {@link Executor} must run callback sequentially, otherwise the order of + * callbacks cannot be guaranteed. + */ + QosCallbackConnection(@NonNull final ConnectivityManager connectivityManager, + @NonNull final QosCallback callback, + @NonNull final Executor executor) { + mConnectivityManager = Objects.requireNonNull(connectivityManager, + "connectivityManager must be non-null"); + mCallback = Objects.requireNonNull(callback, "callback must be non-null"); + mExecutor = Objects.requireNonNull(executor, "executor must be non-null"); + } + + /** + * Called when either the {@link EpsBearerQosSessionAttributes} has changed or on the first time + * the attributes have become available. + * + * @param session the session that is now available + * @param attributes the corresponding attributes of session + */ + @Override + public void onQosEpsBearerSessionAvailable(@NonNull final QosSession session, + @NonNull final EpsBearerQosSessionAttributes attributes) { + + mExecutor.execute(() -> { + final QosCallback callback = mCallback; + if (callback != null) { + callback.onQosSessionAvailable(session, attributes); + } + }); + } + + /** + * Called when the session is lost. + * + * @param session the session that was lost + */ + @Override + public void onQosSessionLost(@NonNull final QosSession session) { + mExecutor.execute(() -> { + final QosCallback callback = mCallback; + if (callback != null) { + callback.onQosSessionLost(session); + } + }); + } + + /** + * Called when there is an error on the registered callback. + * + * @param errorType the type of error + */ + @Override + public void onError(@QosCallbackException.ExceptionType final int errorType) { + mExecutor.execute(() -> { + final QosCallback callback = mCallback; + if (callback != null) { + // Messages no longer need to be received since there was an error. + stopReceivingMessages(); + mConnectivityManager.unregisterQosCallback(callback); + callback.onError(QosCallbackException.createException(errorType)); + } + }); + } + + /** + * The callback will stop receiving messages. + * <p/> + * There are no synchronization guarantees on exactly when the callback will stop receiving + * messages. + */ + void stopReceivingMessages() { + mCallback = null; + } +} diff --git a/core/java/android/net/QosCallbackException.java b/core/java/android/net/QosCallbackException.java new file mode 100644 index 000000000000..7fd9a527e2ac --- /dev/null +++ b/core/java/android/net/QosCallbackException.java @@ -0,0 +1,110 @@ +/* + * 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.net; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This is the exception type passed back through the onError method on {@link QosCallback}. + * {@link QosCallbackException#getCause()} contains the actual error that caused this exception. + * + * The possible exception types as causes are: + * 1. {@link NetworkReleasedException} + * 2. {@link SocketNotBoundException} + * 3. {@link UnsupportedOperationException} + * 4. {@link SocketLocalAddressChangedException} + * + * @hide + */ +@SystemApi +public final class QosCallbackException extends Exception { + + /** @hide */ + @IntDef(prefix = {"EX_TYPE_"}, value = { + EX_TYPE_FILTER_NONE, + EX_TYPE_FILTER_NETWORK_RELEASED, + EX_TYPE_FILTER_SOCKET_NOT_BOUND, + EX_TYPE_FILTER_NOT_SUPPORTED, + EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ExceptionType {} + + private static final String TAG = "QosCallbackException"; + + // Types of exceptions supported // + /** {@hide} */ + public static final int EX_TYPE_FILTER_NONE = 0; + + /** {@hide} */ + public static final int EX_TYPE_FILTER_NETWORK_RELEASED = 1; + + /** {@hide} */ + public static final int EX_TYPE_FILTER_SOCKET_NOT_BOUND = 2; + + /** {@hide} */ + public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 3; + + /** {@hide} */ + public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 4; + + /** + * Creates exception based off of a type and message. Not all types of exceptions accept a + * custom message. + * + * {@hide} + */ + @NonNull + static QosCallbackException createException(@ExceptionType final int type) { + switch (type) { + case EX_TYPE_FILTER_NETWORK_RELEASED: + return new QosCallbackException(new NetworkReleasedException()); + case EX_TYPE_FILTER_SOCKET_NOT_BOUND: + return new QosCallbackException(new SocketNotBoundException()); + case EX_TYPE_FILTER_NOT_SUPPORTED: + return new QosCallbackException(new UnsupportedOperationException( + "This device does not support the specified filter")); + case EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED: + return new QosCallbackException( + new SocketLocalAddressChangedException()); + default: + Log.wtf(TAG, "create: No case setup for exception type: '" + type + "'"); + return new QosCallbackException( + new RuntimeException("Unknown exception code: " + type)); + } + } + + /** + * @hide + */ + public QosCallbackException(@NonNull final String message) { + super(message); + } + + /** + * @hide + */ + public QosCallbackException(@NonNull final Throwable cause) { + super(cause); + } +} diff --git a/core/java/android/net/QosFilter.java b/core/java/android/net/QosFilter.java new file mode 100644 index 000000000000..ab55002e02b3 --- /dev/null +++ b/core/java/android/net/QosFilter.java @@ -0,0 +1,75 @@ +/* + * 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.net; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +import java.net.InetAddress; + +/** + * Provides the related filtering logic to the {@link NetworkAgent} to match {@link QosSession}s + * to their related {@link QosCallback}. + * + * Used by the {@link com.android.server.ConnectivityService} to validate a {@link QosCallback} + * is still able to receive a {@link QosSession}. + * + * @hide + */ +@SystemApi +public abstract class QosFilter { + + /** + * The constructor is kept hidden from outside this package to ensure that all derived types + * are known and properly handled when being passed to and from {@link NetworkAgent}. + * + * @hide + */ + QosFilter() { + } + + /** + * The network used with this filter. + * + * @return the registered {@link Network} + */ + @NonNull + public abstract Network getNetwork(); + + /** + * Validates that conditions have not changed such that no further {@link QosSession}s should + * be passed back to the {@link QosCallback} associated to this filter. + * + * @return the error code when present, otherwise the filter is valid + * + * @hide + */ + @QosCallbackException.ExceptionType + public abstract int validate(); + + /** + * Determines whether or not the parameters is a match for the filter. + * + * @param address the local address + * @param startPort the start of the port range + * @param endPort the end of the port range + * @return whether the parameters match the local address of the filter + */ + public abstract boolean matchesLocalAddress(@NonNull InetAddress address, + int startPort, int endPort); +} + diff --git a/core/java/android/net/QosFilterParcelable.aidl b/core/java/android/net/QosFilterParcelable.aidl new file mode 100644 index 000000000000..312d6352ee92 --- /dev/null +++ b/core/java/android/net/QosFilterParcelable.aidl @@ -0,0 +1,21 @@ +/* +** +** 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.net; + +parcelable QosFilterParcelable; + diff --git a/core/java/android/net/QosFilterParcelable.java b/core/java/android/net/QosFilterParcelable.java new file mode 100644 index 000000000000..da3b2cf8ff7a --- /dev/null +++ b/core/java/android/net/QosFilterParcelable.java @@ -0,0 +1,113 @@ +/* + * 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 android.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.util.Objects; + +/** + * Aware of how to parcel different types of {@link QosFilter}s. Any new type of qos filter must + * have a specialized case written here. + * <p/> + * Specifically leveraged when transferring {@link QosFilter} from + * {@link com.android.server.ConnectivityService} to {@link NetworkAgent} when the filter is first + * registered. + * <p/> + * This is not meant to be used in other contexts. + * + * @hide + */ +public final class QosFilterParcelable implements Parcelable { + + private static final String LOG_TAG = QosFilterParcelable.class.getSimpleName(); + + // Indicates that the filter was not successfully written to the parcel. + private static final int NO_FILTER_PRESENT = 0; + + // The parcel is of type qos socket filter. + private static final int QOS_SOCKET_FILTER = 1; + + private final QosFilter mQosFilter; + + /** + * The underlying qos filter. + * <p/> + * Null only in the case parceling failed. + */ + @Nullable + public QosFilter getQosFilter() { + return mQosFilter; + } + + public QosFilterParcelable(@NonNull final QosFilter qosFilter) { + Objects.requireNonNull(qosFilter, "qosFilter must be non-null"); + + // NOTE: Normally a type check would belong here, but doing so breaks unit tests that rely + // on mocking qos filter. + mQosFilter = qosFilter; + } + + private QosFilterParcelable(final Parcel in) { + final int filterParcelType = in.readInt(); + + switch (filterParcelType) { + case QOS_SOCKET_FILTER: { + mQosFilter = new QosSocketFilter(QosSocketInfo.CREATOR.createFromParcel(in)); + break; + } + + case NO_FILTER_PRESENT: + default: { + mQosFilter = null; + } + } + } + + public static final Creator<QosFilterParcelable> CREATOR = new Creator<QosFilterParcelable>() { + @Override + public QosFilterParcelable createFromParcel(final Parcel in) { + return new QosFilterParcelable(in); + } + + @Override + public QosFilterParcelable[] newArray(final int size) { + return new QosFilterParcelable[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + if (mQosFilter instanceof QosSocketFilter) { + dest.writeInt(QOS_SOCKET_FILTER); + final QosSocketFilter qosSocketFilter = (QosSocketFilter) mQosFilter; + qosSocketFilter.getQosSocketInfo().writeToParcel(dest, 0); + return; + } + dest.writeInt(NO_FILTER_PRESENT); + Log.e(LOG_TAG, "Parceling failed, unknown type of filter present: " + mQosFilter); + } +} diff --git a/core/java/android/net/QosSession.aidl b/core/java/android/net/QosSession.aidl new file mode 100644 index 000000000000..c2cf36624b55 --- /dev/null +++ b/core/java/android/net/QosSession.aidl @@ -0,0 +1,21 @@ +/* +** +** 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.net; + +parcelable QosSession; + diff --git a/core/java/android/net/QosSession.java b/core/java/android/net/QosSession.java new file mode 100644 index 000000000000..4f3bb77c5877 --- /dev/null +++ b/core/java/android/net/QosSession.java @@ -0,0 +1,136 @@ +/* + * 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.net; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Provides identifying information of a QoS session. Sent to an application through + * {@link QosCallback}. + * + * @hide + */ +@SystemApi +public final class QosSession implements Parcelable { + + /** + * The {@link QosSession} is a LTE EPS Session. + */ + public static final int TYPE_EPS_BEARER = 1; + + private final int mSessionId; + + private final int mSessionType; + + /** + * Gets the unique id of the session that is used to differentiate sessions across different + * types. + * <p/> + * Note: Different qos sessions can be provided by different actors. + * + * @return the unique id + */ + public long getUniqueId() { + return (long) mSessionType << 32 | mSessionId; + } + + /** + * Gets the session id that is unique within that type. + * <p/> + * Note: The session id is set by the actor providing the qos. It can be either manufactured by + * the actor, but also may have a particular meaning within that type. For example, using the + * bearer id as the session id for {@link android.telephony.data.EpsBearerQosSessionAttributes} + * is a straight forward way to keep the sessions unique from one another within that type. + * + * @return the id of the session + */ + public int getSessionId() { + return mSessionId; + } + + /** + * Gets the type of session. + */ + @QosSessionType + public int getSessionType() { + return mSessionType; + } + + /** + * Creates a {@link QosSession}. + * + * @param sessionId uniquely identifies the session across all sessions of the same type + * @param sessionType the type of session + */ + public QosSession(final int sessionId, @QosSessionType final int sessionType) { + //Ensures the session id is unique across types of sessions + mSessionId = sessionId; + mSessionType = sessionType; + } + + + @Override + public String toString() { + return "QosSession{" + + "mSessionId=" + mSessionId + + ", mSessionType=" + mSessionType + + '}'; + } + + /** + * Annotations for types of qos sessions. + */ + @IntDef(value = { + TYPE_EPS_BEARER, + }) + @interface QosSessionType {} + + private QosSession(final Parcel in) { + mSessionId = in.readInt(); + mSessionType = in.readInt(); + } + + @NonNull + public static final Creator<QosSession> CREATOR = new Creator<QosSession>() { + @NonNull + @Override + public QosSession createFromParcel(@NonNull final Parcel in) { + return new QosSession(in); + } + + @NonNull + @Override + public QosSession[] newArray(final int size) { + return new QosSession[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeInt(mSessionId); + dest.writeInt(mSessionType); + } +} diff --git a/core/java/android/net/TransportInfo.java b/core/java/android/net/QosSessionAttributes.java index b78d3feccfa0..7a885942d1b5 100644 --- a/core/java/android/net/TransportInfo.java +++ b/core/java/android/net/QosSessionAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * 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. @@ -16,10 +16,15 @@ package android.net; +import android.annotation.SystemApi; + /** - * A container for transport-specific capabilities which is returned by - * {@link NetworkCapabilities#getTransportInfo()}. Specific networks - * may provide concrete implementations of this interface. + * Implemented by classes that encapsulate Qos related attributes that describe a Qos Session. + * + * Use the instanceof keyword to determine the underlying type. + * + * @hide */ -public interface TransportInfo { +@SystemApi +public interface QosSessionAttributes { } diff --git a/core/java/android/net/QosSocketFilter.java b/core/java/android/net/QosSocketFilter.java new file mode 100644 index 000000000000..2080e68f5fba --- /dev/null +++ b/core/java/android/net/QosSocketFilter.java @@ -0,0 +1,166 @@ +/* + * 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.net; + +import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE; +import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.ParcelFileDescriptor; +import android.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.util.Objects; + +/** + * Filters a {@link QosSession} according to the binding on the provided {@link Socket}. + * + * @hide + */ +public class QosSocketFilter extends QosFilter { + + private static final String TAG = QosSocketFilter.class.getSimpleName(); + + @NonNull + private final QosSocketInfo mQosSocketInfo; + + /** + * Creates a {@link QosSocketFilter} based off of {@link QosSocketInfo}. + * + * @param qosSocketInfo the information required to filter and validate + */ + public QosSocketFilter(@NonNull final QosSocketInfo qosSocketInfo) { + Objects.requireNonNull(qosSocketInfo, "qosSocketInfo must be non-null"); + mQosSocketInfo = qosSocketInfo; + } + + /** + * Gets the parcelable qos socket info that was used to create the filter. + */ + @NonNull + public QosSocketInfo getQosSocketInfo() { + return mQosSocketInfo; + } + + /** + * Performs two validations: + * 1. If the socket is not bound, then return + * {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND}. This is detected + * by checking the local address on the filter which becomes null when the socket is no + * longer bound. + * 2. In the scenario that the socket is now bound to a different local address, which can + * happen in the case of UDP, then + * {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED} is returned. + * @return validation error code + */ + @Override + public int validate() { + final InetSocketAddress sa = getAddressFromFileDescriptor(); + if (sa == null) { + return QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND; + } + + if (!sa.equals(mQosSocketInfo.getLocalSocketAddress())) { + return EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED; + } + + return EX_TYPE_FILTER_NONE; + } + + /** + * The local address of the socket's binding. + * + * Note: If the socket is no longer bound, null is returned. + * + * @return the local address + */ + @Nullable + private InetSocketAddress getAddressFromFileDescriptor() { + final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor(); + if (parcelFileDescriptor == null) return null; + + final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor(); + if (fd == null) return null; + + final SocketAddress address; + try { + address = Os.getsockname(fd); + } catch (final ErrnoException e) { + Log.e(TAG, "getAddressFromFileDescriptor: getLocalAddress exception", e); + return null; + } + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } + return null; + } + + /** + * The network used with this filter. + * + * @return the registered {@link Network} + */ + @NonNull + @Override + public Network getNetwork() { + return mQosSocketInfo.getNetwork(); + } + + /** + * @inheritDoc + */ + @Override + public boolean matchesLocalAddress(@NonNull final InetAddress address, final int startPort, + final int endPort) { + if (mQosSocketInfo.getLocalSocketAddress() == null) { + return false; + } + + return matchesLocalAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort, + endPort); + } + + /** + * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} with the + * filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}. + * <p> + * This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked + * due to being final. + * + * @param filterSocketAddress the socket address of the filter + * @param address the address to compare the filterSocketAddressWith + * @param startPort the start of the port range to check + * @param endPort the end of the port range to check + */ + @VisibleForTesting + public static boolean matchesLocalAddress(@NonNull final InetSocketAddress filterSocketAddress, + @NonNull final InetAddress address, + final int startPort, final int endPort) { + return startPort <= filterSocketAddress.getPort() + && endPort >= filterSocketAddress.getPort() + && filterSocketAddress.getAddress().equals(address); + } +} diff --git a/core/java/android/net/QosSocketInfo.aidl b/core/java/android/net/QosSocketInfo.aidl new file mode 100644 index 000000000000..476c0900e23e --- /dev/null +++ b/core/java/android/net/QosSocketInfo.aidl @@ -0,0 +1,21 @@ +/* +** +** 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.net; + +parcelable QosSocketInfo; + diff --git a/core/java/android/net/QosSocketInfo.java b/core/java/android/net/QosSocketInfo.java new file mode 100644 index 000000000000..d37c4691ddde --- /dev/null +++ b/core/java/android/net/QosSocketInfo.java @@ -0,0 +1,154 @@ +/* + * 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.net; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Objects; + +/** + * Used in conjunction with + * {@link ConnectivityManager#registerQosCallback} + * in order to receive Qos Sessions related to the local address and port of a bound {@link Socket}. + * + * @hide + */ +@SystemApi +public final class QosSocketInfo implements Parcelable { + + @NonNull + private final Network mNetwork; + + @NonNull + private final ParcelFileDescriptor mParcelFileDescriptor; + + @NonNull + private final InetSocketAddress mLocalSocketAddress; + + /** + * The {@link Network} the socket is on. + * + * @return the registered {@link Network} + */ + @NonNull + public Network getNetwork() { + return mNetwork; + } + + /** + * The parcel file descriptor wrapped around the socket's file descriptor. + * + * @return the parcel file descriptor of the socket + */ + @NonNull + ParcelFileDescriptor getParcelFileDescriptor() { + return mParcelFileDescriptor; + } + + /** + * The local address of the socket passed into {@link QosSocketInfo(Network, Socket)}. + * The value does not reflect any changes that occur to the socket after it is first set + * in the constructor. + * + * @return the local address of the socket + */ + @NonNull + public InetSocketAddress getLocalSocketAddress() { + return mLocalSocketAddress; + } + + /** + * Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link Socket}. The + * {@link Socket} must remain bound in order to receive {@link QosSession}s. + * + * @param network the network + * @param socket the bound {@link Socket} + */ + public QosSocketInfo(@NonNull final Network network, @NonNull final Socket socket) + throws IOException { + Objects.requireNonNull(socket, "socket cannot be null"); + + mNetwork = Objects.requireNonNull(network, "network cannot be null"); + mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$()); + mLocalSocketAddress = + new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort()); + } + + /* Parcelable methods */ + private QosSocketInfo(final Parcel in) { + mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in)); + mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in); + + final int addressLength = in.readInt(); + mLocalSocketAddress = readSocketAddress(in, addressLength); + } + + private InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) { + final byte[] address = new byte[addressLength]; + in.readByteArray(address); + final int port = in.readInt(); + + try { + return new InetSocketAddress(InetAddress.getByAddress(address), port); + } catch (final UnknownHostException e) { + /* The catch block was purposely left empty. UnknownHostException will never be thrown + since the address provided is numeric and non-null. */ + } + return new InetSocketAddress(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + mNetwork.writeToParcel(dest, 0); + mParcelFileDescriptor.writeToParcel(dest, 0); + + final byte[] address = mLocalSocketAddress.getAddress().getAddress(); + dest.writeInt(address.length); + dest.writeByteArray(address); + dest.writeInt(mLocalSocketAddress.getPort()); + } + + @NonNull + public static final Parcelable.Creator<QosSocketInfo> CREATOR = + new Parcelable.Creator<QosSocketInfo>() { + @NonNull + @Override + public QosSocketInfo createFromParcel(final Parcel in) { + return new QosSocketInfo(in); + } + + @NonNull + @Override + public QosSocketInfo[] newArray(final int size) { + return new QosSocketInfo[size]; + } + }; +} diff --git a/core/java/android/net/SocketLocalAddressChangedException.java b/core/java/android/net/SocketLocalAddressChangedException.java new file mode 100644 index 000000000000..9daad83fd13e --- /dev/null +++ b/core/java/android/net/SocketLocalAddressChangedException.java @@ -0,0 +1,32 @@ +/* + * 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 android.net; + +import android.annotation.SystemApi; + +/** + * Thrown when the local address of the socket has changed. + * + * @hide + */ +@SystemApi +public class SocketLocalAddressChangedException extends Exception { + /** @hide */ + public SocketLocalAddressChangedException() { + super("The local address of the socket changed"); + } +} diff --git a/core/java/android/net/SocketNotBoundException.java b/core/java/android/net/SocketNotBoundException.java new file mode 100644 index 000000000000..b1d7026ac981 --- /dev/null +++ b/core/java/android/net/SocketNotBoundException.java @@ -0,0 +1,32 @@ +/* + * 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 android.net; + +import android.annotation.SystemApi; + +/** + * Thrown when a previously bound socket becomes unbound. + * + * @hide + */ +@SystemApi +public class SocketNotBoundException extends Exception { + /** @hide */ + public SocketNotBoundException() { + super("The socket is unbound"); + } +} diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/net/UnderlyingNetworkInfo.aidl index 6fc97be4095b..a56f2f40583b 100644 --- a/core/java/com/android/internal/net/VpnInfo.aidl +++ b/core/java/android/net/UnderlyingNetworkInfo.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package com.android.internal.net; +package android.net; -parcelable VpnInfo; +parcelable UnderlyingNetworkInfo; diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java new file mode 100644 index 000000000000..8fb4832e06c8 --- /dev/null +++ b/core/java/android/net/UnderlyingNetworkInfo.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 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.net; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A lightweight container used to carry information on the networks that underly a given + * virtual network. + * + * @hide + */ +public final class UnderlyingNetworkInfo implements Parcelable { + /** The owner of this network. */ + public final int ownerUid; + /** The interface name of this network. */ + @NonNull + public final String iface; + /** The names of the interfaces underlying this network. */ + @NonNull + public final List<String> underlyingIfaces; + + public UnderlyingNetworkInfo(int ownerUid, @NonNull String iface, + @NonNull List<String> underlyingIfaces) { + Objects.requireNonNull(iface); + Objects.requireNonNull(underlyingIfaces); + this.ownerUid = ownerUid; + this.iface = iface; + this.underlyingIfaces = underlyingIfaces; + } + + private UnderlyingNetworkInfo(@NonNull Parcel in) { + this.ownerUid = in.readInt(); + this.iface = in.readString(); + this.underlyingIfaces = new ArrayList<>(); + in.readList(this.underlyingIfaces, null /*classLoader*/); + } + + @Override + public String toString() { + return "UnderlyingNetworkInfo{" + + "ownerUid=" + ownerUid + + ", iface='" + iface + '\'' + + ", underlyingIfaces='" + underlyingIfaces.toString() + '\'' + + '}'; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(ownerUid); + dest.writeString(iface); + dest.writeList(underlyingIfaces); + } + + @NonNull + public static final Parcelable.Creator<UnderlyingNetworkInfo> CREATOR = + new Parcelable.Creator<UnderlyingNetworkInfo>() { + @NonNull + @Override + public UnderlyingNetworkInfo createFromParcel(@NonNull Parcel in) { + return new UnderlyingNetworkInfo(in); + } + + @NonNull + @Override + public UnderlyingNetworkInfo[] newArray(int size) { + return new UnderlyingNetworkInfo[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UnderlyingNetworkInfo)) return false; + final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o; + return ownerUid == that.ownerUid + && Objects.equals(iface, that.iface) + && Objects.equals(underlyingIfaces, that.underlyingIfaces); + } + + @Override + public int hashCode() { + return Objects.hash(ownerUid, iface, underlyingIfaces); + } +} diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java index 250cff29944a..a22d41a5ef86 100644 --- a/core/java/android/net/http/SslCertificate.java +++ b/core/java/android/net/http/SslCertificate.java @@ -26,7 +26,7 @@ import android.view.View; import android.widget.TextView; import com.android.internal.util.HexDump; -import com.android.org.bouncycastle.asn1.x509.X509Name; +import com.android.internal.org.bouncycastle.asn1.x509.X509Name; import java.io.ByteArrayInputStream; import java.math.BigInteger; diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java index ab12cdd22685..3d79f284fcd3 100644 --- a/core/java/android/net/metrics/ApfProgramEvent.java +++ b/core/java/android/net/metrics/ApfProgramEvent.java @@ -39,7 +39,11 @@ import java.util.List; * An event logged when there is a change or event that requires updating the * the APF program in place with a new APF program. * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class ApfProgramEvent implements IpConnectivityLog.Event { diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java index fcafb7ebd676..a32d3a65b73a 100644 --- a/core/java/android/net/metrics/ApfStats.java +++ b/core/java/android/net/metrics/ApfStats.java @@ -27,7 +27,11 @@ import android.os.Parcelable; /** * An event logged for an interface with APF capabilities when its IpClient state machine exits. * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class ApfStats implements IpConnectivityLog.Event { diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java index 8de427de1dab..e175d587c137 100644 --- a/core/java/android/net/metrics/DhcpClientEvent.java +++ b/core/java/android/net/metrics/DhcpClientEvent.java @@ -28,7 +28,11 @@ import android.text.TextUtils; /** * An event recorded when a DhcpClient state machine transitions to a new state. * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class DhcpClientEvent implements IpConnectivityLog.Event { diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java index de3129d5e94d..7dd0696d81a3 100644 --- a/core/java/android/net/metrics/DhcpErrorEvent.java +++ b/core/java/android/net/metrics/DhcpErrorEvent.java @@ -27,7 +27,11 @@ import com.android.internal.util.MessageUtils; /** * Event class used to record error events when parsing DHCP response packets. * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class DhcpErrorEvent implements IpConnectivityLog.Event { public static final int L2_ERROR = 1; diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java index a008d855025a..5cadb45590bb 100644 --- a/core/java/android/net/metrics/IpConnectivityLog.java +++ b/core/java/android/net/metrics/IpConnectivityLog.java @@ -17,10 +17,13 @@ package android.net.metrics; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; +import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; @@ -32,7 +35,11 @@ import com.android.internal.util.BitUtils; /** * Class for logging IpConnectvity events with IpConnectivityMetrics * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public class IpConnectivityLog { private static final String TAG = IpConnectivityLog.class.getSimpleName(); @@ -66,6 +73,9 @@ public class IpConnectivityLog { final IIpConnectivityMetrics service = IIpConnectivityMetrics.Stub.asInterface(ServiceManager.getService(SERVICE_NAME)); if (service == null) { + if (DBG) { + Log.d(TAG, SERVICE_NAME + " service was not ready"); + } return false; } // Two threads racing here will write the same pointer because getService @@ -83,9 +93,6 @@ public class IpConnectivityLog { */ public boolean log(@NonNull ConnectivityMetricsEvent ev) { if (!checkLoggerService()) { - if (DBG) { - Log.d(TAG, SERVICE_NAME + " service was not ready"); - } return false; } if (ev.timestamp == 0) { @@ -134,7 +141,7 @@ public class IpConnectivityLog { * @return true if the event was successfully logged. */ public boolean log(@NonNull Network network, @NonNull int[] transports, @NonNull Event data) { - return log(network.netId, transports, data); + return log(network.getNetId(), transports, data); } /** @@ -161,6 +168,56 @@ public class IpConnectivityLog { return log(makeEv(data)); } + /** + * Logs the validation status of the default network. + * @param valid whether the current default network was validated (i.e., whether it had + * {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED} + * @return true if the event was successfully logged. + * @hide + */ + public boolean logDefaultNetworkValidity(boolean valid) { + if (!checkLoggerService()) { + return false; + } + try { + mService.logDefaultNetworkValidity(valid); + } catch (RemoteException ignored) { + // Only called within the system server. + } + return true; + } + + /** + * Logs a change in the default network. + * + * @param defaultNetwork the current default network + * @param score the current score of {@code defaultNetwork} + * @param lp the {@link LinkProperties} of {@code defaultNetwork} + * @param nc the {@link NetworkCapabilities} of the {@code defaultNetwork} + * @param validated whether {@code defaultNetwork} network is validated + * @param previousDefaultNetwork the previous default network + * @param previousScore the score of {@code previousDefaultNetwork} + * @param previousLp the {@link LinkProperties} of {@code previousDefaultNetwork} + * @param previousNc the {@link NetworkCapabilities} of {@code previousDefaultNetwork} + * @return true if the event was successfully logged. + * @hide + */ + public boolean logDefaultNetworkEvent(@Nullable Network defaultNetwork, int score, + boolean validated, @Nullable LinkProperties lp, @Nullable NetworkCapabilities nc, + @Nullable Network previousDefaultNetwork, int previousScore, + @Nullable LinkProperties previousLp, @Nullable NetworkCapabilities previousNc) { + if (!checkLoggerService()) { + return false; + } + try { + mService.logDefaultNetworkEvent(defaultNetwork, score, validated, lp, nc, + previousDefaultNetwork, previousScore, previousLp, previousNc); + } catch (RemoteException ignored) { + // Only called within the system server. + } + return true; + } + private static ConnectivityMetricsEvent makeEv(Event data) { ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); ev.data = data; diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java index 4f7f3263117b..3abcc0589dc1 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -33,7 +33,11 @@ import java.lang.annotation.RetentionPolicy; * An event recorded by IpClient when IP provisioning completes for a network or * when a network disconnects. * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class IpManagerEvent implements IpConnectivityLog.Event { diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java index d5003badd614..0b65bbdbcbf6 100644 --- a/core/java/android/net/metrics/IpReachabilityEvent.java +++ b/core/java/android/net/metrics/IpReachabilityEvent.java @@ -29,7 +29,11 @@ import com.android.internal.util.MessageUtils; * An event recorded when IpReachabilityMonitor sends a neighbor probe or receives * a neighbor probe result. * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class IpReachabilityEvent implements IpConnectivityLog.Event { diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java index 8c28f7a7d643..47eeeff90088 100644 --- a/core/java/android/net/metrics/NetworkEvent.java +++ b/core/java/android/net/metrics/NetworkEvent.java @@ -31,7 +31,11 @@ import java.lang.annotation.RetentionPolicy; /** * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class NetworkEvent implements IpConnectivityLog.Event { diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java index b54874f5a573..05a47e55fce4 100644 --- a/core/java/android/net/metrics/RaEvent.java +++ b/core/java/android/net/metrics/RaEvent.java @@ -25,7 +25,11 @@ import android.os.Parcelable; /** * An event logged when the APF packet socket receives an RA packet. * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class RaEvent implements IpConnectivityLog.Event { diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java index 7f4e4a73677e..8118fe005d5d 100644 --- a/core/java/android/net/metrics/ValidationProbeEvent.java +++ b/core/java/android/net/metrics/ValidationProbeEvent.java @@ -32,7 +32,11 @@ import java.lang.annotation.RetentionPolicy; /** * An event recorded by NetworkMonitor when sending a probe for finding captive portals. * {@hide} + * @deprecated The event may not be sent in Android S and above. The events + * are logged by a single caller in the system using signature permissions + * and that caller is migrating to statsd. */ +@Deprecated @SystemApi public final class ValidationProbeEvent implements IpConnectivityLog.Event { diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl index 04b585cdf420..4f293eeb3c3b 100644 --- a/core/java/android/net/vcn/IVcnManagementService.aidl +++ b/core/java/android/net/vcn/IVcnManagementService.aidl @@ -16,7 +16,11 @@ package android.net.vcn; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; +import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; +import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.os.ParcelUuid; /** @@ -25,4 +29,8 @@ import android.os.ParcelUuid; interface IVcnManagementService { void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName); void clearVcnConfig(in ParcelUuid subscriptionGroup); + + void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); + void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); + VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp); } diff --git a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl new file mode 100644 index 000000000000..f8ae492016f0 --- /dev/null +++ b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl @@ -0,0 +1,22 @@ +/* + * 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.net.vcn; + +/** @hide */ +interface IVcnUnderlyingNetworkPolicyListener { + void onPolicyChanged(); +}
\ No newline at end of file diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java index ede8faaaf261..5eb4ba6a2f8e 100644 --- a/core/java/android/net/vcn/VcnConfig.java +++ b/core/java/android/net/vcn/VcnConfig.java @@ -96,7 +96,11 @@ public final class VcnConfig implements Parcelable { return mPackageName; } - /** Retrieves the set of configured tunnels. */ + /** + * Retrieves the set of configured tunnels. + * + * @hide + */ @NonNull public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() { return Collections.unmodifiableSet(mGatewayConnectionConfigs); @@ -146,7 +150,7 @@ public final class VcnConfig implements Parcelable { } @Override - public void writeToParcel(Parcel out, int flags) { + public void writeToParcel(@NonNull Parcel out, int flags) { out.writeParcelable(toPersistableBundle(), flags); } @@ -164,8 +168,12 @@ public final class VcnConfig implements Parcelable { } }; - /** This class is used to incrementally build {@link VcnConfig} objects. */ - public static class Builder { + /** + * This class is used to incrementally build {@link VcnConfig} objects. + * + * @hide + */ + public static final class Builder { @NonNull private final String mPackageName; @NonNull @@ -182,6 +190,7 @@ public final class VcnConfig implements Parcelable { * * @param gatewayConnectionConfig the configuration for an individual gateway connection * @return this {@link Builder} instance, for chaining + * @hide */ @NonNull public Builder addGatewayConnectionConfig( @@ -196,6 +205,7 @@ public final class VcnConfig implements Parcelable { * Builds and validates the VcnConfig. * * @return an immutable VcnConfig instance + * @hide */ @NonNull public VcnConfig build() { diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 039360a69a3a..cead2f1caad1 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -15,10 +15,9 @@ */ package android.net.vcn; -import static android.net.NetworkCapabilities.NetCapability; - import static com.android.internal.annotations.VisibleForTesting.Visibility; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,14 +26,19 @@ import android.os.PersistableBundle; import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.server.vcn.util.PersistableBundleUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Objects; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.concurrent.TimeUnit; /** @@ -99,6 +103,26 @@ public final class VcnGatewayConnectionConfig { ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps); } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"NET_CAPABILITY_"}, + value = { + NetworkCapabilities.NET_CAPABILITY_MMS, + NetworkCapabilities.NET_CAPABILITY_SUPL, + NetworkCapabilities.NET_CAPABILITY_DUN, + NetworkCapabilities.NET_CAPABILITY_FOTA, + NetworkCapabilities.NET_CAPABILITY_IMS, + NetworkCapabilities.NET_CAPABILITY_CBS, + NetworkCapabilities.NET_CAPABILITY_IA, + NetworkCapabilities.NET_CAPABILITY_RCS, + NetworkCapabilities.NET_CAPABILITY_XCAP, + NetworkCapabilities.NET_CAPABILITY_EIMS, + NetworkCapabilities.NET_CAPABILITY_INTERNET, + NetworkCapabilities.NET_CAPABILITY_MCX, + }) + public @interface VcnSupportedCapability {} + private static final int DEFAULT_MAX_MTU = 1500; /** @@ -130,10 +154,10 @@ public final class VcnGatewayConnectionConfig { }; private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities"; - @NonNull private final Set<Integer> mExposedCapabilities; + @NonNull private final SortedSet<Integer> mExposedCapabilities; private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities"; - @NonNull private final Set<Integer> mUnderlyingCapabilities; + @NonNull private final SortedSet<Integer> mUnderlyingCapabilities; // TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig @@ -143,14 +167,14 @@ public final class VcnGatewayConnectionConfig { private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs"; @NonNull private final long[] mRetryIntervalsMs; - @VisibleForTesting(visibility = Visibility.PRIVATE) - public VcnGatewayConnectionConfig( + /** Builds a VcnGatewayConnectionConfig with the specified parameters. */ + private VcnGatewayConnectionConfig( @NonNull Set<Integer> exposedCapabilities, @NonNull Set<Integer> underlyingCapabilities, @NonNull long[] retryIntervalsMs, @IntRange(from = MIN_MTU_V6) int maxMtu) { - mExposedCapabilities = exposedCapabilities; - mUnderlyingCapabilities = underlyingCapabilities; + mExposedCapabilities = new TreeSet(exposedCapabilities); + mUnderlyingCapabilities = new TreeSet(underlyingCapabilities); mRetryIntervalsMs = retryIntervalsMs; mMaxMtu = maxMtu; @@ -165,9 +189,9 @@ public final class VcnGatewayConnectionConfig { final PersistableBundle underlyingCapsBundle = in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY); - mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList( + mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList( exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); - mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList( + mUnderlyingCapabilities = new TreeSet<>(PersistableBundleUtils.toList( underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY); mMaxMtu = in.getInt(MAX_MTU_KEY); @@ -221,52 +245,93 @@ public final class VcnGatewayConnectionConfig { /** * Returns all exposed capabilities. * + * <p>The returned integer-value capabilities will not contain duplicates, and will be sorted in + * ascending numerical order. + * + * @see Builder#addExposedCapability(int) + * @see Builder#clearExposedCapability(int) * @hide */ @NonNull + public int[] getExposedCapabilities() { + // Sorted set guarantees ordering + return ArrayUtils.convertToIntArray(new ArrayList<>(mExposedCapabilities)); + } + + /** + * Returns all exposed capabilities. + * + * <p>Left to prevent the need to make major changes while changes are actively in flight. + * + * @deprecated use getExposedCapabilities() instead + * @hide + */ + @Deprecated + @NonNull public Set<Integer> getAllExposedCapabilities() { return Collections.unmodifiableSet(mExposedCapabilities); } /** - * Checks if this config is configured to support/expose a specific capability. + * Returns all capabilities required of underlying networks. + * + * <p>The returned integer-value capabilities will be sorted in ascending numerical order. * - * @param capability the capability to check for + * @see Builder#addRequiredUnderlyingCapability(int) + * @see Builder#clearRequiredUnderlyingCapability(int) + * @hide */ - public boolean hasExposedCapability(@NetCapability int capability) { - checkValidCapability(capability); - - return mExposedCapabilities.contains(capability); + @NonNull + public int[] getRequiredUnderlyingCapabilities() { + // Sorted set guarantees ordering + return ArrayUtils.convertToIntArray(new ArrayList<>(mUnderlyingCapabilities)); } /** * Returns all capabilities required of underlying networks. * + * <p>Left to prevent the need to make major changes while changes are actively in flight. + * + * @deprecated use getRequiredUnderlyingCapabilities() instead * @hide */ + @Deprecated @NonNull public Set<Integer> getAllUnderlyingCapabilities() { return Collections.unmodifiableSet(mUnderlyingCapabilities); } /** - * Checks if this config requires an underlying network to have the specified capability. + * Retrieves the configured retry intervals. * - * @param capability the capability to check for + * @see Builder#setRetryInterval(long[]) + * @hide */ - public boolean requiresUnderlyingCapability(@NetCapability int capability) { - checkValidCapability(capability); - - return mUnderlyingCapabilities.contains(capability); + @NonNull + public long[] getRetryInterval() { + return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length); } - /** Retrieves the configured retry intervals. */ + /** + * Retrieves the configured retry intervals. + * + * <p>Left to prevent the need to make major changes while changes are actively in flight. + * + * @deprecated use getRequiredUnderlyingCapabilities() instead + * @hide + */ + @Deprecated @NonNull public long[] getRetryIntervalsMs() { - return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length); + return getRetryInterval(); } - /** Retrieves the maximum MTU allowed for this Gateway Connection. */ + /** + * Retrieves the maximum MTU allowed for this Gateway Connection. + * + * @see Builder.setMaxMtu(int) + * @hide + */ @IntRange(from = MIN_MTU_V6) public int getMaxMtu() { return mMaxMtu; @@ -321,8 +386,12 @@ public final class VcnGatewayConnectionConfig { && mMaxMtu == rhs.mMaxMtu; } - /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */ - public static class Builder { + /** + * This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. + * + * @hide + */ + public static final class Builder { @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); @NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet(); @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS; @@ -340,8 +409,10 @@ public final class VcnGatewayConnectionConfig { * @return this {@link Builder} instance, for chaining * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway * Connection + * @hide */ - public Builder addExposedCapability(@NetCapability int exposedCapability) { + @NonNull + public Builder addExposedCapability(@VcnSupportedCapability int exposedCapability) { checkValidCapability(exposedCapability); mExposedCapabilities.add(exposedCapability); @@ -356,8 +427,10 @@ public final class VcnGatewayConnectionConfig { * @return this {@link Builder} instance, for chaining * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway * Connection + * @hide */ - public Builder removeExposedCapability(@NetCapability int exposedCapability) { + @NonNull + public Builder clearExposedCapability(@VcnSupportedCapability int exposedCapability) { checkValidCapability(exposedCapability); mExposedCapabilities.remove(exposedCapability); @@ -372,8 +445,11 @@ public final class VcnGatewayConnectionConfig { * @return this {@link Builder} instance, for chaining * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying * networks + * @hide */ - public Builder addRequiredUnderlyingCapability(@NetCapability int underlyingCapability) { + @NonNull + public Builder addRequiredUnderlyingCapability( + @VcnSupportedCapability int underlyingCapability) { checkValidCapability(underlyingCapability); mUnderlyingCapabilities.add(underlyingCapability); @@ -392,8 +468,11 @@ public final class VcnGatewayConnectionConfig { * @return this {@link Builder} instance, for chaining * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying * networks + * @hide */ - public Builder removeRequiredUnderlyingCapability(@NetCapability int underlyingCapability) { + @NonNull + public Builder clearRequiredUnderlyingCapability( + @VcnSupportedCapability int underlyingCapability) { checkValidCapability(underlyingCapability); mUnderlyingCapabilities.remove(underlyingCapability); @@ -422,6 +501,7 @@ public final class VcnGatewayConnectionConfig { * 15m]} * @return this {@link Builder} instance, for chaining * @see VcnManager for additional discussion on fail-safe mode + * @hide */ @NonNull public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) { @@ -443,6 +523,7 @@ public final class VcnGatewayConnectionConfig { * @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than * the IPv6 minimum MTU of 1280. Defaults to 1500. * @return this {@link Builder} instance, for chaining + * @hide */ @NonNull public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) { @@ -457,6 +538,7 @@ public final class VcnGatewayConnectionConfig { * Builds and validates the VcnGatewayConnectionConfig. * * @return an immutable VcnGatewayConnectionConfig instance + * @hide */ @NonNull public VcnGatewayConnectionConfig build() { diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index b881a339535b..33beb6a9d188 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -21,11 +21,18 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceSpecificException; +import com.android.internal.annotations.VisibleForTesting; + import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; /** * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks. @@ -57,9 +64,15 @@ import java.io.IOException; * @hide */ @SystemService(Context.VCN_MANAGEMENT_SERVICE) -public final class VcnManager { +public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); + /** @hide */ + @VisibleForTesting + public static final Map< + VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); + @NonNull private final Context mContext; @NonNull private final IVcnManagementService mService; @@ -136,4 +149,132 @@ public final class VcnManager { throw e.rethrowFromSystemServer(); } } + + // TODO: make VcnUnderlyingNetworkPolicyListener @SystemApi + /** + * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components + * can register to receive updates for VCN-underlying Network policies from the System Server. + * + * @hide + */ + public interface VcnUnderlyingNetworkPolicyListener { + /** + * Notifies the implementation that the VCN's underlying Network policy has changed. + * + * <p>After receiving this callback, implementations MUST poll VcnManager for the updated + * VcnUnderlyingNetworkPolicy via VcnManager#getUnderlyingNetworkPolicy. + */ + void onPolicyChanged(); + } + + /** + * Add a listener for VCN-underlying network policy updates. + * + * @param executor the Executor that will be used for invoking all calls to the specified + * Listener + * @param listener the VcnUnderlyingNetworkPolicyListener to be added + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @throws IllegalArgumentException if the specified VcnUnderlyingNetworkPolicyListener is + * already registered + * @hide + */ + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void addVcnUnderlyingNetworkPolicyListener( + @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { + requireNonNull(executor, "executor must not be null"); + requireNonNull(listener, "listener must not be null"); + + VcnUnderlyingNetworkPolicyListenerBinder binder = + new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener); + if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) { + throw new IllegalArgumentException( + "Attempting to add a listener that is already in use"); + } + + try { + mService.addVcnUnderlyingNetworkPolicyListener(binder); + } catch (RemoteException e) { + REGISTERED_POLICY_LISTENERS.remove(listener); + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. + * + * <p>If the specified listener is not currently registered, this is a no-op. + * + * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed + * @hide + */ + public void removeVcnUnderlyingNetworkPolicyListener( + @NonNull VcnUnderlyingNetworkPolicyListener listener) { + requireNonNull(listener, "listener must not be null"); + + VcnUnderlyingNetworkPolicyListenerBinder binder = + REGISTERED_POLICY_LISTENERS.remove(listener); + if (binder == null) { + return; + } + + try { + mService.removeVcnUnderlyingNetworkPolicyListener(binder); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Queries the underlying network policy for a network with the given parameters. + * + * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy + * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network + * Provider MUST poll for the updated Network policy based on that Network's capabilities and + * properties. + * + * @param networkCapabilities the NetworkCapabilities to be used in determining the Network + * policy for this Network. + * @param linkProperties the LinkProperties to be used in determining the Network policy for + * this Network. + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @return the VcnUnderlyingNetworkPolicy to be used for this Network. + * @hide + */ + @NonNull + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties) { + requireNonNull(networkCapabilities, "networkCapabilities must not be null"); + requireNonNull(linkProperties, "linkProperties must not be null"); + + try { + return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System + * Server. + * + * @hide + */ + private static class VcnUnderlyingNetworkPolicyListenerBinder + extends IVcnUnderlyingNetworkPolicyListener.Stub { + @NonNull private final Executor mExecutor; + @NonNull private final VcnUnderlyingNetworkPolicyListener mListener; + + private VcnUnderlyingNetworkPolicyListenerBinder( + Executor executor, VcnUnderlyingNetworkPolicyListener listener) { + mExecutor = executor; + mListener = listener; + } + + @Override + public void onPolicyChanged() { + mExecutor.execute(() -> mListener.onPolicyChanged()); + } + } } diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java new file mode 100644 index 000000000000..4d8cf91621ba --- /dev/null +++ b/core/java/android/net/vcn/VcnTransportInfo.java @@ -0,0 +1,125 @@ +/* + * 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 android.net.vcn; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.TransportInfo; +import android.net.wifi.WifiInfo; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.SubscriptionManager; + +import java.util.Objects; + +/** + * VcnTransportInfo contains information about the VCN's underlying transports for SysUi. + * + * <p>Presence of this class in the NetworkCapabilities.TransportInfo implies that the network is a + * VCN. + * + * <p>VcnTransportInfo must exist on top of either an underlying Wifi or Cellular Network. If the + * underlying Network is WiFi, the subId will be {@link + * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. If the underlying Network is Cellular, the WifiInfo + * will be {@code null}. + * + * @hide + */ +public class VcnTransportInfo implements TransportInfo, Parcelable { + @Nullable private final WifiInfo mWifiInfo; + private final int mSubId; + + public VcnTransportInfo(@NonNull WifiInfo wifiInfo) { + this(wifiInfo, SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + + public VcnTransportInfo(int subId) { + this(null /* wifiInfo */, subId); + } + + private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) { + if (wifiInfo == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + throw new IllegalArgumentException( + "VcnTransportInfo requires either non-null WifiInfo or valid subId"); + } + + mWifiInfo = wifiInfo; + mSubId = subId; + } + + /** + * Get the {@link WifiInfo} for this VcnTransportInfo. + * + * <p>If the underlying Network for the associated VCN is Cellular, returns null. + * + * @return the WifiInfo if there is an underlying WiFi connection, else null. + */ + @Nullable + public WifiInfo getWifiInfo() { + return mWifiInfo; + } + + /** + * Get the subId for the VCN Network associated with this VcnTransportInfo. + * + * <p>If the underlying Network for the associated VCN is WiFi, returns {@link + * SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * + * @return the Subscription ID if a cellular underlying Network is present, else {@link + * android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID}. + */ + public int getSubId() { + return mSubId; + } + + @Override + public int hashCode() { + return Objects.hash(mWifiInfo, mSubId); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VcnTransportInfo)) return false; + final VcnTransportInfo that = (VcnTransportInfo) o; + + return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId; + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) {} + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<VcnTransportInfo> CREATOR = + new Creator<VcnTransportInfo>() { + public VcnTransportInfo createFromParcel(Parcel in) { + // return null instead of a default VcnTransportInfo to avoid leaking + // information about this being a VCN Network (instead of macro cellular, etc) + return null; + } + + public VcnTransportInfo[] newArray(int size) { + return new VcnTransportInfo[size]; + } + }; +} diff --git a/apex/permission/framework/java/android/permission/PermissionState.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl index e810db8ecbfe..6cb6ee685a64 100644 --- a/apex/permission/framework/java/android/permission/PermissionState.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl @@ -14,9 +14,7 @@ * limitations under the License. */ -package android.permission; +package android.net.vcn; -/** - * @hide - */ -public class PermissionState {} +/** @hide */ +parcelable VcnUnderlyingNetworkPolicy; diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java new file mode 100644 index 000000000000..dd7c86d87ff2 --- /dev/null +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java @@ -0,0 +1,110 @@ +/* + * 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.net.vcn; + +import android.annotation.NonNull; +import android.net.NetworkCapabilities; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * VcnUnderlyingNetworkPolicy represents the Network policy for a VCN-managed Network. + * + * <p>Transports that are bringing up networks capable of acting as a VCN's underlying network + * should query for policy state upon major capability changes (e.g. changing of TRUSTED bit), and + * when prompted by VcnManagementService via VcnUnderlyingNetworkPolicyListener. + * + * @hide + */ +public final class VcnUnderlyingNetworkPolicy implements Parcelable { + private final boolean mIsTearDownRequested; + private final NetworkCapabilities mMergedNetworkCapabilities; + + /** + * Constructs a VcnUnderlyingNetworkPolicy with the specified parameters. + * + * @hide + */ + public VcnUnderlyingNetworkPolicy( + boolean isTearDownRequested, @NonNull NetworkCapabilities mergedNetworkCapabilities) { + Objects.requireNonNull( + mergedNetworkCapabilities, "mergedNetworkCapabilities must be nonnull"); + + mIsTearDownRequested = isTearDownRequested; + mMergedNetworkCapabilities = mergedNetworkCapabilities; + } + + /** + * Returns whether this Carrier VCN policy policy indicates that the underlying Network should + * be torn down. + */ + public boolean isTeardownRequested() { + return mIsTearDownRequested; + } + + /** + * Returns the NetworkCapabilities with Carrier VCN policy bits merged into the provided + * capabilities. + */ + @NonNull + public NetworkCapabilities getMergedNetworkCapabilities() { + return mMergedNetworkCapabilities; + } + + @Override + public int hashCode() { + return Objects.hash(mIsTearDownRequested, mMergedNetworkCapabilities); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VcnUnderlyingNetworkPolicy)) return false; + final VcnUnderlyingNetworkPolicy that = (VcnUnderlyingNetworkPolicy) o; + + return mIsTearDownRequested == that.mIsTearDownRequested + && mMergedNetworkCapabilities.equals(that.mMergedNetworkCapabilities); + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBoolean(mIsTearDownRequested); + dest.writeParcelable(mMergedNetworkCapabilities, flags); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR = + new Creator<VcnUnderlyingNetworkPolicy>() { + public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) { + return new VcnUnderlyingNetworkPolicy( + in.readBoolean(), in.readParcelable(null)); + } + + public VcnUnderlyingNetworkPolicy[] newArray(int size) { + return new VcnUnderlyingNetworkPolicy[size]; + } + }; +} diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 0b2cfdd9ece3..bc3d5c4ab1ac 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -72,4 +72,7 @@ interface INfcAdapter boolean deviceSupportsNfcSecure(); boolean setNfcSecure(boolean enable); + boolean setAlwaysOn(boolean value); + boolean isAlwaysOnEnabled(); + boolean isAlwaysOnSupported(); } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index a17a5370e787..e85eb935a8e7 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -350,6 +350,22 @@ public final class NfcAdapter { "android.nfc.extra.HANDOVER_TRANSFER_STATUS"; /** @hide */ + public static final String ACTION_ALWAYS_ON_STATE_CHANGED = + "android.nfc.action.ALWAYS_ON_STATE_CHANGED"; + + /** + * Used as an int extra field in {@link #ACTION_ALWAYS_ON_STATE_CHANGED} + * intents to request the current power state. Possible values are: + * {@link #STATE_OFF}, + * {@link #STATE_TURNING_ON}, + * {@link #STATE_ON}, + * {@link #STATE_TURNING_OFF}, + * @hide + */ + public static final String EXTRA_ALWAYS_ON_STATE = + "android.nfc.extra.ALWAYS_ON_STATE"; + + /** @hide */ public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0; /** @hide */ public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1; @@ -358,6 +374,14 @@ public final class NfcAdapter { public static final String EXTRA_HANDOVER_TRANSFER_URI = "android.nfc.extra.HANDOVER_TRANSFER_URI"; + /** + * Broadcast Action: Notify possible NFC transaction blocked because device is locked. + * <p>An external NFC field detected when device locked and SecureNfc enabled. + * @hide + */ + public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = + "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC"; + // Guarded by NfcAdapter.class static boolean sIsInitialized = false; static boolean sHasNfcFeature; @@ -2211,4 +2235,106 @@ public final class NfcAdapter { return mContext.getApplicationInfo().targetSdkVersion; } } + + /** + * Sets NFC controller always on feature. + * <p>This API is for the NFCC internal state management. It allows to discriminate + * the controller function from the NFC function by keeping the NFC Controller on without + * any NFC RF enabled if necessary. + * <p>This call is asynchronous. Listen for {@link #ACTION_ALWAYS_ON_STATE_CHANGED} + * broadcasts to find out when the operation is complete. + * <p>If this returns true, then either NFCC is already on, or + * a {@link #ACTION_ALWAYS_ON_STATE_CHANGED} broadcast will be sent to indicate + * a state transition. + * If this returns false, then there is some problem that prevents an attempt to turn NFCC on. + * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is + * disabled), if false the NFCC will follow completely the Nfc adapter state. + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @return void + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public boolean setAlwaysOn(boolean value) { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + try { + return sService.setAlwaysOn(value); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + // Try one more time + if (sService == null) { + Log.e(TAG, "Failed to recover NFC Service."); + return false; + } + try { + return sService.setAlwaysOn(value); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to recover NFC Service."); + } + return false; + } + } + + /** + * Checks NFC controller always on feature is enabled. + * + * @return True if NFC controller always on is enabled, false otherwise + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @hide + */ + + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public boolean isAlwaysOnEnabled() { + try { + return sService.isAlwaysOnEnabled(); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + // Try one more time + if (sService == null) { + Log.e(TAG, "Failed to recover NFC Service."); + return false; + } + try { + return sService.isAlwaysOnEnabled(); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to recover NFC Service."); + } + return false; + } + } + + /** + * Checks if the device supports NFC controller always on functionality. + * + * @return True if device supports NFC controller always on, false otherwise + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + * @hide + */ + + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public boolean isAlwaysOnSupported() { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + try { + return sService.isAlwaysOnSupported(); + } catch (RemoteException e) { + attemptDeadServiceRecovery(e); + // Try one more time + if (sService == null) { + Log.e(TAG, "Failed to recover NFC Service."); + return false; + } + try { + return sService.isAlwaysOnSupported(); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to recover NFC Service."); + } + return false; + } + } } diff --git a/core/java/android/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java index fdccaae9cb1b..225636565480 100644 --- a/core/java/android/nfc/tech/Ndef.java +++ b/core/java/android/nfc/tech/Ndef.java @@ -112,7 +112,7 @@ public final class Ndef extends BasicTagTechnology { public static final String NFC_FORUM_TYPE_1 = "org.nfcforum.ndef.type1"; /** NFC Forum Tag Type 2 */ public static final String NFC_FORUM_TYPE_2 = "org.nfcforum.ndef.type2"; - /** NFC Forum Tag Type 4 */ + /** NFC Forum Tag Type 3 */ public static final String NFC_FORUM_TYPE_3 = "org.nfcforum.ndef.type3"; /** NFC Forum Tag Type 4 */ public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4"; diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 46ad7b880a37..305c686f8657 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -22,11 +22,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.ActivityManager; import android.content.Context; -import android.os.Handler; import android.util.Log; import android.widget.Toast; @@ -41,12 +41,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; -/** - * Class that provides a privileged API to capture and consume bugreports. - * - * @hide - */ -@SystemApi +/** Class that provides a privileged API to capture and consume bugreports. */ @SystemService(Context.BUGREPORT_SERVICE) public final class BugreportManager { @@ -61,28 +56,30 @@ public final class BugreportManager { mBinder = binder; } - /** - * An interface describing the callback for bugreport progress and status. - */ + /** An interface describing the callback for bugreport progress and status. */ public abstract static class BugreportCallback { - /** @hide */ + /** + * Possible error codes taking a bugreport can encounter. + * + * @hide + */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = { "BUGREPORT_ERROR_" }, value = { - BUGREPORT_ERROR_INVALID_INPUT, - BUGREPORT_ERROR_RUNTIME, - BUGREPORT_ERROR_USER_DENIED_CONSENT, - BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT, - BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS - }) - - /** Possible error codes taking a bugreport can encounter */ + @IntDef( + prefix = {"BUGREPORT_ERROR_"}, + value = { + BUGREPORT_ERROR_INVALID_INPUT, + BUGREPORT_ERROR_RUNTIME, + BUGREPORT_ERROR_USER_DENIED_CONSENT, + BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT, + BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS + }) public @interface BugreportErrorCode {} /** The input options were invalid */ public static final int BUGREPORT_ERROR_INVALID_INPUT = IDumpstateListener.BUGREPORT_ERROR_INVALID_INPUT; - /** A runtime error occured */ + /** A runtime error occurred */ public static final int BUGREPORT_ERROR_RUNTIME = IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR; @@ -100,6 +97,7 @@ public final class BugreportManager { /** * Called when there is a progress update. + * * @param progress the progress in [0.0, 100.0] */ public void onProgress(@FloatRange(from = 0f, to = 100f) float progress) {} @@ -114,14 +112,12 @@ public final class BugreportManager { * out, but the bugreport could be available in the internal directory of dumpstate for * manual retrieval. * - * <p> If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the - * caller should try later, as only one bugreport can be in progress at a time. + * <p>If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the caller + * should try later, as only one bugreport can be in progress at a time. */ public void onError(@BugreportErrorCode int errorCode) {} - /** - * Called when taking bugreport finishes successfully. - */ + /** Called when taking bugreport finishes successfully. */ public void onFinished() {} /** @@ -138,20 +134,23 @@ public final class BugreportManager { * seconds to return in the worst case. {@code callback} will receive progress and status * updates. * - * <p>The bugreport artifacts will be copied over to the given file descriptors only if the - * user consents to sharing with the calling app. + * <p>The bugreport artifacts will be copied over to the given file descriptors only if the user + * consents to sharing with the calling app. * * <p>{@link BugreportManager} takes ownership of {@code bugreportFd} and {@code screenshotFd}. * - * @param bugreportFd file to write the bugreport. This should be opened in write-only, - * append mode. - * @param screenshotFd file to write the screenshot, if necessary. This should be opened - * in write-only, append mode. + * @param bugreportFd file to write the bugreport. This should be opened in write-only, append + * mode. + * @param screenshotFd file to write the screenshot, if necessary. This should be opened in + * write-only, append mode. * @param params options that specify what kind of a bugreport should be taken * @param callback callback for progress and status updates + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.DUMP) - public void startBugreport(@NonNull ParcelFileDescriptor bugreportFd, + public void startBugreport( + @NonNull ParcelFileDescriptor bugreportFd, @Nullable ParcelFileDescriptor screenshotFd, @NonNull BugreportParams params, @NonNull @CallbackExecutor Executor executor, @@ -165,17 +164,21 @@ public final class BugreportManager { boolean isScreenshotRequested = screenshotFd != null; if (screenshotFd == null) { // Binder needs a valid File Descriptor to be passed - screenshotFd = ParcelFileDescriptor.open(new File("/dev/null"), - ParcelFileDescriptor.MODE_READ_ONLY); + screenshotFd = + ParcelFileDescriptor.open( + new File("/dev/null"), ParcelFileDescriptor.MODE_READ_ONLY); } - DumpstateListener dsListener = new DumpstateListener(executor, callback, - isScreenshotRequested); + DumpstateListener dsListener = + new DumpstateListener(executor, callback, isScreenshotRequested); // Note: mBinder can get callingUid from the binder transaction. - mBinder.startBugreport(-1 /* callingUid */, + mBinder.startBugreport( + -1 /* callingUid */, mContext.getOpPackageName(), bugreportFd.getFileDescriptor(), screenshotFd.getFileDescriptor(), - params.getMode(), dsListener, isScreenshotRequested); + params.getMode(), + dsListener, + isScreenshotRequested); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (FileNotFoundException e) { @@ -189,13 +192,64 @@ public final class BugreportManager { } } - /* - * Cancels a currently running bugreport. + /** + * Starts a connectivity bugreport. + * + * <p>The connectivity bugreport is a specialized version of bugreport that only includes + * information specifically for debugging connectivity-related issues (e.g. telephony, wi-fi, + * and IP networking issues). It is intended primarily for use by OEMs and network providers + * such as mobile network operators. In addition to generally excluding information that isn't + * targeted to connectivity debugging, this type of bugreport excludes PII and sensitive + * information that isn't strictly necessary for connectivity debugging. + * + * <p>The calling app MUST have a context-specific reason for requesting a connectivity + * bugreport, such as detecting a connectivity-related issue. This API SHALL NOT be used to + * perform random sampling from a fleet of public end-user devices. + * + * <p>Calling this API will cause the system to ask the user for consent every single time. The + * bugreport artifacts will be copied over to the given file descriptors only if the user + * consents to sharing with the calling app. + * + * <p>This starts a bugreport in the background. However the call itself can take several + * seconds to return in the worst case. {@code callback} will receive progress and status + * updates. + * + * <p>Requires that the calling app has carrier privileges (see {@link + * android.telephony.TelephonyManager#hasCarrierPrivileges}) on any active subscription. + * + * @param bugreportFd file to write the bugreport. This should be opened in write-only, append + * mode. + * @param callback callback for progress and status updates. */ - @RequiresPermission(android.Manifest.permission.DUMP) + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + public void startConnectivityBugreport( + @NonNull ParcelFileDescriptor bugreportFd, + @NonNull @CallbackExecutor Executor executor, + @NonNull BugreportCallback callback) { + startBugreport( + bugreportFd, + null /* screenshotFd */, + new BugreportParams(BugreportParams.BUGREPORT_MODE_TELEPHONY), + executor, + callback); + } + + /** + * Cancels the currently running bugreport. + * + * <p>Apps are only able to cancel their own bugreports. App A cannot cancel a bugreport started + * by app B. + * + * <p>Requires permission: {@link android.Manifest.permission#DUMP} or that the calling app has + * carrier privileges (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on + * any active subscription. + * + * @throws SecurityException if trying to cancel another app's bugreport in progress + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges public void cancelBugreport() { try { - mBinder.cancelBugreport(); + mBinder.cancelBugreport(-1 /* callingUid */, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -205,23 +259,26 @@ public final class BugreportManager { * Requests a bugreport. * * <p>This requests the platform/system to take a bugreport and makes the final bugreport - * available to the user. The user may choose to share it with another app, but the bugreport - * is never given back directly to the app that requested it. + * available to the user. The user may choose to share it with another app, but the bugreport is + * never given back directly to the app that requested it. * - * @param params {@link BugreportParams} that specify what kind of a bugreport should - * be taken, please note that not all kinds of bugreport allow for a - * progress notification - * @param shareTitle title on the final share notification + * @param params {@link BugreportParams} that specify what kind of a bugreport should be taken, + * please note that not all kinds of bugreport allow for a progress notification + * @param shareTitle title on the final share notification * @param shareDescription description on the final share notification + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.DUMP) - public void requestBugreport(@NonNull BugreportParams params, @Nullable CharSequence shareTitle, + public void requestBugreport( + @NonNull BugreportParams params, + @Nullable CharSequence shareTitle, @Nullable CharSequence shareDescription) { try { String title = shareTitle == null ? null : shareTitle.toString(); String description = shareDescription == null ? null : shareDescription.toString(); - ActivityManager.getService().requestBugReportWithDescription(title, description, - params.getMode()); + ActivityManager.getService() + .requestBugReportWithDescription(title, description, params.getMode()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -232,8 +289,8 @@ public final class BugreportManager { private final BugreportCallback mCallback; private final boolean mIsScreenshotRequested; - DumpstateListener(Executor executor, BugreportCallback callback, - boolean isScreenshotRequested) { + DumpstateListener( + Executor executor, BugreportCallback callback, boolean isScreenshotRequested) { mExecutor = executor; mCallback = callback; mIsScreenshotRequested = isScreenshotRequested; @@ -243,9 +300,7 @@ public final class BugreportManager { public void onProgress(int progress) throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mCallback.onProgress(progress); - }); + mExecutor.execute(() -> mCallback.onProgress(progress)); } finally { Binder.restoreCallingIdentity(identity); } @@ -255,9 +310,7 @@ public final class BugreportManager { public void onError(int errorCode) throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mCallback.onError(errorCode); - }); + mExecutor.execute(() -> mCallback.onError(errorCode)); } finally { Binder.restoreCallingIdentity(identity); } @@ -267,9 +320,7 @@ public final class BugreportManager { public void onFinished() throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mCallback.onFinished(); - }); + mExecutor.execute(() -> mCallback.onFinished()); } finally { Binder.restoreCallingIdentity(identity); } @@ -284,20 +335,19 @@ public final class BugreportManager { Handler mainThreadHandler = new Handler(Looper.getMainLooper()); mainThreadHandler.post( () -> { - int message = success ? R.string.bugreport_screenshot_success_toast - : R.string.bugreport_screenshot_failure_toast; + int message = + success + ? R.string.bugreport_screenshot_success_toast + : R.string.bugreport_screenshot_failure_toast; Toast.makeText(mContext, message, Toast.LENGTH_LONG).show(); }); } @Override - public void onUiIntensiveBugreportDumpsFinished() - throws RemoteException { + public void onUiIntensiveBugreportDumpsFinished() throws RemoteException { final long identity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> { - mCallback.onEarlyReportFinished(); - }); + mExecutor.execute(() -> mCallback.onEarlyReportFinished()); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 0d8769e7635c..5ae53b502330 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -26,6 +26,7 @@ import android.app.ActivityThread; import android.app.Application; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.sysprop.SocProperties; import android.sysprop.TelephonyProperties; import android.text.TextUtils; import android.util.Slog; @@ -87,6 +88,14 @@ public class Build { /** The end-user-visible name for the end product. */ public static final String MODEL = getString("ro.product.model"); + /** The manufacturer of the device's primary system-on-chip. */ + @NonNull + public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN); + + /** The model name of the device's primary system-on-chip. */ + @NonNull + public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN); + /** The system bootloader version number. */ public static final String BOOTLOADER = getString("ro.bootloader"); @@ -317,6 +326,7 @@ public class Build { * @see #SDK_INT * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @TestApi public static final int FIRST_SDK_INT = SystemProperties .getInt("ro.product.first_api_level", 0); diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 8c77a2099975..6c49b365c4f3 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -22,6 +22,10 @@ per-file BatteryStats* = file:/BATTERY_STATS_OWNERS per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS per-file PowerComponents.java = file:/BATTERY_STATS_OWNERS +# Multiuser +per-file IUser* = file:/MULTIUSER_OWNERS +per-file User* = file:/MULTIUSER_OWNERS + # Binder per-file BadParcelableException.java = file:platform/frameworks/native:/libs/binder/OWNERS per-file Binder.java = file:platform/frameworks/native:/libs/binder/OWNERS diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java index 71344f90de75..f853e67f87d0 100644 --- a/core/java/android/os/ServiceManager.java +++ b/core/java/android/os/ServiceManager.java @@ -288,6 +288,20 @@ public final class ServiceManager { } /** + * Get service debug info. + * @return an array of information for each service (like listServices, but with PIDs) + * @hide + */ + public static ServiceDebugInfo[] getServiceDebugInfo() { + try { + return getIServiceManager().getServiceDebugInfo(); + } catch (RemoteException e) { + Log.e(TAG, "error in getServiceDebugInfo", e); + return null; + } + } + + /** * This is only intended to be called when the process is first being brought * up and bound by the activity manager. There is only one thread in the process * at that time, so no locking is done. diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java index b70b6b5d209e..60acc57d0cfe 100644 --- a/core/java/android/os/ServiceManagerNative.java +++ b/core/java/android/os/ServiceManagerNative.java @@ -103,6 +103,10 @@ class ServiceManagerProxy implements IServiceManager { throw new RemoteException(); } + public ServiceDebugInfo[] getServiceDebugInfo() throws RemoteException { + return mServiceManager.getServiceDebugInfo(); + } + /** * Same as mServiceManager but used by apps. * diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 67d5f5f205cc..59302afd5fb2 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1992,13 +1992,16 @@ public class UserManager { } /** - * Checks if specified user can have restricted profile. + * Checks if the calling context user can have a restricted profile. + * @return whether the context user can have a restricted profile. * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) - public boolean canHaveRestrictedProfile(@UserIdInt int userId) { + @UserHandleAware + public boolean canHaveRestrictedProfile() { try { - return mService.canHaveRestrictedProfile(userId); + return mService.canHaveRestrictedProfile(mUserId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2020,6 +2023,25 @@ public class UserManager { } /** + * Get the parent of a restricted profile. + * + * @return the parent of the user or {@code null} if the user is not restricted profile + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, + Manifest.permission.CREATE_USERS}) + @UserHandleAware + public @Nullable UserHandle getRestrictedProfileParent() { + final UserInfo info = getUserInfo(mUserId); + if (info == null) return null; + if (!info.isRestricted()) return null; + final int parent = info.restrictedProfileParentId; + if (parent == UserHandle.USER_NULL) return null; + return UserHandle.of(parent); + } + + /** * Checks if a user is a guest user. * @return whether user is a guest user. * @hide diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 0fe889410685..5aa4e27fc2f3 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -35,8 +35,6 @@ import android.os.Message; import android.os.Messenger; import android.os.ParcelableException; import android.os.RemoteException; -import android.os.SystemProperties; -import android.util.FeatureFlagUtils; import android.util.Slog; import java.lang.annotation.Retention; @@ -251,13 +249,7 @@ public class DynamicSystemClient { mService.send(msg); } catch (RemoteException e) { Slog.e(TAG, "Unable to get status from installation service"); - if (mExecutor != null) { - mExecutor.execute(() -> { - mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); - }); - } else { - mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); - } + notifyOnStatusChangedListener(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); } } @@ -311,6 +303,20 @@ public class DynamicSystemClient { mExecutor = null; } + private void notifyOnStatusChangedListener( + int status, int cause, long progress, Throwable detail) { + if (mListener != null) { + if (mExecutor != null) { + mExecutor.execute( + () -> { + mListener.onStatusChanged(status, cause, progress, detail); + }); + } else { + mListener.onStatusChanged(status, cause, progress, detail); + } + } + } + /** * Bind to {@code DynamicSystem} installation service. Binding to the installation service * allows it to send status updates to {@link #OnStatusChangedListener}. It is recommanded @@ -320,11 +326,6 @@ public class DynamicSystemClient { @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) @SystemApi public void bind() { - if (!featureFlagEnabled()) { - Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; bind() aborted."); - return; - } - Intent intent = new Intent(); intent.setClassName("com.android.dynsystem", "com.android.dynsystem.DynamicSystemInstallationService"); @@ -395,11 +396,6 @@ public class DynamicSystemClient { @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull Uri systemUrl, @BytesLong long systemSize, @BytesLong long userdataSize) { - if (!featureFlagEnabled()) { - Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; start() aborted."); - return; - } - Intent intent = new Intent(); intent.setClassName("com.android.dynsystem", @@ -407,6 +403,7 @@ public class DynamicSystemClient { intent.setData(systemUrl); intent.setAction(ACTION_START_INSTALL); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(KEY_SYSTEM_SIZE, systemSize); intent.putExtra(KEY_USERDATA_SIZE, userdataSize); @@ -414,11 +411,6 @@ public class DynamicSystemClient { mContext.startActivity(intent); } - private boolean featureFlagEnabled() { - return SystemProperties.getBoolean( - FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false); - } - private void handleMessage(Message msg) { switch (msg.what) { case MSG_POST_STATUS: @@ -432,13 +424,7 @@ public class DynamicSystemClient { Throwable detail = t == null ? null : t.getCause(); - if (mExecutor != null) { - mExecutor.execute(() -> { - mListener.onStatusChanged(status, cause, progress, detail); - }); - } else { - mListener.onStatusChanged(status, cause, progress, detail); - } + notifyOnStatusChangedListener(status, cause, progress, detail); break; default: // do nothing diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index df3c4d55d979..51856d8bb723 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -50,6 +50,8 @@ public class DiskInfo implements Parcelable { public static final int FLAG_DEFAULT_PRIMARY = 1 << 1; public static final int FLAG_SD = 1 << 2; public static final int FLAG_USB = 1 << 3; + /** The FLAG_STUB_VISIBLE is set from vold, which gets the flag from outside (e.g., ChromeOS) */ + public static final int FLAG_STUB_VISIBLE = 1 << 6; public final String id; @UnsupportedAppUsage @@ -152,6 +154,10 @@ public class DiskInfo implements Parcelable { return (flags & FLAG_USB) != 0; } + public boolean isStubVisible() { + return (flags & FLAG_STUB_VISIBLE) != 0; + } + @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 99bdfd1fc103..4669b208b163 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -195,4 +195,5 @@ interface IStorageManager { void abortChanges(in String message, boolean retry) = 87; void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88; void fixupAppDir(in String path) = 89; + void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90; } diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS index 8af7de597294..ff126e12cf61 100644 --- a/core/java/android/os/storage/OWNERS +++ b/core/java/android/os/storage/OWNERS @@ -1,7 +1,10 @@ # Bug component: 95221 -narayan@google.com -nandana@google.com corinac@google.com +nandana@google.com zezeozue@google.com maco@google.com +sahanas@google.com +abkaur@google.com +chiangi@google.com +narayan@google.com diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index e7e2c619ee6a..bd84c84f0430 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -92,6 +92,13 @@ public final class DeviceConfig { public static final String NAMESPACE_APP_COMPAT = "app_compat"; /** + * Namespace for all app hibernation related features. + * @hide + */ + @SystemApi + public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation"; + + /** * Namespace for AttentionManagerService related features. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1e14e3a4a701..0f7365dcfd90 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -999,6 +999,20 @@ public final class Settings { "android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS"; /** + * Activity Action: Show settings to manage all SIM profiles. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_ALL_SUBSCRIPTIONS_SETTINGS = + "android.settings.MANAGE_ALL_SUBSCRIPTIONS_SETTINGS"; + + /** * Activity Action: Show screen for controlling which apps can draw on top of other apps. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard against this. @@ -14406,6 +14420,17 @@ public final class Settings { */ public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE = "nr_nsa_tracking_screen_off_mode"; + + /** + * Used to enable / disable the Restricted Networking Mode in which network access is + * restricted to apps holding the CONNECTIVITY_USE_RESTRICTED_NETWORKS permission. + * + * Values are: + * 0: disabled + * 1: enabled + * @hide + */ + public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode"; } /** diff --git a/core/java/android/se/OWNERS b/core/java/android/se/OWNERS index f1539dc55d59..5682fd3281f4 100644 --- a/core/java/android/se/OWNERS +++ b/core/java/android/se/OWNERS @@ -1,4 +1,5 @@ # Bug component: 456592 -cbrubaker@google.com -vishwath@google.com +zachoverflow@google.com +alisher@google.com +jackcwyu@google.com diff --git a/core/java/android/se/omapi/OWNERS b/core/java/android/se/omapi/OWNERS index f1539dc55d59..5682fd3281f4 100644 --- a/core/java/android/se/omapi/OWNERS +++ b/core/java/android/se/omapi/OWNERS @@ -1,4 +1,5 @@ # Bug component: 456592 -cbrubaker@google.com -vishwath@google.com +zachoverflow@google.com +alisher@google.com +jackcwyu@google.com diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java index a5c5c613e1f2..333af91ac872 100644 --- a/core/java/android/se/omapi/SEService.java +++ b/core/java/android/se/omapi/SEService.java @@ -22,7 +22,10 @@ package android.se.omapi; +import android.annotation.BroadcastBehavior; import android.annotation.NonNull; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -71,6 +74,28 @@ public final class SEService { } /** + * Broadcast Action: Intent to notify if the secure element state is changed. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @BroadcastBehavior(registeredOnly = true, protectedBroadcast = true) + public static final String ACTION_SECURE_ELEMENT_STATE_CHANGED = + "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED"; + + /** + * Mandatory extra containing the reader name of the state changed secure element. + * + * @see Reader#getName() + */ + public static final String EXTRA_READER_NAME = "android.se.omapi.extra.READER_NAME"; + + /** + * Mandatory extra containing the connected state of the state changed secure element. + * + * True if the secure element is connected correctly, false otherwise. + */ + public static final String EXTRA_READER_STATE = "android.se.omapi.extra.READER_STATE"; + + /** * Listener object that allows the notification of the caller if this * SEService could be bound to the backend. */ diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 017f40521a81..f994d2930cd9 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -177,6 +177,7 @@ public final class KeymasterDefs { public static final int KM_PURPOSE_SIGN = KeyPurpose.SIGN; public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY; public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY; + public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY; // Key formats. public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509; diff --git a/core/java/android/service/dataloader/OWNERS b/core/java/android/service/dataloader/OWNERS new file mode 100644 index 000000000000..7f3906baed2e --- /dev/null +++ b/core/java/android/service/dataloader/OWNERS @@ -0,0 +1 @@ +include /core/java/android/os/incremental/OWNERS
\ No newline at end of file diff --git a/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl b/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl new file mode 100644 index 000000000000..d9b403ca0609 --- /dev/null +++ b/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl @@ -0,0 +1,25 @@ +/* + * 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 android.service.resumeonreboot; + +import android.os.RemoteCallback; + +/** @hide */ +interface IResumeOnRebootService { + oneway void wrapSecret(in byte[] unwrappedBlob, in long lifeTimeInMillis, in RemoteCallback resultCallback); + oneway void unwrap(in byte[] wrappedBlob, in RemoteCallback resultCallback); +}
\ No newline at end of file diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java new file mode 100644 index 000000000000..4ebaa96f4be2 --- /dev/null +++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java @@ -0,0 +1,164 @@ +/* + * 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 android.service.resumeonreboot; + +import android.annotation.DurationMillisLong; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SdkConstant; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.ParcelableException; +import android.os.RemoteCallback; +import android.os.RemoteException; + +import com.android.internal.os.BackgroundThread; + +import java.io.IOException; + +/** + * Base class for service that provides wrapping/unwrapping of the opaque blob needed for + * ResumeOnReboot operation. The package needs to provide a wrap/unwrap implementation for handling + * the opaque blob, that's secure even when on device keystore and clock is compromised. This can + * be achieved by using tamper-resistant hardware such as a secure element with a secure clock, or + * using a remote server to store and retrieve data and manage timing. + * + * <p>To extend this class, you must declare the service in your manifest file with the + * {@link android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE} permission, + * include an intent filter with the {@link #SERVICE_INTERFACE} action and mark the service as + * direct-boot aware. In addition, the package that contains the service must be granted + * {@link android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE}. + * For example:</p> + * <pre> + * <service android:name=".FooResumeOnRebootService" + * android:exported="true" + * android:priority="100" + * android:directBootAware="true" + * android:permission="android.permission.BIND_RESUME_ON_REBOOT_SERVICE"> + * <intent-filter> + * <action android:name="android.service.resumeonreboot.ResumeOnRebootService" /> + * </intent-filter> + * </service> + * </pre> + * + * //TODO: Replace this with public link when available. + * + * @hide + * @see + * <a href="https://goto.google.com/server-based-ror">https://goto.google.com/server-based-ror</a> + */ +@SystemApi +public abstract class ResumeOnRebootService extends Service { + + /** + * The intent that the service must respond to. Add it to the intent filter of the service. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = + "android.service.resumeonreboot.ResumeOnRebootService"; + /** @hide */ + public static final String UNWRAPPED_BLOB_KEY = "unrwapped_blob_key"; + /** @hide */ + public static final String WRAPPED_BLOB_KEY = "wrapped_blob_key"; + /** @hide */ + public static final String EXCEPTION_KEY = "exception_key"; + + private final Handler mHandler = BackgroundThread.getHandler(); + + /** + * Implementation for wrapping the opaque blob used for resume-on-reboot prior to + * reboot. The service should not assume any structure of the blob to be wrapped. The + * implementation should wrap the opaque blob in a reasonable time or throw {@link IOException} + * if it's unable to complete the action. + * + * @param blob The opaque blob with size on the order of 100 bytes. + * @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the + * implementation and any attempt to unWrap the wrapped blob returned by + * this function after expiration should + * fail. + * @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes. + * @throws IOException if the implementation is unable to wrap the blob successfully. + */ + @NonNull + public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis) + throws IOException; + + /** + * Implementation for unwrapping the wrapped blob used for resume-on-reboot after reboot. This + * operation would happen after reboot during direct boot mode (i.e before device is unlocked + * for the first time). The implementation should unwrap the wrapped blob in a reasonable time + * and returns the result or throw {@link IOException} if it's unable to complete the action + * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is + * stale. + * + * @param wrappedBlob The wrapped blob with size on the order of 100 bytes. + * @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes. + * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully. + */ + @NonNull + public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException; + + private final android.service.resumeonreboot.IResumeOnRebootService mInterface = + new android.service.resumeonreboot.IResumeOnRebootService.Stub() { + + @Override + public void wrapSecret(byte[] unwrappedBlob, + @DurationMillisLong long lifeTimeInMillis, + RemoteCallback resultCallback) throws RemoteException { + mHandler.post(() -> { + try { + byte[] wrappedBlob = onWrap(unwrappedBlob, + lifeTimeInMillis); + Bundle bundle = new Bundle(); + bundle.putByteArray(WRAPPED_BLOB_KEY, wrappedBlob); + resultCallback.sendResult(bundle); + } catch (Throwable e) { + Bundle bundle = new Bundle(); + bundle.putParcelable(EXCEPTION_KEY, new ParcelableException(e)); + resultCallback.sendResult(bundle); + } + }); + } + + @Override + public void unwrap(byte[] wrappedBlob, RemoteCallback resultCallback) + throws RemoteException { + mHandler.post(() -> { + try { + byte[] unwrappedBlob = onUnwrap(wrappedBlob); + Bundle bundle = new Bundle(); + bundle.putByteArray(UNWRAPPED_BLOB_KEY, unwrappedBlob); + resultCallback.sendResult(bundle); + } catch (Throwable e) { + Bundle bundle = new Bundle(); + bundle.putParcelable(EXCEPTION_KEY, new ParcelableException(e)); + resultCallback.sendResult(bundle); + } + }); + } + }; + + @Nullable + @Override + public IBinder onBind(@Nullable Intent intent) { + return mInterface.asBinder(); + } +} diff --git a/core/java/android/service/search/OWNERS b/core/java/android/service/search/OWNERS new file mode 100644 index 000000000000..92835c2b0626 --- /dev/null +++ b/core/java/android/service/search/OWNERS @@ -0,0 +1,2 @@ +hyunyoungs@google.com +sfufa@google.com diff --git a/core/java/android/service/textservice/OWNERS b/core/java/android/service/textservice/OWNERS index 10b8b7637431..0471e29a25cd 100644 --- a/core/java/android/service/textservice/OWNERS +++ b/core/java/android/service/textservice/OWNERS @@ -1,3 +1,3 @@ -# Bug component: 34867 +# Bug component: 816455 -include ../../inputmethodservice/OWNERS
\ No newline at end of file +include /services/core/java/com/android/server/textservices/OWNERS diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index d6ae434af9d5..03d3755111aa 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -17,7 +17,10 @@ package android.telephony; import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -29,9 +32,16 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; import android.telephony.Annotation.CallState; +import android.telephony.Annotation.DataActivityType; +import android.telephony.Annotation.DisconnectCauses; +import android.telephony.Annotation.NetworkType; +import android.telephony.Annotation.PreciseDisconnectCauses; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; +import android.telephony.NetworkRegistrationInfo.Domain; +import android.telephony.TelephonyManager.DataEnabledReason; +import android.telephony.TelephonyManager.DataState; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; @@ -40,6 +50,8 @@ import com.android.internal.telephony.IPhoneStateListener; import dalvik.system.VMRuntime; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; @@ -114,7 +126,9 @@ public class PhoneStateListener { * * @see #onServiceStateChanged * @see ServiceState + * @deprecated Use {@link ServiceStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_SERVICE_STATE = 0x00000001; /** @@ -122,8 +136,7 @@ public class PhoneStateListener { * {@more} * * @see #onSignalStrengthChanged - * - * @deprecated by {@link #LISTEN_SIGNAL_STRENGTHS} + * @deprecated Use {@link SignalStrengthsChangedListener} instead. */ @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 0x00000002; @@ -139,7 +152,9 @@ public class PhoneStateListener { * voicemail icon. * * @see #onMessageWaitingIndicatorChanged + * @deprecated Use {@link MessageWaitingIndicatorChangedListener} instead. */ + @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004; /** @@ -150,7 +165,9 @@ public class PhoneStateListener { * {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onCallForwardingIndicatorChanged + * @deprecated Use {@link CallForwardingIndicatorChangedListener} instead. */ + @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008; /** @@ -166,7 +183,9 @@ public class PhoneStateListener { * instead. * * @see #onCellLocationChanged + * @deprecated Use {@link CellLocationChangedListener} instead. */ + @Deprecated public static final int LISTEN_CELL_LOCATION = 0x00000010; /** @@ -174,14 +193,18 @@ public class PhoneStateListener { * {@more} * * @see #onCallStateChanged + * @deprecated Use {@link CallStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_CALL_STATE = 0x00000020; /** * Listen for changes to the data connection state (cellular). * * @see #onDataConnectionStateChanged + * @deprecated Use {@link DataConnectionStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040; /** @@ -192,7 +215,9 @@ public class PhoneStateListener { * data-traffic icon. * * @see #onDataActivity + * @deprecated Use {@link DataActivityListener} instead. */ + @Deprecated public static final int LISTEN_DATA_ACTIVITY = 0x00000080; /** @@ -202,7 +227,9 @@ public class PhoneStateListener { * icon. * * @see #onSignalStrengthsChanged + * @deprecated Use {@link SignalStrengthsChangedListener} instead. */ + @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100; /** @@ -212,7 +239,9 @@ public class PhoneStateListener { * @see #onSignalStrengthsChanged * * @hide + * @deprecated Use {@link AlwaysReportedSignalStrengthChangedListener} instead. */ + @Deprecated @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200; @@ -223,7 +252,9 @@ public class PhoneStateListener { * permission. * * @see #onCellInfoChanged + * @deprecated Use {@link CellInfoChangedListener} instead. */ + @Deprecated public static final int LISTEN_CELL_INFO = 0x00000400; /** @@ -235,8 +266,10 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @hide + * @deprecated Use {@link PreciseCallStateChangedListener} instead. */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @SystemApi public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800; @@ -248,8 +281,10 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onPreciseDataConnectionStateChanged + * @deprecated Use {@link PreciseDataConnectionStateChangedListener} instead. */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000; /** @@ -259,7 +294,7 @@ public class PhoneStateListener { * READ_PRECISE_PHONE_STATE} * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) * - * @deprecated Use {@link TelephonyManager#getModemActivityInfo()} + * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} instead. * @hide */ @Deprecated @@ -272,7 +307,9 @@ public class PhoneStateListener { * * @see #onServiceStateChanged(ServiceState) * @hide + * @deprecated Use {@link SrvccStateChangedListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 0x00004000; @@ -290,10 +327,11 @@ public class PhoneStateListener { /** * Listen for carrier network changes indicated by a carrier app. * - * @see #onCarrierNetworkRequest - * @see TelephonyManager#notifyCarrierNetworkChange(boolean) + * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) * @hide + * @deprecated Use {@link CarrierNetworkChangeListener} instead. */ + @Deprecated public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000; /** @@ -312,7 +350,9 @@ public class PhoneStateListener { * * @see #onVoiceActivationStateChanged * @hide + * @deprecated Use {@link VoiceActivationStateChangedListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000; @@ -324,20 +364,24 @@ public class PhoneStateListener { * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN - * {@more} + * * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been * fully activated * * @see #onDataActivationStateChanged * @hide + * @deprecated Use {@link DataActivationStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000; /** * Listen for changes to the user mobile data state * * @see #onUserMobileDataStateChanged + * @deprecated Use {@link UserMobileDataStateChangedListener} instead. */ + @Deprecated public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000; /** @@ -348,7 +392,9 @@ public class PhoneStateListener { * {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onDisplayInfoChanged + * @deprecated Use {@link DisplayInfoChangedListener} instead. */ + @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000; /** @@ -356,7 +402,9 @@ public class PhoneStateListener { * * @see #onPhoneCapabilityChanged * @hide + * @deprecated Use {@link PhoneCapabilityChangedListener} instead. */ + @Deprecated public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000; /** @@ -366,17 +414,19 @@ public class PhoneStateListener { * subscription user selected as default data subscription in DSDS mode. * * @see #onActiveDataSubscriptionIdChanged + * @deprecated Use {@link ActiveDataSubscriptionIdChangedListener} instead. */ + @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000; /** * Listen for changes to the radio power state. * - * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} - * * @see #onRadioPowerStateChanged * @hide + * @deprecated Use {@link RadioPowerStateChangedListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 0x00800000; @@ -386,7 +436,10 @@ public class PhoneStateListener { * * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @deprecated Use {@link EmergencyNumberListChangedListener} instead. */ + @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000; /** @@ -397,8 +450,10 @@ public class PhoneStateListener { * or the calling app has carrier privileges * (see {@link TelephonyManager#hasCarrierPrivileges}). * + * @deprecated Use {@link CallDisconnectCauseChangedListener} instead. */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000; /** @@ -410,9 +465,11 @@ public class PhoneStateListener { * * @see #onCallAttributesChanged * @hide + * @deprecated Use {@link CallAttributesChangedListener} instead. */ + @Deprecated @SystemApi - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000; /** @@ -424,18 +481,20 @@ public class PhoneStateListener { * (see {@link TelephonyManager#hasCarrierPrivileges}). * * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo) + * @deprecated Use {@link ImsCallDisconnectCauseChangedListener} instead. */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000; /** * Listen for the emergency number placed from an outgoing call. * - * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} - * * @see #onOutgoingEmergencyCall * @hide + * @deprecated Use {@link OutgoingEmergencyCallListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 0x10000000; @@ -443,11 +502,11 @@ public class PhoneStateListener { /** * Listen for the emergency number placed from an outgoing SMS. * - * <p>Requires permission {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION} - * * @see #onOutgoingEmergencySms * @hide + * @deprecated Use {@link OutgoingEmergencySmsListener} instead. */ + @Deprecated @SystemApi @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 0x20000000; @@ -466,7 +525,9 @@ public class PhoneStateListener { * of whether the calling app has carrier privileges. * * @see #onRegistrationFailed + * @deprecated Use {@link RegistrationFailedListener} instead. */ + @Deprecated @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000; @@ -480,10 +541,525 @@ public class PhoneStateListener { * of whether the calling app has carrier privileges. * * @see #onBarringInfoChanged + * @deprecated Use {@link BarringInfoChangedListener} instead. */ + @Deprecated @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = 0x80000000; + /** + * Event for changes to the network service state (cellular). + * + * @see ServiceStateChangedListener#onServiceStateChanged + * @see ServiceState + * + * @hide + */ + @SystemApi + public static final int EVENT_SERVICE_STATE_CHANGED = 1; + + /** + * Event for changes to the network signal strength (cellular). + * + * @see SignalStrengthsChangedListener#onSignalStrengthsChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; + + /** + * Event for changes to the message-waiting indicator. + * + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that + * the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * <p> + * Example: The status bar uses this to determine when to display the + * voicemail icon. + * + * @see MessageWaitingIndicatorChangedListener#onMessageWaitingIndicatorChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_MESSAGE_WAITING_INDICATOR_CHANGED = 3; + + /** + * Event for changes to the call-forwarding indicator. + * + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that + * the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see CallForwardingIndicatorChangedListener#onCallForwardingIndicatorChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; + + /** + * Event for changes to the device's cell location. Note that + * this will result in frequent callbacks to the listener. + * + * If you need regular location updates but want more control over + * the update interval or location precision, you can set up a listener + * through the {@link android.location.LocationManager location manager} + * instead. + * + * @see CellLocationChangedListener#onCellLocationChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public static final int EVENT_CELL_LOCATION_CHANGED = 5; + + /** + * Event for changes to the device call state. + * + * @see CallStateChangedListener#onCallStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) + public static final int EVENT_CALL_STATE_CHANGED = 6; + + /** + * Event for changes to the data connection state (cellular). + * + * @see DataConnectionStateChangedListener#onDataConnectionStateChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; + + /** + * Event for changes to the direction of data traffic on the data + * connection (cellular). + * + * Example: The status bar uses this to display the appropriate + * data-traffic icon. + * + * @see DataActivityListener#onDataActivity + * + * @hide + */ + @SystemApi + public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; + + /** + * Event for changes to the network signal strengths (cellular). + * <p> + * Example: The status bar uses this to control the signal-strength + * icon. + * + * @see SignalStrengthsChangedListener#onSignalStrengthsChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; + + /** + * Event for changes of the network signal strengths (cellular) always reported from modem, + * even in some situations such as the screen of the device is off. + * + * @see AlwaysReportedSignalStrengthChangedListener#onSignalStrengthsChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; + + /** + * Event for changes to observed cell info. + * + * @see CellInfoChangedListener#onCellInfoChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public static final int EVENT_CELL_INFO_CHANGED = 11; + + /** + * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing, + * background and foreground calls. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see PreciseCallStateChangedListener#onPreciseCallStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_PRECISE_CALL_STATE_CHANGED = 12; + + /** + * Event for {@link PreciseDataConnectionState} on the data connection (cellular). + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see PreciseDataConnectionStateChangedListener#onPreciseDataConnectionStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED = 13; + + /** + * Event for real time info for all data connections (cellular)). + * + * @see #onDataConnectionRealTimeInfoChanged(DataConnectionRealTimeInfo) + * + * @deprecated Use {@link TelephonyManager#requestModemActivityInfo} + * @hide + */ + @Deprecated + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED = 14; + + /** + * Event for OEM hook raw event + * + * @see #onOemHookRawEvent + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_OEM_HOOK_RAW = 15; + + /** + * Event for changes to the SRVCC state of the active call. + * + * @see SrvccStateChangedListener#onSrvccStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_SRVCC_STATE_CHANGED = 16; + + /** + * Event for carrier network changes indicated by a carrier app. + * + * @see android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean) + * @see CarrierNetworkChangeListener#onCarrierNetworkChange + * + * @hide + */ + @SystemApi + public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; + + /** + * Event for changes to the sim voice activation state + * + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED + * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN + * + * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been + * fully activated + * + * @see VoiceActivationStateChangedListener#onVoiceActivationStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; + + /** + * Event for changes to the sim data activation state + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED + * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN + * + * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been + * fully activated + * + * @see DataActivationStateChangedListener#onDataActivationStateChanged + * @hide + */ + @SystemApi + public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; + + /** + * Event for changes to the user mobile data state + * + * @see UserMobileDataStateChangedListener#onUserMobileDataStateChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; + + /** + * Event for display info changed event. + * + * @see DisplayInfoChangedListener#onDisplayInfoChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_DISPLAY_INFO_CHANGED = 21; + + /** + * Event for changes to the phone capability. + * + * @see PhoneCapabilityChangedListener#onPhoneCapabilityChanged + * + * @hide + */ + @SystemApi + public static final int EVENT_PHONE_CAPABILITY_CHANGED = 22; + + /** + * Event for changes to active data subscription ID. Active data subscription is + * the current subscription used to setup Cellular Internet data. For example, + * it could be the current active opportunistic subscription in use, or the + * subscription user selected as default data subscription in DSDS mode. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see ActiveDataSubscriptionIdChangedListener#onActiveDataSubscriptionIdChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; + + /** + * Event for changes to the radio power state. + * + * @see RadioPowerStateChangedListener#onRadioPowerStateChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public static final int EVENT_RADIO_POWER_STATE_CHANGED = 24; + + /** + * Event for changes to emergency number list based on all active subscriptions. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling + * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see EmergencyNumberListChangedListener#onEmergencyNumberListChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; + + /** + * Event for call disconnect causes which contains {@link DisconnectCause} and + * {@link PreciseDisconnectCause}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see CallDisconnectCauseChangedListener#onCallDisconnectCauseChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; + + /** + * Event for changes to the call attributes of a currently active call. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see CallAttributesChangedListener#onCallAttributesChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; + + /** + * Event for IMS call disconnect causes which contains + * {@link android.telephony.ims.ImsReasonInfo} + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see ImsCallDisconnectCauseChangedListener#onImsCallDisconnectCauseChanged(ImsReasonInfo) + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; + + /** + * Event for the emergency number placed from an outgoing call. + * + * @see OutgoingEmergencyCallListener#onOutgoingEmergencyCall + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public static final int EVENT_OUTGOING_EMERGENCY_CALL = 29; + + /** + * Event for the emergency number placed from an outgoing SMS. + * + * @see OutgoingEmergencySmsListener#onOutgoingEmergencySms + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public static final int EVENT_OUTGOING_EMERGENCY_SMS = 30; + + /** + * Event for registration failures. + * + * Event for indications that a registration procedure has failed in either the CS or PS + * domain. This indication does not necessarily indicate a change of service state, which should + * be tracked via {@link #EVENT_SERVICE_STATE_CHANGED}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or + * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless + * of whether the calling app has carrier privileges. + * + * @see RegistrationFailedListener#onRegistrationFailed + * + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public static final int EVENT_REGISTRATION_FAILURE = 31; + + /** + * Event for Barring Information for the current registered / camped cell. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or + * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * <p>Also requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission, regardless + * of whether the calling app has carrier privileges. + * + * @see BarringInfoChangedListener#onBarringInfoChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public static final int EVENT_BARRING_INFO_CHANGED = 32; + + /** + * Event for changes to the physical channel configuration. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see PhysicalChannelConfigChangedListener#onPhysicalChannelConfigChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 33; + + /** + * Event for changes to the data enabled. + * + * Event for indications that the enabled status of current data has changed. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see DataEnabledChangedListener#onDataEnabledChanged + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public static final int EVENT_DATA_ENABLED_CHANGED = 34; + + /** @hide */ + @IntDef(prefix = { "EVENT_" }, value = { + EVENT_SERVICE_STATE_CHANGED, + EVENT_SIGNAL_STRENGTH_CHANGED, + EVENT_MESSAGE_WAITING_INDICATOR_CHANGED, + EVENT_CALL_FORWARDING_INDICATOR_CHANGED, + EVENT_CELL_LOCATION_CHANGED, + EVENT_CALL_STATE_CHANGED, + EVENT_DATA_CONNECTION_STATE_CHANGED, + EVENT_DATA_ACTIVITY_CHANGED, + EVENT_SIGNAL_STRENGTHS_CHANGED, + EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED, + EVENT_CELL_INFO_CHANGED, + EVENT_PRECISE_CALL_STATE_CHANGED, + EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED, + EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED, + EVENT_OEM_HOOK_RAW, + EVENT_SRVCC_STATE_CHANGED, + EVENT_CARRIER_NETWORK_CHANGED, + EVENT_VOICE_ACTIVATION_STATE_CHANGED, + EVENT_DATA_ACTIVATION_STATE_CHANGED, + EVENT_USER_MOBILE_DATA_STATE_CHANGED, + EVENT_DISPLAY_INFO_CHANGED, + EVENT_PHONE_CAPABILITY_CHANGED, + EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED, + EVENT_RADIO_POWER_STATE_CHANGED, + EVENT_EMERGENCY_NUMBER_LIST_CHANGED, + EVENT_CALL_DISCONNECT_CAUSE_CHANGED, + EVENT_CALL_ATTRIBUTES_CHANGED, + EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED, + EVENT_OUTGOING_EMERGENCY_CALL, + EVENT_OUTGOING_EMERGENCY_SMS, + EVENT_REGISTRATION_FAILURE, + EVENT_BARRING_INFO_CHANGED, + EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, + EVENT_DATA_ENABLED_CHANGED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TelephonyEvent {} + /* * Subscription used to listen to the phone state changes * @hide @@ -495,13 +1071,19 @@ public class PhoneStateListener { /** * @hide */ + //TODO: The maxTargetSdk should be S if the build time tool updates it. @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - @UnsupportedAppUsage - public final IPhoneStateListener callback; + @UnsupportedAppUsage( + maxTargetSdk = Build.VERSION_CODES.R, + publicAlternatives = "Use {@code TelephonyManager#registerPhoneStateListener(" + + "Executor, PhoneStateListener)} instead") + public IPhoneStateListener callback; /** * Create a PhoneStateListener for the Phone with the default subscription. - * This class requires Looper.myLooper() not return null. + * If this is created for use with deprecated API + * {@link TelephonyManager#listen(PhoneStateListener, int)}, then this class requires + * Looper.myLooper() not return null. */ public PhoneStateListener() { this(null, Looper.myLooper()); @@ -539,7 +1121,10 @@ public class PhoneStateListener { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public PhoneStateListener(Integer subId, Looper looper) { - this(subId, new HandlerExecutor(new Handler(looper))); + if (looper != null) { + setExecutor(new HandlerExecutor(new Handler(looper))); + } + mSubId = subId; if (subId != null && VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) { throw new IllegalArgumentException("PhoneStateListener with subId: " @@ -554,17 +1139,744 @@ public class PhoneStateListener { * The Executor must not be null. * * @param executor a non-null Executor that will execute callbacks for the PhoneStateListener. + * @deprecated Use + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} instead. */ + @Deprecated public PhoneStateListener(@NonNull Executor executor) { - this(null, executor); + setExecutor(executor); + mSubId = null; } - private PhoneStateListener(Integer subId, Executor e) { - if (e == null) { + private @NonNull Executor mExecutor; + + /** + * @hide + */ + public void setExecutor(@NonNull @CallbackExecutor Executor executor) { + if (executor == null) { throw new IllegalArgumentException("PhoneStateListener Executor must be non-null"); } - mSubId = subId; - callback = new IPhoneStateListenerStub(this, e); + mExecutor = executor; + callback = new IPhoneStateListenerStub(this, mExecutor); + } + + /** + * @hide + */ + public boolean isExecutorSet() { + return mExecutor != null; + } + + /** + * Interface for service state listener. + */ + public interface ServiceStateChangedListener { + /** + * Callback invoked when device service state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * The instance of {@link ServiceState} passed as an argument here will have various + * levels of location information stripped from it depending on the location permissions + * that your app holds. + * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will + * receive all the information in {@link ServiceState}. + * + * @see ServiceState#STATE_EMERGENCY_ONLY + * @see ServiceState#STATE_IN_SERVICE + * @see ServiceState#STATE_OUT_OF_SERVICE + * @see ServiceState#STATE_POWER_OFF + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onServiceStateChanged(@NonNull ServiceState serviceState); + } + + /** + * Interface for message waiting indicator listener. + */ + public interface MessageWaitingIndicatorChangedListener { + /** + * Callback invoked when the message-waiting indicator changes on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onMessageWaitingIndicatorChanged(boolean mwi); + } + + /** + * Interface for call-forwarding indicator listener. + */ + public interface CallForwardingIndicatorChangedListener { + /** + * Callback invoked when the call-forwarding indicator changes on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onCallForwardingIndicatorChanged(boolean cfi); + } + + /** + * Interface for device cell location listener. + */ + public interface CellLocationChangedListener { + /** + * Callback invoked when device cell location changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public void onCellLocationChanged(@NonNull CellLocation location); + } + + /** + * Interface for call state listener. + */ + public interface CallStateChangedListener { + /** + * Callback invoked when device call state changes. + * <p> + * Reports the state of Telephony (mobile) calls on the device for the registered s + * ubscription. + * <p> + * Note: the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to all subIds. + * <p> + * Note: The state returned here may differ from that returned by + * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that + * calling {@link TelephonyManager#getCallState()} from within this callback may return a + * different state than the callback reports. + * + * @param state call state + * @param phoneNumber call phone number. If application does not have + * {@link android.Manifest.permission#READ_CALL_LOG} permission or carrier + * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be + * passed as an argument. + */ + @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) + public void onCallStateChanged(@CallState int state, @Nullable String phoneNumber); + } + + /** + * Interface for data connection state listener. + */ + public interface DataConnectionStateChangedListener { + /** + * Callback invoked when connection state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @see TelephonyManager#DATA_DISCONNECTED + * @see TelephonyManager#DATA_CONNECTING + * @see TelephonyManager#DATA_CONNECTED + * @see TelephonyManager#DATA_SUSPENDED + * + * @param state is the current state of data connection. + * @param networkType is the current network type of data connection. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataConnectionStateChanged(@DataState int state, + @NetworkType int networkType); + } + + /** + * Interface for data activity state listener. + */ + public interface DataActivityListener { + /** + * Callback invoked when data activity state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @see TelephonyManager#DATA_ACTIVITY_NONE + * @see TelephonyManager#DATA_ACTIVITY_IN + * @see TelephonyManager#DATA_ACTIVITY_OUT + * @see TelephonyManager#DATA_ACTIVITY_INOUT + * @see TelephonyManager#DATA_ACTIVITY_DORMANT + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataActivity(@DataActivityType int direction); + } + + /** + * Interface for network signal strengths listener. + */ + public interface SignalStrengthsChangedListener { + /** + * Callback invoked when network signal strengths changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); + } + + /** + * Interface for network signal strengths listener which always reported from modem. + */ + public interface AlwaysReportedSignalStrengthChangedListener { + /** + * Callback always invoked from modem when network signal strengths changes on the + * registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength); + } + + /** + * Interface for cell info listener. + */ + public interface CellInfoChangedListener { + /** + * Callback invoked when a observed cell info has changed or new cells have been added + * or removed on the registered subscription. + * Note, the registration subscription ID s from {@link TelephonyManager} object + * which registersPhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param cellInfo is the list of currently visible cells. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) + public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo); + } + + /** + * Interface for precise device call state listener. + * + * @hide + */ + @SystemApi + public interface PreciseCallStateChangedListener { + /** + * Callback invoked when precise device call state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param callState {@link PreciseCallState} + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPreciseCallStateChanged(@NonNull PreciseCallState callState); + } + + /** + * Interface for call disconnect cause listener. + */ + public interface CallDisconnectCauseChangedListener { + /** + * Callback invoked when call disconnect cause changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param disconnectCause {@link DisconnectCause}. + * @param preciseDisconnectCause {@link PreciseDisconnectCause}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause, + @PreciseDisconnectCauses int preciseDisconnectCause); + } + + /** + * Interface for IMS call disconnect cause listener. + */ + public interface ImsCallDisconnectCauseChangedListener { + /** + * Callback invoked when IMS call disconnect cause changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. + * + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo); + } + + /** + * Interface for precise data connection state listener. + */ + public interface PreciseDataConnectionStateChangedListener { + /** + * Callback providing update about the default/internet data connection on the registered + * subscription. + * + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} + * or the calling app has carrier privileges + * (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @param dataConnectionState {@link PreciseDataConnectionState} + */ + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPreciseDataConnectionStateChanged( + @NonNull PreciseDataConnectionState dataConnectionState); + } + + /** + * Interface for Single Radio Voice Call Continuity listener. + * + * @hide + */ + @SystemApi + public interface SrvccStateChangedListener { + /** + * Callback invoked when there has been a change in the Single Radio Voice Call Continuity + * (SRVCC) state for the currently active call on the registered subscription. + * + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onSrvccStateChanged(@SrvccState int srvccState); + } + + /** + * Interface for SIM voice activation state listener. + * + * @hide + */ + @SystemApi + public interface VoiceActivationStateChangedListener { + /** + * Callback invoked when the SIM voice activation state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state is the current SIM voice activation state + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onVoiceActivationStateChanged(@SimActivationState int state); + + } + + /** + * Interface for SIM data activation state listener. + */ + public interface DataActivationStateChangedListener { + /** + * Callback invoked when the SIM data activation state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state is the current SIM data activation state + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onDataActivationStateChanged(@SimActivationState int state); + } + + /** + * Interface for user mobile data state listener. + */ + public interface UserMobileDataStateChangedListener { + /** + * Callback invoked when the user mobile data state has changed on the registered + * subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param enabled indicates whether the current user mobile data state is enabled or + * disabled. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onUserMobileDataStateChanged(boolean enabled); + } + + /** + * Interface for display info listener. + */ + public interface DisplayInfoChangedListener { + /** + * Callback invoked when the display info has changed on the registered subscription. + * <p> The {@link TelephonyDisplayInfo} contains status information shown to the user + * based on carrier policy. + * + * @param telephonyDisplayInfo The display information. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo); + } + + /** + * Interface for the current emergency number list listener. + */ + public interface EmergencyNumberListChangedListener { + /** + * Callback invoked when the current emergency number list has changed on the registered + * subscription. + * + * Note, the registered subscription is associated with {@link TelephonyManager} object + * on which + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)} + * was called. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * given subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param emergencyNumberList Map associating all active subscriptions on the device with + * the list of emergency numbers originating from that + * subscription. + * If there are no active subscriptions, the map will contain a + * single entry with + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} as + * the key and a list of emergency numbers as the value. If no + * emergency number information is available, the value will be + * empty. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onEmergencyNumberListChanged( + @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList); + } + + /** + * Interface for outgoing emergency call listener. + * + * @hide + */ + @SystemApi + public interface OutgoingEmergencyCallListener { + /** + * Callback invoked when an outgoing call is placed to an emergency number. + * + * This method will be called when an emergency call is placed on any subscription + * (including the no-SIM case), regardless of which subscription this listener was + * registered on. + * + * The default implementation of this method calls + * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes. + * Do not call {@code super(...)} from within your implementation unless you want + * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well. + * + * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was + * placed to. + * @param subscriptionId The subscription ID used to place the emergency call. If the + * emergency call was placed without a valid subscription + * (e.g. when there are no SIM cards in the device), this will be + * equal to {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + */ + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, + int subscriptionId); + } + + /** + * Interface for outgoing emergency sms listener. + * + * @hide + */ + @SystemApi + public interface OutgoingEmergencySmsListener { + /** + * Smsback invoked when an outgoing sms is sent to an emergency number. + * + * This method will be called when an emergency sms is sent on any subscription, + * regardless of which subscription this listener was registered on. + * + * The default implementation of this method calls + * {@link #onOutgoingEmergencySms(EmergencyNumber)} for backwards compatibility purposes. Do + * not call {@code super(...)} from within your implementation unless you want + * {@link #onOutgoingEmergencySms(EmergencyNumber)} to be called as well. + * + * @param sentEmergencyNumber The {@link EmergencyNumber} the emergency sms was sent to. + * @param subscriptionId The subscription ID used to send the emergency sms. + */ + @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) + public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber, + int subscriptionId); + } + + /** + * Interface for phone capability listener. + * + */ + public interface PhoneCapabilityChangedListener { + /** + * Callback invoked when phone capability changes. + * Note, this callback triggers regardless of registered subscription. + * + * @param capability the new phone capability + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability); + } + + /** + * Interface for active data subscription ID listener. + */ + public interface ActiveDataSubscriptionIdChangedListener { + /** + * Callback invoked when active data subscription ID changes. + * Note, this callback triggers regardless of registered subscription. + * + * @param subId current subscription used to setup Cellular Internet data. + * For example, it could be the current active opportunistic subscription + * in use, or the subscription user selected as default data subscription in + * DSDS mode. + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public void onActiveDataSubscriptionIdChanged(int subId); + } + + /** + * Interface for modem radio power state listener. + * + * @hide + */ + @SystemApi + public interface RadioPowerStateChangedListener { + /** + * Callback invoked when modem radio power state changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param state the modem radio power state + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onRadioPowerStateChanged(@RadioPowerState int state); + } + + /** + * Interface for carrier network listener. + */ + public interface CarrierNetworkChangeListener { + /** + * Callback invoked when telephony has received notice from a carrier + * app that a network action that could result in connectivity loss + * has been requested by an app using + * {@link android.service.carrier.CarrierService#notifyCarrierNetworkChange(boolean)} + * + * This is optional and is only used to allow the system to provide alternative UI while + * telephony is performing an action that may result in intentional, temporary network + * lack of connectivity. + * + * Note, this callback is pinned to the registered subscription and will be invoked when + * the notifying carrier app has carrier privilege rule on the registered + * subscription. {@link android.telephony.TelephonyManager#hasCarrierPrivileges} + * + * @param active If the carrier network change is or shortly will be active, + * {@code true} indicate that showing alternative UI, {@code false} otherwise. + */ + public void onCarrierNetworkChange(boolean active); + } + + /** + * Interface for registration failures listener. + */ + public interface RegistrationFailedListener { + /** + * Report that Registration or a Location/Routing/Tracking Area update has failed. + * + * <p>Indicate whenever a registration procedure, including a location, routing, or tracking + * area update fails. This includes procedures that do not necessarily result in a change of + * the modem's registration status. If the modem's registration status changes, that is + * reflected in the onNetworkStateChanged() and subsequent + * get{Voice/Data}RegistrationState(). + * + * <p>Because registration failures are ephemeral, this callback is not sticky. + * Registrants will not receive the most recent past value when registering. + * + * @param cellIdentity the CellIdentity, which must include the globally unique identifier + * for the cell (for example, all components of the CGI or ECGI). + * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the + * cell that was chosen for the failed registration attempt. + * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure. + * @param causeCode the primary failure cause code of the procedure. + * For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95 + * For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147 + * For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9 + * For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2 + * Integer.MAX_VALUE if this value is unused. + * @param additionalCauseCode the cause code of any secondary/combined procedure + * if appropriate. For UMTS, if a combined attach succeeds for + * PS only, then the GMM cause code shall be included as an + * additionalCauseCode. For LTE (ESM), cause codes are in + * TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused. + */ + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, + @NonNull String chosenPlmn, @Domain int domain, + int causeCode, int additionalCauseCode); + } + + /** + * Interface for call attributes listener. + * + * @hide + */ + @SystemApi + public interface CallAttributesChangedListener { + /** + * Callback invoked when the call attributes changes on the registered subscription. + * Note, the registration subscription ID comes from {@link TelephonyManager} object + * which registers PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. + * If this TelephonyManager object was created with + * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the + * subscription ID. Otherwise, this callback applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * + * @param callAttributes the call attributes + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + void onCallAttributesChanged(@NonNull CallAttributes callAttributes); + } + + /** + * Interface for barring information listener. + */ + public interface BarringInfoChangedListener { + /** + * Report updated barring information for the current camped/registered cell. + * + * <p>Barring info is provided for all services applicable to the current camped/registered + * cell, for the registered PLMN and current access class/access category. + * + * @param barringInfo for all services on the current cell. + * @see android.telephony.BarringInfo + */ + @RequiresPermission(allOf = { + Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.ACCESS_FINE_LOCATION + }) + public void onBarringInfoChanged(@NonNull BarringInfo barringInfo); + } + + /** + * Interface for current physical channel configuration listener. + * @hide + */ + @SystemApi + public interface PhysicalChannelConfigChangedListener { + /** + * Callback invoked when the current physical channel configuration has changed + * + * @param configs List of the current {@link PhysicalChannelConfig}s + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs); + } + + /** + * Interface for data enabled listener. + * + * @hide + */ + @SystemApi + public interface DataEnabledChangedListener { + /** + * Callback invoked when the data enabled changes. + * + * @param enabled {@code true} if data is enabled, otherwise disabled. + * @param reason Reason for data enabled/disabled. + * See {@link TelephonyManager.DataEnabledReason}. + */ + @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onDataEnabledChanged(boolean enabled, + @DataEnabledReason int reason); } /** @@ -658,8 +1970,7 @@ public class PhoneStateListener { * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}. * If this TelephonyManager object was created with * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the - * subId. Otherwise, this callback applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. + * subId. Otherwise, this callback applies to all subIds. * <p> * Note: The state returned here may differ from that returned by * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that @@ -698,6 +2009,7 @@ public class PhoneStateListener { * same as above, but with the network type. Both called. */ public void onDataConnectionStateChanged(int state, int networkType) { + // default implementation empty } /** @@ -745,6 +2057,7 @@ public class PhoneStateListener { * @param cellInfo is the list of currently visible cells. */ public void onCellInfoChanged(List<CellInfo> cellInfo) { + // default implementation empty } /** @@ -758,7 +2071,7 @@ public class PhoneStateListener { * @param callState {@link PreciseCallState} * @hide */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) @SystemApi public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) { // default implementation empty @@ -777,9 +2090,9 @@ public class PhoneStateListener { * @param preciseDisconnectCause {@link PreciseDisconnectCause}. * */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) - public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause, - int preciseDisconnectCause) { + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) + public void onCallDisconnectCauseChanged(@DisconnectCauses int disconnectCause, + @PreciseDisconnectCauses int preciseDisconnectCause) { // default implementation empty } @@ -795,7 +2108,7 @@ public class PhoneStateListener { * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed. * */ - @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) { // default implementation empty } @@ -817,7 +2130,7 @@ public class PhoneStateListener { * * @param dataConnectionState {@link PreciseDataConnectionState} */ - @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged( @NonNull PreciseDataConnectionState dataConnectionState) { // default implementation empty @@ -855,6 +2168,7 @@ public class PhoneStateListener { */ @SystemApi public void onSrvccStateChanged(@SrvccState int srvccState) { + // default implementation empty } @@ -873,6 +2187,7 @@ public class PhoneStateListener { */ @SystemApi public void onVoiceActivationStateChanged(@SimActivationState int state) { + // default implementation empty } /** @@ -889,6 +2204,7 @@ public class PhoneStateListener { * @hide */ public void onDataActivationStateChanged(@SimActivationState int state) { + // default implementation empty } /** @@ -916,7 +2232,7 @@ public class PhoneStateListener { * * @param telephonyDisplayInfo The display information. */ - @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE)) + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { // default implementation empty } @@ -1030,7 +2346,8 @@ public class PhoneStateListener { /** * Callback invoked when OEM hook raw event is received on the registered subscription. * Note, the registration subId comes from {@link TelephonyManager} object which registers - * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}. + * PhoneStateListener by + * {@link TelephonyManager#registerPhoneStateListener(Executor, PhoneStateListener)}. * If this TelephonyManager object was created with * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * subId. Otherwise, this callback applies to @@ -1052,7 +2369,7 @@ public class PhoneStateListener { * @param capability the new phone capability * @hide */ - public void onPhoneCapabilityChanged(PhoneCapability capability) { + public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability) { // default implementation empty } @@ -1096,7 +2413,8 @@ public class PhoneStateListener { * subId. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. * - * @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE} + * Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} + * * @param state the modem radio power state * @hide */ @@ -1453,18 +2771,16 @@ public class PhoneStateListener { Binder.withCleanCallingIdentity( () -> mExecutor.execute( () -> psl.onImsCallDisconnectCauseChanged(disconnectCause))); - } public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, - @NonNull String chosenPlmn, int domain, - int causeCode, int additionalCauseCode) { + @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onRegistrationFailed( - cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); + cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode))); // default implementation empty } @@ -1475,8 +2791,27 @@ public class PhoneStateListener { Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> psl.onBarringInfoChanged(barringInfo))); } - } + public void onPhysicalChannelConfigChanged(List<PhysicalChannelConfig> configs) { + PhysicalChannelConfigChangedListener listener = + (PhysicalChannelConfigChangedListener) mPhoneStateListenerWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onPhysicalChannelConfigChanged( + configs))); + } + + public void onDataEnabledChanged(boolean enabled, @DataEnabledReason int reason) { + DataEnabledChangedListener listener = + (DataEnabledChangedListener) mPhoneStateListenerWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onDataEnabledChanged( + enabled, reason))); + } + } private void log(String s) { Rlog.d(LOG_TAG, s); diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 3673ae7f7a37..a9548b0a42b4 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -15,6 +15,7 @@ */ package android.telephony; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -24,6 +25,9 @@ import android.compat.annotation.EnabledAfter; import android.content.Context; import android.os.Binder; import android.os.Build; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.Annotation.CallState; @@ -37,6 +41,7 @@ import android.telephony.Annotation.SimActivationState; import android.telephony.Annotation.SrvccState; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; +import android.util.ArraySet; import android.util.Log; import com.android.internal.telephony.IOnSubscriptionsChangedListener; @@ -45,6 +50,7 @@ import com.android.internal.telephony.ITelephonyRegistry; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -206,7 +212,7 @@ public class TelephonyRegistryManager { } /** - * To check the SDK version for {@link #listenForSubscriber}. + * To check the SDK version for {@link #listenWithEventList}. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P) @@ -218,23 +224,23 @@ public class TelephonyRegistryManager { * @param pkg Package name * @param featureId Feature ID * @param listener Listener providing callback - * @param events Events + * @param events List events * @param notifyNow Whether to notify instantly */ - public void listenForSubscriber(int subId, @NonNull String pkg, @NonNull String featureId, - @NonNull PhoneStateListener listener, int events, boolean notifyNow) { + public void listenWithEventList(int subId, @NonNull String pkg, @NonNull String featureId, + @NonNull PhoneStateListener listener, @NonNull int[] events, boolean notifyNow) { try { // subId from PhoneStateListener is deprecated Q on forward, use the subId from // TelephonyManager instance. Keep using subId from PhoneStateListener for pre-Q. if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) { // Since mSubId in PhoneStateListener is deprecated from Q on forward, this is // the only place to set mSubId and its for "informational" only. - listener.mSubId = (events == PhoneStateListener.LISTEN_NONE) + listener.mSubId = (events.length == 0) ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subId; } else if (listener.mSubId != null) { subId = listener.mSubId; } - sRegistry.listenForSubscriber( + sRegistry.listenWithEventList( subId, pkg, featureId, listener.callback, events, notifyNow); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -765,4 +771,366 @@ public class TelephonyRegistryManager { } } + /** + * Notify {@link PhysicalChannelConfig} has changed for a specific subscription. + * + * @param subId the subId + * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel. + */ + public void notifyPhysicalChannelConfigForSubscriber( + int subId, List<PhysicalChannelConfig> configs) { + try { + sRegistry.notifyPhysicalChannelConfigForSubscriber(subId, configs); + } catch (RemoteException ex) { + // system server crash + } + } + + /** + * Notify that the data enabled has changed. + * + * @param enabled True if data is enabled, otherwise disabled. + * @param reason Reason for data enabled/disabled. See {@code REASON_*} in + * {@link TelephonyManager}. + */ + public void notifyDataEnabled(boolean enabled, @TelephonyManager.DataEnabledReason int reason) { + try { + sRegistry.notifyDataEnabled(enabled, reason); + } catch (RemoteException ex) { + // system server crash + } + } + + public @NonNull Set<Integer> getEventsFromListener(@NonNull PhoneStateListener listener) { + + Set<Integer> eventList = new ArraySet<>(); + + if (listener instanceof PhoneStateListener.ServiceStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.MessageWaitingIndicatorChangedListener) { + eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); + } + + if (listener instanceof PhoneStateListener.CallForwardingIndicatorChangedListener) { + eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); + } + + if (listener instanceof PhoneStateListener.CellLocationChangedListener) { + eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED); + } + + if (listener instanceof PhoneStateListener.CallStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.DataConnectionStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.DataActivityListener) { + eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED); + } + + if (listener instanceof PhoneStateListener.SignalStrengthsChangedListener) { + eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED); + } + + if (listener instanceof PhoneStateListener.AlwaysReportedSignalStrengthChangedListener) { + eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); + } + + if (listener instanceof PhoneStateListener.CellInfoChangedListener) { + eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED); + } + + if (listener instanceof PhoneStateListener.PreciseCallStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.CallDisconnectCauseChangedListener) { + eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + } + + if (listener instanceof PhoneStateListener.ImsCallDisconnectCauseChangedListener) { + eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + } + + if (listener instanceof PhoneStateListener.PreciseDataConnectionStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.SrvccStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.VoiceActivationStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.DataActivationStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.UserMobileDataStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.DisplayInfoChangedListener) { + eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED); + } + + if (listener instanceof PhoneStateListener.EmergencyNumberListChangedListener) { + eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); + } + + if (listener instanceof PhoneStateListener.OutgoingEmergencyCallListener) { + eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL); + } + + if (listener instanceof PhoneStateListener.OutgoingEmergencySmsListener) { + eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + } + + if (listener instanceof PhoneStateListener.PhoneCapabilityChangedListener) { + eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED); + } + + if (listener instanceof PhoneStateListener.ActiveDataSubscriptionIdChangedListener) { + eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + } + + if (listener instanceof PhoneStateListener.RadioPowerStateChangedListener) { + eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + } + + if (listener instanceof PhoneStateListener.CarrierNetworkChangeListener) { + eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED); + } + + if (listener instanceof PhoneStateListener.RegistrationFailedListener) { + eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); + } + + if (listener instanceof PhoneStateListener.CallAttributesChangedListener) { + eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + } + + if (listener instanceof PhoneStateListener.BarringInfoChangedListener) { + eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + } + + if (listener instanceof PhoneStateListener.PhysicalChannelConfigChangedListener) { + eventList.add(PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); + } + + if (listener instanceof PhoneStateListener.DataEnabledChangedListener) { + eventList.add(PhoneStateListener.EVENT_DATA_ENABLED_CHANGED); + } + + return eventList; + } + + private @NonNull Set<Integer> getEventsFromBitmask(int eventMask) { + + Set<Integer> eventList = new ArraySet<>(); + + if ((eventMask & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { + eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { + eventList.add(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { + eventList.add(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { + eventList.add(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CALL_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_CALL_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { + eventList.add(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { + eventList.add(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { + eventList.add(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) { + eventList.add(PhoneStateListener.EVENT_CELL_INFO_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO) != 0) { + eventList.add(PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) { + eventList.add(PhoneStateListener.EVENT_OEM_HOOK_RAW); + } + + if ((eventMask & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) { + eventList.add(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) { + eventList.add(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { + eventList.add(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { + eventList.add(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) { + eventList.add(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { + eventList.add(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) { + eventList.add(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { + eventList.add(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { + eventList.add(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) { + eventList.add(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { + eventList.add(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + } + + if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL) != 0) { + eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL); + } + + if ((eventMask & PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS) != 0) { + eventList.add(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + } + + if ((eventMask & PhoneStateListener.LISTEN_REGISTRATION_FAILURE) != 0) { + eventList.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); + } + + if ((eventMask & PhoneStateListener.LISTEN_BARRING_INFO) != 0) { + eventList.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + } + return eventList; + + } + + /** + * Registers a listener object to receive notification of changes + * in specified telephony states. + * <p> + * To register a listener, pass a {@link PhoneStateListener} which implements + * interfaces of events. For example, + * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements + * {@link PhoneStateListener.ServiceStateChangedListener}. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the listener object and passes the current (updated) + * values. + * <p> + * + * If this TelephonyManager object has been created with + * {@link TelephonyManager#createForSubscriptionId}, applies to the given subId. + * Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}. + * To listen events for multiple subIds, pass a separate listener object to + * each TelephonyManager object created with {@link TelephonyManager#createForSubscriptionId}. + * + * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> + * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A + * {@link SecurityException} will be thrown otherwise. + * + * This API should be used sparingly -- large numbers of listeners will cause system + * instability. If a process has registered too many listeners without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more listeners. + * + * @param listener The {@link PhoneStateListener} object to register. + */ + public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, int subId, + String pkgName, String attributionTag, @NonNull PhoneStateListener listener, + boolean notifyNow) { + listener.setExecutor(executor); + registerPhoneStateListener(subId, pkgName, attributionTag, listener, + getEventsFromListener(listener), notifyNow); + } + + public void registerPhoneStateListenerWithEvents(int subId, String pkgName, + String attributionTag, @NonNull PhoneStateListener listener, int events, + boolean notifyNow) { + registerPhoneStateListener( + subId, pkgName, attributionTag, listener, getEventsFromBitmask(events), notifyNow); + } + + private void registerPhoneStateListener(int subId, + String pkgName, String attributionTag, @NonNull PhoneStateListener listener, + @NonNull Set<Integer> events, boolean notifyNow) { + if (listener == null) { + throw new IllegalStateException("telephony service is null."); + } + + listenWithEventList(subId, pkgName, attributionTag, listener, + events.stream().mapToInt(i -> i).toArray(), notifyNow); + } + + /** + * Unregister an existing {@link PhoneStateListener}. + * + * @param listener The {@link PhoneStateListener} object to unregister. + */ + public void unregisterPhoneStateListener(int subId, String pkgName, String attributionTag, + @NonNull PhoneStateListener listener, + boolean notifyNow) { + listenWithEventList(subId, pkgName, attributionTag, listener, new int[0], notifyNow); + } } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 17d3ae4a7ff0..471f2c2aecae 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -24,11 +24,12 @@ import android.content.res.Resources; import android.icu.text.MeasureFormat; import android.icu.util.Measure; import android.icu.util.MeasureUnit; -import android.net.NetworkUtils; import android.text.BidiFormatter; import android.text.TextUtils; import android.view.View; +import com.android.net.module.util.Inet4AddressUtils; + import java.util.Locale; /** @@ -207,7 +208,7 @@ public final class Formatter { */ @Deprecated public static String formatIpAddress(int ipv4Address) { - return NetworkUtils.intToInetAddress(ipv4Address).getHostAddress(); + return Inet4AddressUtils.intToInet4AddressHTL(ipv4Address).getHostAddress(); } private static final int SECONDS_PER_MINUTE = 60; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 537498c44d5e..9d0ae30f1420 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -39,7 +39,6 @@ public class FeatureFlagUtils { public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; - public static final String DYNAMIC_SYSTEM = "settings_dynamic_system"; public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; public static final String SETTINGS_FUSE_FLAG = "settings_fuse"; /** @hide */ @@ -53,7 +52,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_audio_switcher", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "true"); - DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false"); DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false"); diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS index 8f3d9f6f5881..14aa38682d2b 100644 --- a/core/java/android/util/OWNERS +++ b/core/java/android/util/OWNERS @@ -1,3 +1,6 @@ per-file FeatureFlagUtils.java = sbasi@google.com per-file FeatureFlagUtils.java = tmfang@google.com per-file FeatureFlagUtils.java = asapperstein@google.com + +per-file TypedValue.java = file:/core/java/android/content/res/OWNERS +per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java index 93b5fd4cd4b6..9df213b2092f 100644 --- a/core/java/android/uwb/AngleMeasurement.java +++ b/core/java/android/uwb/AngleMeasurement.java @@ -18,6 +18,7 @@ package android.uwb; import android.annotation.FloatRange; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -31,6 +32,7 @@ import java.util.Objects; * * @hide */ +@SystemApi public final class AngleMeasurement implements Parcelable { private final double mRadians; private final double mErrorRadians; diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java index 20a1c7aa72d0..3d8626b98bed 100644 --- a/core/java/android/uwb/AngleOfArrivalMeasurement.java +++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java @@ -18,6 +18,7 @@ package android.uwb; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -28,6 +29,7 @@ import java.util.Objects; * * @hide */ +@SystemApi public final class AngleOfArrivalMeasurement implements Parcelable { private final AngleMeasurement mAzimuthAngleMeasurement; private final AngleMeasurement mAltitudeAngleMeasurement; @@ -53,7 +55,7 @@ public final class AngleOfArrivalMeasurement implements Parcelable { * @return the azimuth {@link AngleMeasurement} */ @NonNull - public AngleMeasurement getAzimuthAngleMeasurement() { + public AngleMeasurement getAzimuth() { return mAzimuthAngleMeasurement; } @@ -70,7 +72,7 @@ public final class AngleOfArrivalMeasurement implements Parcelable { * @return altitude {@link AngleMeasurement} or null when this is not available */ @Nullable - public AngleMeasurement getAltitudeAngleMeasurement() { + public AngleMeasurement getAltitude() { return mAltitudeAngleMeasurement; } @@ -85,8 +87,8 @@ public final class AngleOfArrivalMeasurement implements Parcelable { if (obj instanceof AngleOfArrivalMeasurement) { AngleOfArrivalMeasurement other = (AngleOfArrivalMeasurement) obj; - return mAzimuthAngleMeasurement.equals(other.getAzimuthAngleMeasurement()) - && mAltitudeAngleMeasurement.equals(other.getAltitudeAngleMeasurement()); + return mAzimuthAngleMeasurement.equals(other.getAzimuth()) + && mAltitudeAngleMeasurement.equals(other.getAltitude()); } return false; } @@ -116,11 +118,9 @@ public final class AngleOfArrivalMeasurement implements Parcelable { public AngleOfArrivalMeasurement createFromParcel(Parcel in) { Builder builder = new Builder(); - builder.setAzimuthAngleMeasurement( - in.readParcelable(AngleMeasurement.class.getClassLoader())); + builder.setAzimuth(in.readParcelable(AngleMeasurement.class.getClassLoader())); - builder.setAltitudeAngleMeasurement( - in.readParcelable(AngleMeasurement.class.getClassLoader())); + builder.setAltitude(in.readParcelable(AngleMeasurement.class.getClassLoader())); return builder.build(); } @@ -144,7 +144,7 @@ public final class AngleOfArrivalMeasurement implements Parcelable { * @param azimuthAngle azimuth angle */ @NonNull - public Builder setAzimuthAngleMeasurement(@NonNull AngleMeasurement azimuthAngle) { + public Builder setAzimuth(@NonNull AngleMeasurement azimuthAngle) { mAzimuthAngleMeasurement = azimuthAngle; return this; } @@ -155,7 +155,7 @@ public final class AngleOfArrivalMeasurement implements Parcelable { * @param altitudeAngle altitude angle */ @NonNull - public Builder setAltitudeAngleMeasurement(@NonNull AngleMeasurement altitudeAngle) { + public Builder setAltitude(@NonNull AngleMeasurement altitudeAngle) { mAltitudeAngleMeasurement = altitudeAngle; return this; } diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java index 10c2172d5a6b..2a9bbdf3ec5d 100644 --- a/core/java/android/uwb/DistanceMeasurement.java +++ b/core/java/android/uwb/DistanceMeasurement.java @@ -19,6 +19,7 @@ package android.uwb; import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,6 +33,7 @@ import java.util.Objects; * * @hide */ +@SystemApi public final class DistanceMeasurement implements Parcelable { private final double mMeters; private final double mErrorMeters; diff --git a/core/java/android/uwb/RangingMeasurement.java b/core/java/android/uwb/RangingMeasurement.java index 50e5f0d8d554..249e2b746d0d 100644 --- a/core/java/android/uwb/RangingMeasurement.java +++ b/core/java/android/uwb/RangingMeasurement.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -33,6 +34,7 @@ import java.util.Objects; * * @hide */ +@SystemApi public final class RangingMeasurement implements Parcelable { private final UwbAddress mRemoteDeviceAddress; private final @Status int mStatus; diff --git a/core/java/android/uwb/RangingReport.java b/core/java/android/uwb/RangingReport.java index 5b5f084914ab..7a2df8617700 100644 --- a/core/java/android/uwb/RangingReport.java +++ b/core/java/android/uwb/RangingReport.java @@ -18,6 +18,7 @@ package android.uwb; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -30,6 +31,7 @@ import java.util.Objects; * * @hide */ +@SystemApi public final class RangingReport implements Parcelable { private final List<RangingMeasurement> mRangingMeasurements; diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java index 0f87af415825..bfa8bf21ec6a 100644 --- a/core/java/android/uwb/RangingSession.java +++ b/core/java/android/uwb/RangingSession.java @@ -18,6 +18,7 @@ package android.uwb; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Binder; import android.os.PersistableBundle; import android.os.RemoteException; @@ -42,6 +43,7 @@ import java.util.concurrent.Executor; * * @hide */ +@SystemApi public final class RangingSession implements AutoCloseable { private static final String TAG = "Uwb.RangingSession"; private final SessionHandle mSessionHandle; diff --git a/core/java/android/uwb/UwbAddress.java b/core/java/android/uwb/UwbAddress.java index b9523a307c42..22883be88760 100644 --- a/core/java/android/uwb/UwbAddress.java +++ b/core/java/android/uwb/UwbAddress.java @@ -18,6 +18,7 @@ package android.uwb; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -28,6 +29,7 @@ import java.util.Arrays; * * @hide */ +@SystemApi public final class UwbAddress implements Parcelable { public static final int SHORT_ADDRESS_BYTE_LENGTH = 2; public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8; diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 15ee5b5f22eb..8adfe0601b14 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.IBinder; @@ -44,6 +45,7 @@ import java.util.concurrent.Executor; * * @hide */ +@SystemApi @SystemService(Context.UWB_SERVICE) public final class UwbManager { private IUwbAdapter mUwbAdapter; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0aaedf3f5a14..266c1b0a0e95 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -10379,7 +10379,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) protected boolean isVisibleToUser(Rect boundInView) { if (mAttachInfo != null) { // Attached to invisible window means this view is not visible. diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS index 93b5a2e8bc28..b1d3967a8b04 100644 --- a/core/java/android/view/accessibility/OWNERS +++ b/core/java/android/view/accessibility/OWNERS @@ -9,3 +9,4 @@ sumir@google.com ogunwale@google.com jjaggi@google.com pweaver@google.com +ryanlwlin@google.com diff --git a/core/java/android/view/textclassifier/OWNERS b/core/java/android/view/textclassifier/OWNERS index ac80d9f4cdd0..4bcdeea472e3 100644 --- a/core/java/android/view/textclassifier/OWNERS +++ b/core/java/android/view/textclassifier/OWNERS @@ -6,3 +6,5 @@ svetoslavganov@android.com svetoslavganov@google.com augale@google.com joannechung@google.com +tonymak@google.com +licha@google.com diff --git a/core/java/android/view/textservice/OWNERS b/core/java/android/view/textservice/OWNERS index 582be8dc4594..0471e29a25cd 100644 --- a/core/java/android/view/textservice/OWNERS +++ b/core/java/android/view/textservice/OWNERS @@ -1,3 +1,3 @@ -# Bug component: 34867 +# Bug component: 816455 -include ../inputmethod/OWNERS +include /services/core/java/com/android/server/textservices/OWNERS diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index b9ff26b7d9ff..6281ee9d05d1 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -39,6 +39,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.inspector.InspectableProperty; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -776,17 +777,23 @@ public abstract class AbsSeekBar extends ProgressBar { /** * Grows {@code r} from its center such that each dimension is at least {@code minimumSize}. + * + * The result will still have the same {@link Rect#centerX()} and {@link Rect#centerY()} as the + * input. + * + * @hide */ - private void growRectTo(Rect r, int minimumSize) { - int dy = (minimumSize - r.height()) / 2; + @VisibleForTesting + public void growRectTo(Rect r, int minimumSize) { + int dy = minimumSize - r.height(); if (dy > 0) { - r.top -= dy; - r.bottom += dy; + r.top -= (dy + 1) / 2; + r.bottom += dy / 2; } - int dx = (minimumSize - r.width()) / 2; + int dx = minimumSize - r.width(); if (dx > 0) { - r.left -= dx; - r.right += dx; + r.left -= (dx + 1) / 2; + r.right += dx / 2; } } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index e6a166140d89..beef9825b72a 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -129,7 +129,7 @@ interface IBatteryStats { void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph); void noteWifiBatchedScanStoppedFromSource(in WorkSource ws); void noteWifiRadioPowerState(int powerState, long timestampNs, int uid); - void noteNetworkInterfaceType(String iface, int type); + void noteNetworkInterfaceForTransports(String iface, in int[] transportTypes); void noteNetworkStatsEnabled(); void noteDeviceIdleMode(int mode, String activeReason, int activeUid); void setBatteryState(int status, int health, int plugType, int level, int temp, int volt, diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS index c5a956a81d8d..7ade05cc6de1 100644 --- a/core/java/com/android/internal/app/OWNERS +++ b/core/java/com/android/internal/app/OWNERS @@ -3,3 +3,6 @@ per-file *Resolver* = file:/packages/SystemUI/OWNERS per-file *Chooser* = file:/packages/SystemUI/OWNERS per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS +per-file IVoice* = file:/core/java/android/service/voice/OWNERS +per-file *Hotword* = file:/core/java/android/service/voice/OWNERS +per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java index 670ca9f6091e..03fe4551c249 100644 --- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java +++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java @@ -32,6 +32,7 @@ public class CompatibilityChangeInfo implements Parcelable { private final boolean mDisabled; private final boolean mLoggingOnly; private final @Nullable String mDescription; + private final boolean mOverridable; public long getId() { return mChangeId; @@ -58,9 +59,13 @@ public class CompatibilityChangeInfo implements Parcelable { return mDescription; } + public boolean getOverridable() { + return mOverridable; + } + public CompatibilityChangeInfo( Long changeId, String name, int enableAfterTargetSdk, int enableSinceTargetSdk, - boolean disabled, boolean loggingOnly, String description) { + boolean disabled, boolean loggingOnly, String description, boolean overridable) { this.mChangeId = changeId; this.mName = name; if (enableAfterTargetSdk > 0) { @@ -75,6 +80,7 @@ public class CompatibilityChangeInfo implements Parcelable { this.mDisabled = disabled; this.mLoggingOnly = loggingOnly; this.mDescription = description; + this.mOverridable = overridable; } public CompatibilityChangeInfo(CompatibilityChangeInfo other) { @@ -84,6 +90,7 @@ public class CompatibilityChangeInfo implements Parcelable { this.mDisabled = other.mDisabled; this.mLoggingOnly = other.mLoggingOnly; this.mDescription = other.mDescription; + this.mOverridable = other.mOverridable; } private CompatibilityChangeInfo(Parcel in) { @@ -93,6 +100,7 @@ public class CompatibilityChangeInfo implements Parcelable { mDisabled = in.readBoolean(); mLoggingOnly = in.readBoolean(); mDescription = in.readString(); + mOverridable = in.readBoolean(); } @Override @@ -108,6 +116,7 @@ public class CompatibilityChangeInfo implements Parcelable { dest.writeBoolean(mDisabled); dest.writeBoolean(mLoggingOnly); dest.writeString(mDescription); + dest.writeBoolean(mOverridable); } @Override @@ -126,6 +135,9 @@ public class CompatibilityChangeInfo implements Parcelable { if (getLoggingOnly()) { sb.append("; loggingOnly"); } + if (getOverridable()) { + sb.append("; overridable"); + } return sb.append(")").toString(); } @@ -143,8 +155,8 @@ public class CompatibilityChangeInfo implements Parcelable { && this.mEnableSinceTargetSdk == that.mEnableSinceTargetSdk && this.mDisabled == that.mDisabled && this.mLoggingOnly == that.mLoggingOnly - && this.mDescription.equals(that.mDescription); - + && this.mDescription.equals(that.mDescription) + && this.mOverridable == that.mOverridable; } public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR = diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index cc266d60465e..a5eb5f607c12 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -22,17 +22,16 @@ import java.util.Map; parcelable CompatibilityChangeConfig; parcelable CompatibilityChangeInfo; - /** * Platform private API for talking with the PlatformCompat service. * - * <p> Should be used for gating and logging from non-app processes. - * For app processes please use android.compat.Compatibility API. + * <p>Should be used for gating and logging from non-app processes. + * + * <p>Note: for app processes please use {@code android.compat.Compatibility} API. * * {@hide} */ -interface IPlatformCompat -{ +interface IPlatformCompat { /** * Reports that a compatibility change is affecting an app process now. @@ -40,8 +39,9 @@ interface IPlatformCompat * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)}, * you do not need to call this API directly. The change will be reported for you. * - * @param changeId The ID of the compatibility change taking effect. - * @param appInfo Representing the affected app. + * @param changeId the ID of the compatibility change taking effect + * @param appInfo representing the affected app + * @throws SecurityException if logging is not allowed */ void reportChange(long changeId, in ApplicationInfo appInfo); @@ -51,11 +51,12 @@ interface IPlatformCompat * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, String)}, * you do not need to call this API directly. The change will be reported for you. * - * @param changeId The ID of the compatibility change taking effect. - * @param userId The ID of the user that the operation is done for. - * @param packageName The package name of the app in question. + * @param changeId the ID of the compatibility change taking effect + * @param userId the ID of the user that the operation is done for + * @param packageName the package name of the app in question + * @throws SecurityException if logging is not allowed */ - void reportChangeByPackageName(long changeId, in String packageName, int userId); + void reportChangeByPackageName(long changeId, in String packageName, int userId); /** * Reports that a compatibility change is affecting an app process now. @@ -63,13 +64,14 @@ interface IPlatformCompat * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, int)}, * you do not need to call this API directly. The change will be reported for you. * - * @param changeId The ID of the compatibility change taking effect. - * @param uid The UID of the app in question. + * @param changeId the ID of the compatibility change taking effect + * @param uid the UID of the app in question + * @throws SecurityException if logging is not allowed */ void reportChangeByUid(long changeId, int uid); /** - * Query if a given compatibility change is enabled for an app process. This method should + * Queries if a given compatibility change is enabled for an app process. This method should * be called when implementing functionality on behalf of the affected app. * * <p>If this method returns {@code true}, the calling code should implement the compatibility @@ -79,14 +81,15 @@ interface IPlatformCompat * <p>It will also report the change as {@link #reportChange(long, ApplicationInfo)} would, so * there is no need to call that method directly. * - * @param changeId The ID of the compatibility change in question. - * @param appInfo Representing the app in question. - * @return {@code true} if the change is enabled for the current app. + * @param changeId the ID of the compatibility change in question + * @param appInfo representing the app in question + * @return {@code true} if the change is enabled for the current app + * @throws SecurityException if logging or reading compat confis is not allowed */ boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo); /** - * Query if a given compatibility change is enabled for an app process. This method should + * Queries if a given compatibility change is enabled for an app process. This method should * be called when implementing functionality on behalf of the affected app. * * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a package name @@ -102,15 +105,16 @@ interface IPlatformCompat * <p>It will also report the change as {@link #reportChange(long, String)} would, so there is * no need to call that method directly. * - * @param changeId The ID of the compatibility change in question. - * @param packageName The package name of the app in question. - * @param userId The ID of the user that the operation is done for. - * @return {@code true} if the change is enabled for the current app. + * @param changeId the ID of the compatibility change in question + * @param packageName the package name of the app in question + * @param userId the ID of the user that the operation is done for + * @return {@code true} if the change is enabled for the current app + * @throws SecurityException if logging or reading compat confis is not allowed */ boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId); /** - * Query if a given compatibility change is enabled for an app process. This method should + * Queries if a given compatibility change is enabled for an app process. This method should * be called when implementing functionality on behalf of the affected app. * * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a uid @@ -127,110 +131,132 @@ interface IPlatformCompat * <p>It will also report the change as {@link #reportChange(long, int)} would, so there is * no need to call that method directly. * - * @param changeId The ID of the compatibility change in question. - * @param uid The UID of the app in question. - * @return {@code true} if the change is enabled for the current app. + * @param changeId the ID of the compatibility change in question + * @param uid the UID of the app in question + * @return {@code true} if the change is enabled for the current app + * @throws SecurityException if logging or reading compat confis is not allowed */ boolean isChangeEnabledByUid(long changeId, int uid); /** - * Add overrides to compatibility changes. Kills the app to allow the changes to take effect. + * Adds overrides to compatibility changes. * - * @param overrides Parcelable containing the compat change overrides to be applied. - * @param packageName The package name of the app whose changes will be overridden. + * <p>Kills the app to allow the changes to take effect. * + * @param overrides parcelable containing the compat change overrides to be applied + * @param packageName the package name of the app whose changes will be overridden + * @throws SecurityException if overriding changes is not permitted */ void setOverrides(in CompatibilityChangeConfig overrides, in String packageName); /** - * Add overrides to compatibility changes. Doesn't kill the app, to be only used in tests. + * Adds overrides to compatibility changes. * - * @param overrides Parcelable containing the compat change overrides to be applied. - * @param packageName The package name of the app whose changes will be overridden. + * <p>Does not kill the app, to be only used in tests. * + * @param overrides parcelable containing the compat change overrides to be applied + * @param packageName the package name of the app whose changes will be overridden + * @throws SecurityException if overriding changes is not permitted. */ void setOverridesForTest(in CompatibilityChangeConfig overrides, in String packageName); /** - * Removes an override previously added via {@link #setOverrides(CompatibilityChangeConfig, - * String)}. This restores the default behaviour for the given change and app, once any app - * processes have been restarted. - * Kills the app to allow the changes to take effect. + * Restores the default behaviour for the given change and app. * - * @param changeId The ID of the change that was overridden. - * @param packageName The app package name that was overridden. - * @return {@code true} if an override existed; + * <p>Kills the app to allow the changes to take effect. + * + * @param changeId the ID of the change that was overridden + * @param packageName the app package name that was overridden + * @return {@code true} if an override existed + * @throws SecurityException if overriding changes is not permitted */ boolean clearOverride(long changeId, String packageName); /** - * Enable all compatibility changes which have enabledSinceTargetSdk == - * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the - * changes to take effect. + * Restores the default behaviour for the given change and app. * - * @param packageName The package name of the app whose compatibility changes will be enabled. - * @param targetSdkVersion The targetSdkVersion for filtering the changes to be enabled. + * <p>Does not kill the app; to be only used in tests. + * + * @param changeId the ID of the change that was overridden + * @param packageName the app package name that was overridden + * @throws SecurityException if overriding changes is not permitted + */ + void clearOverrideForTest(long changeId, String packageName); + + /** + * Enables all compatibility changes that have enabledSinceTargetSdk == + * {@param targetSdkVersion} for an app, subject to the policy. + * + * <p>Kills the app to allow the changes to take effect. * + * @param packageName The package name of the app whose compatibility changes will be + * enabled. + * @param targetSdkVersion The targetSdkVersion for filtering the changes to be enabled. * @return The number of changes that were enabled. + * @throws SecurityException if overriding changes is not permitted. */ int enableTargetSdkChanges(in String packageName, int targetSdkVersion); /** - * Disable all compatibility changes which have enabledAfterTargetSdk == - * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the - * changes to take effect. + * Disables all compatibility changes that have enabledAfterTargetSdk == + * {@param targetSdkVersion} for an app, subject to the policy. * - * @param packageName The package name of the app whose compatibility changes will be disabled. - * @param targetSdkVersion The targetSdkVersion for filtering the changes to be disabled. + * <p>Kills the app to allow the changes to take effect. * - * @return The number of changes that were disabled. + * @param packageName the package name of the app whose compatibility changes will be + * disabled + * @param targetSdkVersion the targetSdkVersion for filtering the changes to be disabled + * @return the number of changes that were disabled + * @throws SecurityException if overriding changes is not permitted. */ int disableTargetSdkChanges(in String packageName, int targetSdkVersion); /** - * Revert overrides to compatibility changes. Kills the app to allow the changes to take effect. + * Restores the default behaviour for the given app. * - * @param packageName The package name of the app whose overrides will be cleared. + * <p>Kills the app to allow the changes to take effect. * + * @param packageName the package name of the app whose overrides will be cleared + * @throws SecurityException if overriding changes is not permitted */ void clearOverrides(in String packageName); /** - * Revert overrides to compatibility changes. Doesn't kill the app, to be only used in tests. + * Restores the default behaviour for the given app. * - * @param packageName The package name of the app whose overrides will be cleared. + * <p>Does not kill the app; to be only used in tests. * + * @param packageName the package name of the app whose overrides will be cleared + * @throws SecurityException if overriding changes is not permitted */ void clearOverridesForTest(in String packageName); - /** * Get configs for an application. * - * @param appInfo The application whose config will be returned. - * - * @return A {@link CompatibilityChangeConfig}, representing whether a change is enabled for - * the given app or not. + * @param appInfo the application whose config will be returned + * @return a {@link CompatibilityChangeConfig}, representing whether a change is enabled for + * the given app or not */ CompatibilityChangeConfig getAppConfig(in ApplicationInfo appInfo); /** * List all compatibility changes. * - * @return An array of {@link CompatChangeInfo} known to the service. + * @return an array of {@link CompatibilityChangeInfo} known to the service */ CompatibilityChangeInfo[] listAllChanges(); /** - * List the compatibility changes that should be present in the UI. - * Filters out certain changes like e.g. logging only. - * - * @return An array of {@link CompatChangeInfo}. - */ + * List the compatibility changes that should be present in the UI. + * Filters out certain changes like e.g. logging only. + * + * @return an array of {@link CompatibilityChangeInfo} + */ CompatibilityChangeInfo[] listUIChanges(); /** - * Get an instance that can determine whether a changeid can be overridden for a package name. + * Gets an instance that can determine whether a changeid can be overridden for a package name. */ IOverrideValidator getOverrideValidator(); } diff --git a/core/java/com/android/internal/graphics/fonts/OWNERS b/core/java/com/android/internal/graphics/fonts/OWNERS new file mode 100644 index 000000000000..18486af9d12c --- /dev/null +++ b/core/java/com/android/internal/graphics/fonts/OWNERS @@ -0,0 +1 @@ +include /graphics/java/android/graphics/fonts/OWNERS diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index c0648ab89c41..2e7629a76dee 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -129,7 +129,7 @@ public class VpnConfig implements Parcelable { String[] routes = routesStr.trim().split(" "); for (String route : routes) { //each route is ip/prefix - RouteInfo info = new RouteInfo(new IpPrefix(route), null); + RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST); this.routes.add(info); updateAllowedFamilies(info.getDestination().getAddress()); } diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/com/android/internal/net/VpnInfo.java deleted file mode 100644 index e74af5eb50de..000000000000 --- a/core/java/com/android/internal/net/VpnInfo.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2015 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.internal.net; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Arrays; - -/** - * A lightweight container used to carry information of the ongoing VPN. - * Internal use only.. - * - * @hide - */ -public class VpnInfo implements Parcelable { - public int ownerUid; - public String vpnIface; - public String[] underlyingIfaces; - - @Override - public String toString() { - return "VpnInfo{" - + "ownerUid=" + ownerUid - + ", vpnIface='" + vpnIface + '\'' - + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\'' - + '}'; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(ownerUid); - dest.writeString(vpnIface); - dest.writeStringArray(underlyingIfaces); - } - - public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() { - @Override - public VpnInfo createFromParcel(Parcel source) { - VpnInfo info = new VpnInfo(); - info.ownerUid = source.readInt(); - info.vpnIface = source.readString(); - info.underlyingIfaces = source.readStringArray(); - return info; - } - - @Override - public VpnInfo[] newArray(int size) { - return new VpnInfo[size]; - } - }; -} diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 8bfc28ed5e52..ae680e0febac 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -23,7 +23,6 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.SensorManager; -import android.net.ConnectivityManager; import android.os.BatteryStats; import android.os.BatteryStats.Uid; import android.os.Build; @@ -37,6 +36,7 @@ import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; +import android.telephony.TelephonyManager; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.Log; @@ -148,12 +148,11 @@ public class BatteryStatsHelper { boolean mHasBluetoothPowerReporting = false; public static boolean checkWifiOnly(Context context) { - ConnectivityManager cm = (ConnectivityManager) context.getSystemService( - Context.CONNECTIVITY_SERVICE); - if (cm == null) { + final TelephonyManager tm = context.getSystemService(TelephonyManager.class); + if (tm == null) { return false; } - return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + return !tm.isDataCapable(); } public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 1071d9f2a918..2d75b70333f2 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -16,6 +16,8 @@ package com.android.internal.os; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.os.BatteryStatsManager.NUM_WIFI_STATES; import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES; @@ -32,7 +34,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.hardware.usb.UsbManager; -import android.net.ConnectivityManager; import android.net.INetworkStatsService; import android.net.NetworkStats; import android.net.Uri; @@ -101,6 +102,7 @@ import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; +import com.android.net.module.util.NetworkCapabilitiesUtils; import libcore.util.EmptyArray; @@ -6099,11 +6101,12 @@ public class BatteryStatsImpl extends BatteryStats { } /** @hide */ - public void noteNetworkInterfaceType(String iface, int networkType) { + public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes) { if (TextUtils.isEmpty(iface)) return; + final int displayTransport = NetworkCapabilitiesUtils.getDisplayTransport(transportTypes); synchronized (mModemNetworkLock) { - if (ConnectivityManager.isNetworkTypeMobile(networkType)) { + if (displayTransport == TRANSPORT_CELLULAR) { mModemIfaces = includeInStringArray(mModemIfaces, iface); if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces); } else { @@ -6113,7 +6116,7 @@ public class BatteryStatsImpl extends BatteryStats { } synchronized (mWifiNetworkLock) { - if (ConnectivityManager.isNetworkTypeWifi(networkType)) { + if (displayTransport == TRANSPORT_WIFI) { mWifiIfaces = includeInStringArray(mWifiIfaces, iface); if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces); } else { diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS index 8f78b2a3a5ea..1b07aa0cf0b7 100644 --- a/core/java/com/android/internal/os/OWNERS +++ b/core/java/com/android/internal/os/OWNERS @@ -6,3 +6,5 @@ per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS per-file BatteryStats* = file:/BATTERY_STATS_OWNERS per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS +per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS + diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 790d7f7ab694..6860759eea8a 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -69,15 +69,16 @@ public class WrapperInit { // Tell the Zygote what our actual PID is (since it only knows about the // wrapper that it directly forked). if (fdNum != 0) { + FileDescriptor fd = new FileDescriptor(); try { - FileDescriptor fd = new FileDescriptor(); fd.setInt$(fdNum); DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); os.writeInt(Process.myPid()); os.close(); - IoUtils.closeQuietly(fd); } catch (IOException ex) { Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); + } finally { + IoUtils.closeQuietly(fd); } } diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index d2dc7c283ff7..854fb17e692b 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -23,6 +23,7 @@ import android.telephony.CellInfo; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.TelephonyDisplayInfo; import android.telephony.PhoneCapability; +import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; import android.telephony.ServiceState; @@ -68,4 +69,6 @@ oneway interface IPhoneStateListener { void onRegistrationFailed(in CellIdentity cellIdentity, String chosenPlmn, int domain, int causeCode, int additionalCauseCode); void onBarringInfoChanged(in BarringInfo barringInfo); + void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs); + void onDataEnabledChanged(boolean enabled, int reason); } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 5f2dcc3d22e1..2a73dacbc94a 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -41,16 +41,8 @@ interface ITelephonyRegistry { IOnSubscriptionsChangedListener callback); void removeOnSubscriptionsChangedListener(String pkg, IOnSubscriptionsChangedListener callback); - /** - * @deprecated Use {@link #listenWithFeature(String, String, IPhoneStateListener, int, - * boolean) instead - */ - @UnsupportedAppUsage - void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow); - void listenWithFeature(String pkg, String featureId, IPhoneStateListener callback, int events, - boolean notifyNow); - void listenForSubscriber(in int subId, String pkg, String featureId, - IPhoneStateListener callback, int events, boolean notifyNow); + void listenWithEventList(in int subId, String pkg, String featureId, + IPhoneStateListener callback, in int[] events, boolean notifyNow); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void notifyCallStateForAllSubs(int state, String incomingNumber); void notifyCallState(in int phoneId, in int subId, int state, String incomingNumber); @@ -99,4 +91,7 @@ interface ITelephonyRegistry { void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity, String chosenPlmn, int domain, int causeCode, int additionalCauseCode); void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo); + void notifyPhysicalChannelConfigForSubscriber(in int subId, + in List<PhysicalChannelConfig> configs); + void notifyDataEnabled(boolean enabled, int reason); } diff --git a/core/java/com/android/internal/textservice/OWNERS b/core/java/com/android/internal/textservice/OWNERS new file mode 100644 index 000000000000..0471e29a25cd --- /dev/null +++ b/core/java/com/android/internal/textservice/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 816455 + +include /services/core/java/com/android/server/textservices/OWNERS diff --git a/core/java/com/android/internal/util/LocationPermissionChecker.java b/core/java/com/android/internal/util/LocationPermissionChecker.java index cd8fc350362d..c583d5a5be37 100644 --- a/core/java/com/android/internal/util/LocationPermissionChecker.java +++ b/core/java/com/android/internal/util/LocationPermissionChecker.java @@ -24,6 +24,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.location.LocationManager; +import android.net.NetworkStack; import android.os.Binder; import android.os.Build; import android.os.UserHandle; @@ -147,6 +148,13 @@ public class LocationPermissionChecker { int uid, @Nullable String message) { checkPackage(uid, pkgName); + // Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_STACK & MAINLINE_NETWORK_STACK + // are granted a bypass. + if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid) + || checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) { + return SUCCEEDED; + } + // Location mode must be enabled if (!isLocationModeEnabled()) { return ERROR_LOCATION_MODE_OFF; @@ -259,4 +267,37 @@ public class LocationPermissionChecker { // We don't care about pid, pass in -1 return mContext.checkPermission(permissionType, -1, uid); } + + /** + * Returns true if the |uid| holds NETWORK_SETTINGS permission. + */ + public boolean checkNetworkSettingsPermission(int uid) { + return getUidPermission(android.Manifest.permission.NETWORK_SETTINGS, uid) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission. + */ + public boolean checkNetworkSetupWizardPermission(int uid) { + return getUidPermission(android.Manifest.permission.NETWORK_SETUP_WIZARD, uid) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Returns true if the |uid| holds NETWORK_STACK permission. + */ + public boolean checkNetworkStackPermission(int uid) { + return getUidPermission(android.Manifest.permission.NETWORK_STACK, uid) + == PackageManager.PERMISSION_GRANTED; + } + + /** + * Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission. + */ + public boolean checkMainlineNetworkStackPermission(int uid) { + return getUidPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid) + == PackageManager.PERMISSION_GRANTED; + } + } diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS index cca39ea3287d..ae566c3988cd 100644 --- a/core/java/com/android/internal/widget/OWNERS +++ b/core/java/com/android/internal/widget/OWNERS @@ -1 +1,7 @@ per-file PointerLocationView.java = michaelwr@google.com, svv@google.com + +# LockSettings related +per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS +per-file *LockScreen* = file:/services/core/java/com/android/server/locksettings/OWNERS +per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS +per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java index 93f89b5db820..139b88b108c5 100644 --- a/core/java/com/android/server/net/BaseNetworkObserver.java +++ b/core/java/com/android/server/net/BaseNetworkObserver.java @@ -64,7 +64,7 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub { } @Override - public void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos, + public void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid) { // default no-op } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b4572fda6cca..79a0dfd61e9f 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -311,12 +311,6 @@ cc_library_shared { }, }, - product_variables: { - experimental_mte: { - cflags: ["-DANDROID_EXPERIMENTAL_MTE"], - }, - }, - // Workaround Clang LTO crash. lto: { never: true, diff --git a/core/jni/android_media_AudioErrors.h b/core/jni/android_media_AudioErrors.h index c17a020f74fc..13c9115c1e56 100644 --- a/core/jni/android_media_AudioErrors.h +++ b/core/jni/android_media_AudioErrors.h @@ -35,7 +35,7 @@ enum { AUDIO_JAVA_WOULD_BLOCK = -7, }; -static inline jint nativeToJavaStatus(status_t status) { +static constexpr inline jint nativeToJavaStatus(status_t status) { switch (status) { case NO_ERROR: return AUDIO_JAVA_SUCCESS; diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 7a5c38385f32..065c79b8601f 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -263,18 +263,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we return (jint) AUDIO_JAVA_ERROR; } - // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev. - if (tunerConfiguration != nullptr) { - const TunerConfigurationHelper tunerHelper(env, tunerConfiguration); - ALOGE("Error creating AudioTrack: unsupported tuner contentId:%d syncId:%d", - tunerHelper.getContentId(), tunerHelper.getSyncId()); - return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; - } - // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev. - if (encapsulationMode != 0 /* ENCAPSULATION_MODE_NONE */) { - ALOGE("Error creating AudioTrack: unsupported encapsulationMode %d", encapsulationMode); - return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED; - } + const TunerConfigurationHelper tunerHelper(env, tunerConfiguration); jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); if (nSession == NULL) { @@ -369,6 +358,18 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload } + if (encapsulationMode != 0) { + offloadInfo = AUDIO_INFO_INITIALIZER; + offloadInfo.format = format; + offloadInfo.sample_rate = sampleRateInHertz; + offloadInfo.channel_mask = nativeChannelMask; + offloadInfo.stream_type = AUDIO_STREAM_MUSIC; + offloadInfo.encapsulation_mode = + static_cast<audio_encapsulation_mode_t>(encapsulationMode); + offloadInfo.content_id = tunerHelper.getContentId(); + offloadInfo.sync_id = tunerHelper.getSyncId(); + } + // initialize the native AudioTrack object status_t status = NO_ERROR; switch (memoryMode) { @@ -389,7 +390,8 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we sessionId, // audio session ID offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC, - offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values + (offload || encapsulationMode) ? &offloadInfo : NULL, -1, + -1, // default uid, pid values paa.get()); break; @@ -1364,8 +1366,7 @@ static jint android_media_AudioTrack_setAudioDescriptionMixLeveldB(JNIEnv *env, return (jint)AUDIO_JAVA_ERROR; } - // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level. - return (jint)AUDIO_JAVA_ERROR; + return nativeToJavaStatus(lpTrack->setAudioDescriptionMixLevel(level)); } static jint android_media_AudioTrack_getAudioDescriptionMixLeveldB(JNIEnv *env, jobject thiz, @@ -1381,12 +1382,10 @@ static jint android_media_AudioTrack_getAudioDescriptionMixLeveldB(JNIEnv *env, return (jint)AUDIO_JAVA_ERROR; } - // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level. - // By contract we can return -infinity if unsupported. - *nativeLevel = -std::numeric_limits<float>::infinity(); + status_t status = lpTrack->getAudioDescriptionMixLevel(reinterpret_cast<float *>(nativeLevel)); env->ReleasePrimitiveArrayCritical(level, nativeLevel, 0 /* mode */); - nativeLevel = nullptr; - return (jint)AUDIO_JAVA_SUCCESS; + + return nativeToJavaStatus(status); } static jint android_media_AudioTrack_setDualMonoMode(JNIEnv *env, jobject thiz, jint dualMonoMode) { @@ -1396,8 +1395,8 @@ static jint android_media_AudioTrack_setDualMonoMode(JNIEnv *env, jobject thiz, return (jint)AUDIO_JAVA_ERROR; } - // TODO: replace in r-dev or r-tv-dev with code if HW is able to set audio mix level. - return (jint)AUDIO_JAVA_ERROR; + return nativeToJavaStatus( + lpTrack->setDualMonoMode(static_cast<audio_dual_mono_mode_t>(dualMonoMode))); } static jint android_media_AudioTrack_getDualMonoMode(JNIEnv *env, jobject thiz, @@ -1407,18 +1406,17 @@ static jint android_media_AudioTrack_getDualMonoMode(JNIEnv *env, jobject thiz, ALOGE("%s: AudioTrack not initialized", __func__); return (jint)AUDIO_JAVA_ERROR; } - jfloat *nativeDualMonoMode = (jfloat *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL); + jint *nativeDualMonoMode = (jint *)env->GetPrimitiveArrayCritical(dualMonoMode, NULL); if (nativeDualMonoMode == nullptr) { ALOGE("%s: Cannot retrieve dualMonoMode pointer", __func__); return (jint)AUDIO_JAVA_ERROR; } - // TODO: replace in r-dev or r-tv-dev with code if HW is able to select dual mono mode. - // By contract we can return DUAL_MONO_MODE_OFF if unsupported. - *nativeDualMonoMode = 0; // DUAL_MONO_MODE_OFF for now. + status_t status = lpTrack->getDualMonoMode( + reinterpret_cast<audio_dual_mono_mode_t *>(nativeDualMonoMode)); env->ReleasePrimitiveArrayCritical(dualMonoMode, nativeDualMonoMode, 0 /* mode */); - nativeDualMonoMode = nullptr; - return (jint)AUDIO_JAVA_SUCCESS; + + return nativeToJavaStatus(status); } // ---------------------------------------------------------------------------- diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 2155246cd544..e2af87ee1adf 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -18,6 +18,7 @@ #include <vector> +#include <android/file_descriptor_jni.h> #include <arpa/inet.h> #include <linux/filter.h> #include <linux/if_arp.h> @@ -83,7 +84,7 @@ static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, filter_code, }; - int fd = jniGetFDFromFileDescriptor(env, javaFd); + int fd = AFileDescriptor_getFD(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); @@ -93,7 +94,7 @@ static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) { int optval_ignored = 0; - int fd = jniGetFDFromFileDescriptor(env, javaFd); + int fd = AFileDescriptor_getFD(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", @@ -117,10 +118,9 @@ static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv * return (jboolean) !setNetworkForResolv(netId); } -static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, - jint netId) -{ - return setNetworkForSocket(netId, socket); +static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd, + jint netId) { + return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd)); } static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket) @@ -128,6 +128,10 @@ static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint return (jboolean) !protectFromVpn(socket); } +static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) { + return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd)); +} + static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId) { return (jboolean) !queryUserAccess(uid, netId); @@ -178,7 +182,7 @@ static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint } static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); + int fd = AFileDescriptor_getFD(env, javaFd); int rcode; std::vector<uint8_t> buf(MAXPACKETSIZE, 0); @@ -205,7 +209,7 @@ static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, job } static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) { - int fd = jniGetFDFromFileDescriptor(env, javaFd); + int fd = AFileDescriptor_getFD(env, javaFd); resNetworkCancel(fd); jniSetFileDescriptorOfFD(env, javaFd, -1); } @@ -231,7 +235,7 @@ static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, j return NULL; } - int fd = jniGetFDFromFileDescriptor(env, javaFd); + int fd = AFileDescriptor_getFD(env, javaFd); struct tcp_repair_window trw = {}; socklen_t size = sizeof(trw); @@ -271,8 +275,9 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork }, { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess }, { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution }, - { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork }, - { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn }, + { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork }, + { "protectFromVpn", "(I)Z", (void*) android_net_utils_protectFromVpn }, + { "protectFromVpn", "(Ljava/io/FileDescriptor;)Z", (void*) android_net_utils_protectFromVpnWithFd }, { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess }, { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter }, { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter }, diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp index 0fb29111d043..a9db91be1d5b 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -257,7 +257,17 @@ jobject JHwBlob::NewObject(JNIEnv *env, size_t size) { // XXX Again cannot refer to gFields.constructID because InitClass may // not have been called yet. - return env->NewObject(clazz.get(), constructID, size); + // Cases: + // - this originates from another process (something so large should not fit + // in the binder buffer, and it should be rejected by the binder driver) + // - if this is used in process, this code makes too many heap copies (in + // order to retrofit HIDL's scatter-gather format to java types) to + // justify passing such a large amount of data over this path. So the + // alternative (updating the constructor and other code to accept other + // types, should also probably not be taken in this case). + CHECK_LE(size, std::numeric_limits<jint>::max()); + + return env->NewObject(clazz.get(), constructID, static_cast<jint>(size)); } } // namespace android diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 6381cf59380a..d7001d8d36ea 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -14,15 +14,6 @@ * limitations under the License. */ -/* - * Disable optimization of this file if we are compiling with the address - * sanitizer. This is a mitigation for b/122921367 and can be removed once the - * bug is fixed. - */ -#if __has_feature(address_sanitizer) -#pragma clang optimize off -#endif - #define LOG_TAG "Zygote" #define ATRACE_TAG ATRACE_TAG_DALVIK @@ -79,7 +70,6 @@ #include <bionic/malloc.h> #include <bionic/mte.h> #include <cutils/fs.h> -#include <cutils/memory.h> #include <cutils/multiuser.h> #include <cutils/sockets.h> #include <private/android_filesystem_config.h> @@ -647,13 +637,6 @@ static void PreApplicationInit() { // Set the jemalloc decay time to 1. mallopt(M_DECAY_TIME, 1); - - // Avoid potentially expensive memory mitigations, mostly meant for system - // processes, in apps. These may cause app compat problems, use more memory, - // or reduce performance. While it would be nice to have them for apps, - // we will have to wait until they are proven out, have more efficient - // hardware, and/or apply them only to new applications. - process_disable_memory_mitigations(); } static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) { @@ -1568,7 +1551,6 @@ static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name, jstring managed_nice_name, fail_fn_t fail_fn) { - ensureInAppMountNamespace(fail_fn); std::vector<std::string> merged_data_info_list; insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list, process_name, managed_nice_name, fail_fn); @@ -1715,10 +1697,11 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn); - // System services, isolated process, webview/app zygote, old target sdk app, should - // give a null in same_uid_pkgs and private_volumes so they don't need app data isolation. - // Isolated process / webview / app zygote should be gated by SELinux and file permission - // so they can't even traverse CE / DE directories. + // Make sure app is running in its own mount namespace before isolating its data directories. + ensureInAppMountNamespace(fail_fn); + + // Sandbox data and jit profile directories by overlaying a tmpfs on those dirs and bind + // mount all related packages separately. if (mount_data_dirs) { isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list, uid, process_name, managed_nice_name, fail_fn); @@ -1819,7 +1802,15 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE; break; } - android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level)); + mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level); + + // Avoid heap zero initialization for applications without MTE. Zero init may + // cause app compat problems, use more memory, or reduce performance. While it + // would be nice to have them for apps, we will have to wait until they are + // proven out, have more efficient hardware, and/or apply them only to new + // applications. + mallopt(M_BIONIC_ZERO_INIT, 0); + // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART runtime. runtime_flags &= ~RuntimeFlags::MEMORY_TAG_LEVEL_MASK; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 714a09d02264..a668e8e6dd0f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -309,9 +309,14 @@ <protected-broadcast android:name="android.net.nsd.STATE_CHANGED" /> + <!-- For OMAPI --> + <protected-broadcast android:name="android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED" /> + <protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" /> + <protected-broadcast android:name="android.nfc.action.ALWAYS_ON_STATE_CHANGED" /> <protected-broadcast android:name="android.nfc.action.PREFERRED_PAYMENT_CHANGED" /> <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" /> + <protected-broadcast android:name="android.nfc.action.REQUIRE_UNLOCK_FOR_NFC" /> <protected-broadcast android:name="com.android.nfc.action.LLCP_UP" /> <protected-broadcast android:name="com.android.nfc.action.LLCP_DOWN" /> <protected-broadcast android:name="com.android.nfc.cardemulation.action.CLOSE_TAP_DIALOG" /> @@ -1622,7 +1627,7 @@ <permission android:name="android.permission.MANAGE_IPSEC_TUNNELS" android:protectionLevel="signature|appop" /> - <!-- @hide Allows apps to create and manage Test Networks. + <!-- @SystemApi @hide Allows apps to create and manage Test Networks. <p>Granted only to shell. CTS tests will use UiAutomation.AdoptShellPermissionIdentity() to gain access. --> @@ -1664,6 +1669,12 @@ <permission android:name="android.permission.REQUEST_NETWORK_SCORES" android:protectionLevel="signature|setup" /> + <!-- Allows applications to restart the Wi-Fi subsystem. + @SystemApi + <p>Not for use by third-party applications. @hide --> + <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" + android:protectionLevel="signature|setup" /> + <!-- @SystemApi @hide Allows applications to toggle airplane mode. <p>Not for use by third-party or privileged applications. --> @@ -2979,6 +2990,12 @@ <permission android:name="android.permission.RECOVERY" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to do certain operations needed for + resume on reboot feature. + @hide --> + <permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to read system update info. @hide --> <permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO" @@ -4490,7 +4507,7 @@ <!-- Allows access to keyguard secure storage. Only allowed for system processes. @hide --> <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" - android:protectionLevel="signature" /> + android:protectionLevel="signature|setup" /> <!-- Allows applications to set the initial lockscreen state. <p>Not for use by third-party applications. @hide --> diff --git a/core/res/OWNERS b/core/res/OWNERS index 02cf0b71ff69..a30111b44382 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -1,7 +1,9 @@ adamp@google.com alanv@google.com +asc@google.com dsandler@android.com dsandler@google.com +dupin@google.com hackbod@android.com hackbod@google.com jsharkey@android.com diff --git a/core/res/assets/images/progress_font.png b/core/res/assets/images/progress_font.png Binary files differnew file mode 100644 index 000000000000..78c3ed9cd699 --- /dev/null +++ b/core/res/assets/images/progress_font.png diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 2f1bcdc76afb..4b3d82a04b8b 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8968,6 +8968,11 @@ changed at runtime by calling {@link android.media.tv.TvInputManager#updateTvInputInfo(android.media.tv.TvInputInfo)}. --> <attr name="tunerCount" format="integer" /> + <!-- Attribute whether the TV input service can pause recording programs. + This value can be changed at runtime by calling + {@link android.media.tv.TvInputManager#updateTvInputInfo(android.media.tv.TvInputInfo)} + . --> + <attr name="canPauseRecording" format="boolean" /> </declare-styleable> <!-- Attributes that can be used with <code>rating-system-definition</code> tags inside of the diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 42a658e50073..20cb27085661 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -455,6 +455,10 @@ --> </string-array> + <!-- Whether the internal vehicle network should remain active even when no + apps requested it. --> + <bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool> + <!-- Configuration of network interfaces that support WakeOnLAN --> <string-array translatable="false" name="config_wakeonlan_supported_interfaces"> <!-- @@ -4407,4 +4411,7 @@ <!-- Component names of the services which will keep critical code path warm --> <string-array name="config_keep_warming_services" translatable="false" /> + + <!-- Whether to select voice/data/sms preference without user confirmation --> + <bool name="config_voice_data_sms_auto_fallback">false</bool> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e00aff1af37b..a0be0681bd38 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3043,6 +3043,7 @@ =============================================================== --> <public-group type="attr" first-id="0x01010617"> + <public name="canPauseRecording" /> <!-- attribute definitions go here --> </public-group> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 1143467425af..8ac00dceea9e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -110,7 +110,7 @@ <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> <string name="ClipMmi">Incoming Caller ID</string> <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> - <string name="ClirMmi">Outgoing Caller ID</string> + <string name="ClirMmi">Hide Outgoing Caller ID</string> <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. --> <string name="ColpMmi">Connected Line ID</string> <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c505afe0509e..4509b4e0b3f9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -684,6 +684,7 @@ <java-symbol type="string" name="config_ethernet_iface_regex" /> <java-symbol type="string" name="not_checked" /> <java-symbol type="array" name="config_ethernet_interfaces" /> + <java-symbol type="bool" name="config_vehicleInternalNetworkAlwaysRequested" /> <java-symbol type="array" name="config_wakeonlan_supported_interfaces" /> <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" /> <java-symbol type="string" name="config_mms_user_agent" /> @@ -4056,4 +4057,6 @@ <java-symbol type="array" name="config_notificationMsgPkgsAllowedAsConvos" /> <java-symbol type="array" name="config_keep_warming_services" /> + + <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" /> </resources> diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop index 1bcc773a9a5d..93e8b788aed9 100644 --- a/core/sysprop/WatchdogProperties.sysprop +++ b/core/sysprop/WatchdogProperties.sysprop @@ -16,7 +16,7 @@ module: "android.sysprop.WatchdogProperties" owner: Platform # To escape the watchdog timeout loop, fatal reboot the system when -# watchdog timed out 'fatal_count' times in 'fatal_window_second' +# watchdog timed out 'fatal_count' times in 'fatal_window_seconds' # seconds, if both values are not 0. Default value of both is 0. prop { api_name: "fatal_count" @@ -26,8 +26,9 @@ prop { access: Readonly } +# See 'fatal_count' for documentation. prop { - api_name: "fatal_window_second" + api_name: "fatal_window_seconds" type: Integer prop_name: "framework_watchdog.fatal_window.second" scope: Internal @@ -35,9 +36,9 @@ prop { } # The fatal counting can be disabled by setting property -# 'is_fatal_ignore' to true. +# 'should_ignore_fatal_count' to true. prop { - api_name: "is_fatal_ignore" + api_name: "should_ignore_fatal_count" type: Boolean prop_name: "persist.debug.framework_watchdog.fatal_ignore" scope: Internal diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt index d901aef945c9..c8462111fa94 100644 --- a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt +++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt @@ -7,13 +7,13 @@ props { prop_name: "framework_watchdog.fatal_count" } prop { - api_name: "fatal_window_second" + api_name: "fatal_window_seconds" type: Integer scope: Internal prop_name: "framework_watchdog.fatal_window.second" } prop { - api_name: "is_fatal_ignore" + api_name: "should_ignore_fatal_count" scope: Internal prop_name: "persist.debug.framework_watchdog.fatal_ignore" } diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java index f9e3bc60561c..09f16a82cd7f 100644 --- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java +++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java @@ -23,7 +23,10 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.Manifest; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.BugreportManager; import android.os.BugreportManager.BugreportCallback; import android.os.BugreportParams; @@ -31,7 +34,9 @@ import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.StrictMode; +import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; @@ -53,10 +58,11 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.File; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; - /** * Tests for BugreportManager API. */ @@ -67,8 +73,22 @@ public class BugreportManagerTest { private static final String TAG = "BugreportManagerTest"; private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10); + private static final long DUMPSTATE_STARTUP_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); + + // A small timeout used when waiting for the result of a BugreportCallback to be received. + // This value must be at least 1000ms since there is an intentional delay in + // BugreportManagerServiceImpl in the error case. + private static final long CALLBACK_RESULT_TIMEOUT_MS = 1500; + + // Sent by Shell when its bugreport finishes (contains final bugreport/screenshot file name + // associated with the bugreport). + private static final String INTENT_BUGREPORT_FINISHED = + "com.android.internal.intent.action.BUGREPORT_FINISHED"; + private static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT"; + private static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT"; + private Handler mHandler; private Executor mExecutor; private BugreportManager mBrm; @@ -171,7 +191,7 @@ public class BugreportManagerTest { ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2); ParcelFileDescriptor screenshotFd2 = parcelFd(screenshotFile2); mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2); - Thread.sleep(500 /* .5s */); + Thread.sleep(CALLBACK_RESULT_TIMEOUT_MS); // Verify #2 encounters an error. assertThat(callback2.getErrorCode()).isEqualTo( @@ -180,7 +200,7 @@ public class BugreportManagerTest { // Cancel #1 so we can move on to the next test. mBrm.cancelBugreport(); - Thread.sleep(500 /* .5s */); + waitTillDoneOrTimeout(callback); assertThat(callback.isDone()).isTrue(); assertFdsAreClosed(mBugreportFd, mScreenshotFd); } @@ -206,12 +226,54 @@ public class BugreportManagerTest { // Try again, with DUMP permission. getPermissions(); mBrm.cancelBugreport(); - Thread.sleep(500 /* .5s */); + waitTillDoneOrTimeout(callback); assertThat(callback.isDone()).isTrue(); assertFdsAreClosed(mBugreportFd, mScreenshotFd); } @Test + public void cancelBugreport_noReportStarted() throws Exception { + // Without the native DumpstateService running, we don't get a SecurityException. + mBrm.cancelBugreport(); + } + + @LargeTest + @Test + public void cancelBugreport_fromDifferentUid() throws Exception { + assertThat(Process.myUid()).isNotEqualTo(Process.SHELL_UID); + + // Start a bugreport through ActivityManager's shell command - this starts a BR from the + // shell UID rather than our own. + BugreportBroadcastReceiver br = new BugreportBroadcastReceiver(); + InstrumentationRegistry.getContext() + .registerReceiver(br, new IntentFilter(INTENT_BUGREPORT_FINISHED)); + UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + .executeShellCommand("am bug-report"); + + // The command triggers the report through a broadcast, so wait until dumpstate actually + // starts up, which may take a bit. + waitTillDumpstateRunningOrTimeout(); + + try { + mBrm.cancelBugreport(); + fail("Expected cancelBugreport to throw SecurityException when report started by " + + "different UID"); + } catch (SecurityException expected) { + } finally { + // Do this in the finally block so that even if this test case fails, we don't break + // other test cases unexpectedly due to the still-running shell report. + try { + // The shell's BR is still running and should complete successfully. + br.waitForBugreportFinished(); + } finally { + // The latch may fail for a number of reasons but we still need to unregister the + // BroadcastReceiver. + InstrumentationRegistry.getContext().unregisterReceiver(br); + } + } + } + + @Test public void insufficientPermissions_throwsException() throws Exception { dropPermissions(); @@ -346,6 +408,28 @@ public class BugreportManagerTest { .adoptShellPermissionIdentity(Manifest.permission.DUMP); } + private static boolean isDumpstateRunning() { + String[] output; + try { + output = + UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + .executeShellCommand("ps -A -o NAME | grep dumpstate") + .trim() + .split("\n"); + } catch (IOException e) { + Log.w(TAG, "Failed to check if dumpstate is running", e); + return false; + } + for (String line : output) { + // Check for an exact match since there may be other things that contain "dumpstate" as + // a substring (e.g. the dumpstate HAL). + if (TextUtils.equals("dumpstate", line)) { + return true; + } + } + return false; + } + private static void assertFdIsClosed(ParcelFileDescriptor pfd) { try { int fd = pfd.getFd(); @@ -364,18 +448,25 @@ public class BugreportManagerTest { return System.currentTimeMillis(); } - private static boolean shouldTimeout(long startTimeMs) { - return now() - startTimeMs >= BUGREPORT_TIMEOUT_MS; + private static void waitTillDumpstateRunningOrTimeout() throws Exception { + long startTimeMs = now(); + while (!isDumpstateRunning()) { + Thread.sleep(500 /* .5s */); + if (now() - startTimeMs >= DUMPSTATE_STARTUP_TIMEOUT_MS) { + break; + } + Log.d(TAG, "Waited " + (now() - startTimeMs) + "ms for dumpstate to start"); + } } private static void waitTillDoneOrTimeout(BugreportCallbackImpl callback) throws Exception { long startTimeMs = now(); while (!callback.isDone()) { Thread.sleep(1000 /* 1s */); - if (shouldTimeout(startTimeMs)) { + if (now() - startTimeMs >= BUGREPORT_TIMEOUT_MS) { break; } - Log.d(TAG, "Waited " + (now() - startTimeMs) + "ms"); + Log.d(TAG, "Waited " + (now() - startTimeMs) + "ms for bugreport to finish"); } } @@ -450,6 +541,36 @@ public class BugreportManagerTest { assertTrue(device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS)); } + private class BugreportBroadcastReceiver extends BroadcastReceiver { + Intent mBugreportFinishedIntent = null; + final CountDownLatch mLatch; + + BugreportBroadcastReceiver() { + mLatch = new CountDownLatch(1); + } + + @Override + public void onReceive(Context context, Intent intent) { + setBugreportFinishedIntent(intent); + mLatch.countDown(); + } + + private void setBugreportFinishedIntent(Intent intent) { + mBugreportFinishedIntent = intent; + } + + public Intent getBugreportFinishedIntent() { + return mBugreportFinishedIntent; + } + + public void waitForBugreportFinished() throws Exception { + if (!mLatch.await(BUGREPORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + throw new Exception("Failed to receive BUGREPORT_FINISHED in " + + BUGREPORT_TIMEOUT_MS + " ms."); + } + } + } + /** * A rule to change strict mode vm policy temporarily till test method finished. * diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS index bd7da0c3f209..b3f399363aef 100644 --- a/core/tests/coretests/src/android/app/OWNERS +++ b/core/tests/coretests/src/android/app/OWNERS @@ -1 +1,6 @@ per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS + +# Notification, DND, Status bar +per-file *Notification* = file:/packages/SystemUI/OWNERS +per-file *Zen* = file:/packages/SystemUI/OWNERS +per-file *StatusBar* = file:/packages/SystemUI/OWNERS diff --git a/core/tests/coretests/src/android/app/people/OWNERS b/core/tests/coretests/src/android/app/people/OWNERS new file mode 100644 index 000000000000..6ec8e6aa8d81 --- /dev/null +++ b/core/tests/coretests/src/android/app/people/OWNERS @@ -0,0 +1 @@ +file:/core/java/android/app/people/OWNERS
\ No newline at end of file diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS index 912db1e835dc..c61a4b538a44 100644 --- a/core/tests/coretests/src/android/content/OWNERS +++ b/core/tests/coretests/src/android/content/OWNERS @@ -1,3 +1,4 @@ +per-file AssetTest.java = file:/core/java/android/content/res/OWNERS per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS -per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS +per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS diff --git a/core/tests/coretests/src/android/content/integrity/OWNERS b/core/tests/coretests/src/android/content/integrity/OWNERS new file mode 100644 index 000000000000..4ffc7041a527 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/OWNERS @@ -0,0 +1 @@ +include /core/java/android/content/integrity/OWNERS diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS new file mode 100644 index 000000000000..867336515ce3 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/OWNERS @@ -0,0 +1,5 @@ +include /core/java/android/content/pm/OWNERS + +per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS +per-file SigningDetailsTest.java = cbrubaker@google.com +per-file SigningDetailsTest.java = mpgroover@google.com diff --git a/core/tests/coretests/src/android/content/res/OWNERS b/core/tests/coretests/src/android/content/res/OWNERS new file mode 100644 index 000000000000..3e79d8ff0bbe --- /dev/null +++ b/core/tests/coretests/src/android/content/res/OWNERS @@ -0,0 +1 @@ +include /core/java/android/content/res/OWNERS diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java index 0f17d27048f3..6be9306bbd2d 100644 --- a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java +++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java @@ -254,7 +254,7 @@ public class DateIntervalFormatTest { assertEquals("19–22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); - assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009", + assertEquals("lun, 19 de ene. – jue, 22 de ene. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", @@ -265,7 +265,7 @@ public class DateIntervalFormatTest { assertEquals("19 de ene. – 22 de abr. 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); - assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009", + assertEquals("lun, 19 de ene. – mié, 22 de abr. de 2009", formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); assertEquals("enero–abril de 2009", @@ -286,9 +286,9 @@ public class DateIntervalFormatTest { assertEquals("19–22 de enero de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0)); - assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, + assertEquals("19–22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); - assertEquals("lun., 19 ene. – jue., 22 ene. 2009", + assertEquals("lun, 19 ene – jue, 22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009", @@ -296,19 +296,19 @@ public class DateIntervalFormatTest { assertEquals("19 de enero–22 de abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0)); - assertEquals("19 ene. – 22 abr. 2009", + assertEquals("19 ene – 22 abr 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); - assertEquals("lun., 19 ene. – mié., 22 abr. 2009", + assertEquals("lun, 19 ene – mié, 22 abr 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL)); assertEquals("enero–abril de 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY)); - assertEquals("19 ene. 2009 – 9 feb. 2012", + assertEquals("19 ene 2009 – 9 feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL)); - assertEquals("ene. 2009 – feb. 2012", + assertEquals("ene 2009 – feb 2012", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR, FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL)); assertEquals("19 de enero de 2009–9 de febrero de 2012", diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java index 068d04798858..5612833e5ddd 100644 --- a/core/tests/coretests/src/android/text/format/FormatterTest.java +++ b/core/tests/coretests/src/android/text/format/FormatterTest.java @@ -212,7 +212,7 @@ public class FormatterTest { // Make sure it works on different locales. setLocale(new Locale("ru", "RU")); - assertEquals("1 мин.", Formatter.formatShortElapsedTimeRoundingUpToMinutes( + assertEquals("1 мин", Formatter.formatShortElapsedTimeRoundingUpToMinutes( mContext, 1 * SECOND)); } diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java index 4b3b5735b4f3..b3425162f48f 100644 --- a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java +++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java @@ -755,8 +755,8 @@ public class RelativeDateTimeFormatterTest { final Locale locale = new Locale("fr"); android.icu.text.RelativeDateTimeFormatter icuFormatter = android.icu.text.RelativeDateTimeFormatter.getInstance(locale); - assertEquals("D à T", icuFormatter.combineDateAndTime("D", "T")); + assertEquals("D, T", icuFormatter.combineDateAndTime("D", "T")); // Ensure single quote ' and curly braces {} are not interpreted in input values. - assertEquals("D'x' à T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}")); + assertEquals("D'x', T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}")); } } diff --git a/core/tests/coretests/src/android/view/autofill/OWNERS b/core/tests/coretests/src/android/view/autofill/OWNERS new file mode 100644 index 000000000000..9a30e826a24f --- /dev/null +++ b/core/tests/coretests/src/android/view/autofill/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 351486 + +include /core/java/android/view/autofill/OWNERS diff --git a/core/tests/coretests/src/android/view/contentcapture/OWNERS b/core/tests/coretests/src/android/view/contentcapture/OWNERS new file mode 100644 index 000000000000..24561c59bba6 --- /dev/null +++ b/core/tests/coretests/src/android/view/contentcapture/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 544200 + +include /core/java/android/view/contentcapture/OWNERS diff --git a/core/tests/coretests/src/android/view/textclassifier/OWNERS b/core/tests/coretests/src/android/view/textclassifier/OWNERS new file mode 100644 index 000000000000..46b3cb8824a0 --- /dev/null +++ b/core/tests/coretests/src/android/view/textclassifier/OWNERS @@ -0,0 +1 @@ +include /core/java/android/service/textclassifier/OWNERS diff --git a/core/tests/coretests/src/android/view/textservice/OWNERS b/core/tests/coretests/src/android/view/textservice/OWNERS new file mode 100644 index 000000000000..0471e29a25cd --- /dev/null +++ b/core/tests/coretests/src/android/view/textservice/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 816455 + +include /services/core/java/com/android/server/textservices/OWNERS diff --git a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java index aec60963c389..ccd873dc390e 100644 --- a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java +++ b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java @@ -30,7 +30,6 @@ import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RectShape; import android.platform.test.annotations.Presubmit; -import android.view.View; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -48,6 +47,7 @@ import java.util.List; @Presubmit public class AbsSeekBarTest { + public static final int PADDING = 10; private Context mContext; private AbsSeekBar mBar; @@ -59,48 +59,56 @@ public class AbsSeekBarTest { @Test public void testExclusionForThumb_limitedTo48dp() { - mBar.setPadding(10, 10, 10, 10); - mBar.setThumb(newThumb(dpToPx(20))); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); + mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); mBar.setProgress(50); - measureAndLayout(dpToPx(200), dpToPx(100)); + + final int thumbOffset = mBar.getThumbOffset(); + + measureAndLayout(dpToPxSize(200), dpToPxSize(100)); List<Rect> exclusions = mBar.getSystemGestureExclusionRects(); assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size()); assertEquals("exclusion should be centered on thumb", - center(mBar), center(exclusions.get(0))); - assertEquals("exclusion should be 48dp high", dpToPx(48), exclusions.get(0).height()); - assertEquals("exclusion should be 48dp wide", dpToPx(48), exclusions.get(0).width()); + center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)), + center(exclusions.get(0))); + assertEquals("exclusion should be 48dp high", dpToPxSize(48), exclusions.get(0).height()); + assertEquals("exclusion should be 48dp wide", dpToPxSize(48), exclusions.get(0).width()); } @Test public void testExclusionForThumb_limitedToHeight() { - mBar.setPadding(10, 10, 10, 10); - mBar.setThumb(newThumb(dpToPx(20))); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); + mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); mBar.setProgress(50); - measureAndLayout(dpToPx(200), dpToPx(32)); + + final int thumbOffset = mBar.getThumbOffset(); + + measureAndLayout(dpToPxSize(200), dpToPxSize(32)); List<Rect> exclusions = mBar.getSystemGestureExclusionRects(); assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size()); assertEquals("exclusion should be centered on thumb", - center(mBar), center(exclusions.get(0))); - assertEquals("exclusion should be 32dp high", dpToPx(32), exclusions.get(0).height()); - assertEquals("exclusion should be 32dp wide", dpToPx(32), exclusions.get(0).width()); + center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)), + center(exclusions.get(0))); + assertEquals("exclusion should be 32dp high", dpToPxSize(32), exclusions.get(0).height()); + assertEquals("exclusion should be 32dp wide", dpToPxSize(32), exclusions.get(0).width()); } @Test public void testExclusionForThumb_passesThroughUserExclusions() { mBar.setSystemGestureExclusionRects(Arrays.asList(new Rect(1, 2, 3, 4))); - mBar.setPadding(10, 10, 10, 10); - mBar.setThumb(newThumb(dpToPx(20))); + mBar.setPadding(PADDING, PADDING, PADDING, PADDING); + mBar.setThumb(newThumb(dpToPxSize(20))); mBar.setMin(0); mBar.setMax(100); mBar.setProgress(50); - measureAndLayout(dpToPx(200), dpToPx(32)); + measureAndLayout(dpToPxSize(200), dpToPxSize(32)); assertThat(mBar.getSystemGestureExclusionRects(), hasItem(new Rect(1, 2, 3, 4))); assertThat(mBar.getSystemGestureExclusionRects(), hasSize(2)); @@ -110,12 +118,37 @@ public class AbsSeekBarTest { assertThat(mBar.getSystemGestureExclusionRects(), hasSize(2)); } + @Test + public void testGrowRectTo_evenInitialDifference() { + doGrowRectTest(new Rect(0, 0, 0, 0), 10, new Rect(-5, -5, 5, 5)); + } + + @Test + public void testGrowRectTo_unevenInitialDifference() { + doGrowRectTest(new Rect(0, 0, 1, 1), 10, new Rect(-5, -5, 5, 5)); + } + + @Test + public void testGrowRectTo_unevenInitialDifference_unevenSize() { + doGrowRectTest(new Rect(0, 0, 0, 0), 9, new Rect(-5, -5, 4, 4)); + } + + public void doGrowRectTest(Rect in, int minimumSize, Rect expected) { + Rect result = new Rect(in); + mBar.growRectTo(result, minimumSize); + + assertEquals("grown rect", expected, result); + assertEquals("grown rect center point", center(expected), center(result)); + } + private Point center(Rect rect) { return new Point(rect.centerX(), rect.centerY()); } - private Point center(View view) { - return center(new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); + private Rect offset(Rect rect, int dx, int dy) { + Rect result = new Rect(rect); + result.offset(dx, dy); + return result; } private ShapeDrawable newThumb(int size) { @@ -130,7 +163,7 @@ public class AbsSeekBarTest { mBar.layout(0, 0, wPx, hPx); } - private int dpToPx(int dp) { - return (int) (mContext.getResources().getDisplayMetrics().density * dp); + private int dpToPxSize(int dp) { + return (int) (mContext.getResources().getDisplayMetrics().density * dp + 0.5f); } } diff --git a/core/tests/coretests/src/com/android/internal/app/OWNERS b/core/tests/coretests/src/com/android/internal/app/OWNERS new file mode 100644 index 000000000000..6888be321476 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/app/OWNERS @@ -0,0 +1 @@ +include /core/java/com/android/internal/app/OWNERS diff --git a/core/tests/mockingcoretests/src/android/view/OWNERS b/core/tests/mockingcoretests/src/android/view/OWNERS new file mode 100644 index 000000000000..9c9f824ba12b --- /dev/null +++ b/core/tests/mockingcoretests/src/android/view/OWNERS @@ -0,0 +1,2 @@ +# Display +per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml index db45750ff6dc..ebbdda559ed2 100644 --- a/core/tests/overlaytests/device/AndroidTest.xml +++ b/core/tests/overlaytests/device/AndroidTest.xml @@ -24,7 +24,7 @@ <option name="remount-system" value="true" /> <option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" /> </target_preparer> - + <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. --> <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer"> <option name="pre-reboot" value="true" /> diff --git a/core/tests/overlaytests/device/TEST_MAPPING b/core/tests/overlaytests/device/TEST_MAPPING deleted file mode 100644 index 43ee00f47e84..000000000000 --- a/core/tests/overlaytests/device/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit": [ - { - "name" : "OverlayDeviceTests" - } - ] -} diff --git a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java index 4c3eaeb1730b..7175f562d7ef 100644 --- a/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java @@ -15,6 +15,8 @@ */ package com.android.internal.util; +import static android.Manifest.permission.NETWORK_SETTINGS; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -82,6 +84,7 @@ public class LocationPermissionCheckerTest { private int mAllowCoarseLocationApps; private int mFineLocationPermission; private int mAllowFineLocationApps; + private int mNetworkSettingsPermission; private int mCurrentUser; private boolean mIsLocationEnabled; private boolean mThrowSecurityException; @@ -138,6 +141,7 @@ public class LocationPermissionCheckerTest { mFineLocationPermission = PackageManager.PERMISSION_DENIED; mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED; mAllowFineLocationApps = AppOpsManager.MODE_ERRORED; + mNetworkSettingsPermission = PackageManager.PERMISSION_DENIED; } private void setupMockInterface() { @@ -151,6 +155,8 @@ public class LocationPermissionCheckerTest { .thenReturn(mCoarseLocationPermission); when(mMockContext.checkPermission(mManifestStringFine, -1, mUid)) .thenReturn(mFineLocationPermission); + when(mMockContext.checkPermission(NETWORK_SETTINGS, -1, mUid)) + .thenReturn(mNetworkSettingsPermission); when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled); } @@ -264,6 +270,21 @@ public class LocationPermissionCheckerTest { assertEquals(LocationPermissionChecker.ERROR_LOCATION_MODE_OFF, result); } + @Test + public void testenforceCanAccessScanResults_LocationModeDisabledHasNetworkSettings() + throws Exception { + mThrowSecurityException = false; + mIsLocationEnabled = false; + mNetworkSettingsPermission = PackageManager.PERMISSION_GRANTED; + setupTestCase(); + + final int result = + mChecker.checkLocationPermissionWithDetailInfo( + TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); + assertEquals(LocationPermissionChecker.SUCCEEDED, result); + } + + private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { try { r.run(); diff --git a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java index e0884e3e1c28..9394dec7f46f 100644 --- a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java +++ b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java @@ -42,14 +42,14 @@ public class AngleOfArrivalMeasurementTest { AngleOfArrivalMeasurement.Builder builder = new AngleOfArrivalMeasurement.Builder(); tryBuild(builder, false); - builder.setAltitudeAngleMeasurement(altitude); + builder.setAltitude(altitude); tryBuild(builder, false); - builder.setAzimuthAngleMeasurement(azimuth); + builder.setAzimuth(azimuth); AngleOfArrivalMeasurement measurement = tryBuild(builder, true); - assertEquals(azimuth, measurement.getAzimuthAngleMeasurement()); - assertEquals(altitude, measurement.getAltitudeAngleMeasurement()); + assertEquals(azimuth, measurement.getAzimuth()); + assertEquals(altitude, measurement.getAltitude()); } private AngleMeasurement getAngleMeasurement(double radian, double error, double confidence) { diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java index b4b2e303443e..8e7f7c562ade 100644 --- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java +++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java @@ -42,8 +42,8 @@ public class UwbTestUtils { public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() { return new AngleOfArrivalMeasurement.Builder() - .setAltitudeAngleMeasurement(getAngleMeasurement()) - .setAzimuthAngleMeasurement(getAngleMeasurement()) + .setAltitude(getAngleMeasurement()) + .setAzimuth(getAngleMeasurement()) .build(); } diff --git a/data/etc/OWNERS b/data/etc/OWNERS index 5efd0bd06b74..65d3a012b129 100644 --- a/data/etc/OWNERS +++ b/data/etc/OWNERS @@ -1,3 +1,4 @@ +alanstokes@google.com cbrubaker@google.com hackbod@android.com hackbod@google.com @@ -11,3 +12,5 @@ svetoslavganov@google.com toddke@android.com toddke@google.com yamasani@google.com + +per-file preinstalled-packages* = file:/MULTIUSER_OWNERS diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index a185da19e71b..a664ee3a6d19 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -344,7 +344,7 @@ applications that come with the platform <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <permission name="android.permission.MOVE_PACKAGE"/> <!-- Needed for test only --> - <permission name="android.permission.NETWORK_AIRPLANE_MODE"/> + <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/> <permission name="android.permission.OBSERVE_APP_USAGE"/> <permission name="android.permission.NETWORK_SCAN"/> <permission name="android.permission.PACKAGE_USAGE_STATS" /> @@ -443,6 +443,9 @@ applications that come with the platform <!-- Permission needed for CTS test - WifiManagerTest --> <permission name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" /> <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> + <!-- Permission required for GTS test - GtsAssistIntentTestCases --> + <permission name="android.permission.MANAGE_SOUND_TRIGGER" /> + <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java index 7c0af6def696..6398cee74cba 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java @@ -37,6 +37,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.time.Instant; import java.util.Collection; import java.util.LinkedList; import java.util.Map; @@ -237,12 +238,18 @@ class CredstoreIdentityCredential extends IdentityCredential { } private boolean mAllowUsingExhaustedKeys = true; + private boolean mAllowUsingExpiredKeys = false; @Override public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) { mAllowUsingExhaustedKeys = allowUsingExhaustedKeys; } + @Override + public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) { + mAllowUsingExpiredKeys = allowUsingExpiredKeys; + } + private boolean mOperationHandleSet = false; private long mOperationHandle = 0; @@ -256,7 +263,8 @@ class CredstoreIdentityCredential extends IdentityCredential { public long getCredstoreOperationHandle() { if (!mOperationHandleSet) { try { - mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys); + mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys, + mAllowUsingExpiredKeys); mOperationHandleSet = true; } catch (android.os.RemoteException e) { throw new RuntimeException("Unexpected RemoteException ", e); @@ -306,7 +314,8 @@ class CredstoreIdentityCredential extends IdentityCredential { rnsParcels, sessionTranscript != null ? sessionTranscript : new byte[0], readerSignature != null ? readerSignature : new byte[0], - mAllowUsingExhaustedKeys); + mAllowUsingExhaustedKeys, + mAllowUsingExpiredKeys); } catch (android.os.RemoteException e) { throw new RuntimeException("Unexpected RemoteException ", e); } catch (android.os.ServiceSpecificException e) { @@ -410,6 +419,34 @@ class CredstoreIdentityCredential extends IdentityCredential { } @Override + public void storeStaticAuthenticationData(X509Certificate authenticationKey, + Instant expirationDate, + byte[] staticAuthData) + throws UnknownAuthenticationKeyException { + try { + AuthKeyParcel authKeyParcel = new AuthKeyParcel(); + authKeyParcel.x509cert = authenticationKey.getEncoded(); + long millisSinceEpoch = (expirationDate.getEpochSecond() * 1000) + + (expirationDate.getNano() / 1000000); + mBinder.storeStaticAuthenticationDataWithExpiration(authKeyParcel, + millisSinceEpoch, staticAuthData); + } catch (CertificateEncodingException e) { + throw new RuntimeException("Error encoding authenticationKey", e); + } catch (android.os.RemoteException e) { + throw new RuntimeException("Unexpected RemoteException ", e); + } catch (android.os.ServiceSpecificException e) { + if (e.errorCode == ICredentialStore.ERROR_NOT_SUPPORTED) { + throw new UnsupportedOperationException("Not supported", e); + } else if (e.errorCode == ICredentialStore.ERROR_AUTHENTICATION_KEY_NOT_FOUND) { + throw new UnknownAuthenticationKeyException(e.getMessage(), e); + } else { + throw new RuntimeException("Unexpected ServiceSpecificException with code " + + e.errorCode, e); + } + } + } + + @Override public @NonNull int[] getAuthenticationDataUsageCount() { try { int[] usageCount = mBinder.getAuthenticationDataUsageCount(); @@ -421,4 +458,49 @@ class CredstoreIdentityCredential extends IdentityCredential { + e.errorCode, e); } } + + @Override + public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) { + try { + byte[] proofOfOwnership = mBinder.proveOwnership(challenge); + return proofOfOwnership; + } catch (android.os.RemoteException e) { + throw new RuntimeException("Unexpected RemoteException ", e); + } catch (android.os.ServiceSpecificException e) { + if (e.errorCode == ICredentialStore.ERROR_NOT_SUPPORTED) { + throw new UnsupportedOperationException("Not supported", e); + } else { + throw new RuntimeException("Unexpected ServiceSpecificException with code " + + e.errorCode, e); + } + } + } + + @Override + public @NonNull byte[] delete(@NonNull byte[] challenge) { + try { + byte[] proofOfDeletion = mBinder.deleteWithChallenge(challenge); + return proofOfDeletion; + } catch (android.os.RemoteException e) { + throw new RuntimeException("Unexpected RemoteException ", e); + } catch (android.os.ServiceSpecificException e) { + throw new RuntimeException("Unexpected ServiceSpecificException with code " + + e.errorCode, e); + } + } + + @Override + public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) { + try { + IWritableCredential binder = mBinder.update(); + byte[] proofOfProvision = + CredstoreWritableIdentityCredential.personalize(binder, personalizationData); + return proofOfProvision; + } catch (android.os.RemoteException e) { + throw new RuntimeException("Unexpected RemoteException ", e); + } catch (android.os.ServiceSpecificException e) { + throw new RuntimeException("Unexpected ServiceSpecificException with code " + + e.errorCode, e); + } + } } diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java index 129063361b35..d8d47424e2e8 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java @@ -162,5 +162,4 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore { + e.errorCode, e); } } - } diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java index 725e3d8e429a..d2e7984ce19f 100644 --- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java @@ -76,7 +76,14 @@ class CredstoreWritableIdentityCredential extends WritableIdentityCredential { @NonNull @Override public byte[] personalize(@NonNull PersonalizationData personalizationData) { + return personalize(mBinder, personalizationData); + } + // Used by both personalize() and CredstoreIdentityCredential.update(). + // + @NonNull + static byte[] personalize(IWritableCredential binder, + @NonNull PersonalizationData personalizationData) { Collection<AccessControlProfile> accessControlProfiles = personalizationData.getAccessControlProfiles(); @@ -144,7 +151,7 @@ class CredstoreWritableIdentityCredential extends WritableIdentityCredential { secureUserId = getRootSid(); } try { - byte[] personalizationReceipt = mBinder.personalize(acpParcels, ensParcels, + byte[] personalizationReceipt = binder.personalize(acpParcels, ensParcels, secureUserId); return personalizationReceipt; } catch (android.os.RemoteException e) { @@ -164,5 +171,4 @@ class CredstoreWritableIdentityCredential extends WritableIdentityCredential { return rootSid; } - } diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java index 4eb6e420c07f..8f175bb63edb 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -23,6 +23,7 @@ import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.PublicKey; import java.security.cert.X509Certificate; +import java.time.Instant; import java.util.Collection; import java.util.Map; @@ -114,6 +115,25 @@ public abstract class IdentityCredential { public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys); /** + * Sets whether to allow using an authentication key which has been expired if no + * other key is available. This must be called prior to calling + * {@link #getEntries(byte[], Map, byte[], byte[])}. + * + * <p>By default this is set to false. + * + * <p>This is only implemented in feature version 202101 or later. If not implemented, the call + * fails with {@link UnsupportedOperationException}. See + * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known + * feature versions. + * + * @param allowUsingExpiredKeys whether to allow using an authentication key which use count + * has been exceeded if no other key is available. + */ + public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) { + throw new UnsupportedOperationException(); + } + + /** * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an * operation handle. * @@ -289,6 +309,21 @@ public abstract class IdentityCredential { * * <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey * can be obtained using the {@link #getCredentialKeyCertificateChain()} method. + + * <p>If the implementation is feature version 202101 or later, + * each X.509 certificate contains an X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26 which + * contains a DER encoded OCTET STRING with the bytes of the CBOR with the following CDDL: + * <pre> + * ProofOfBinding = [ + * "ProofOfBinding", + * bstr, // Contains SHA-256(ProofOfProvisioning) + * ] + * </pre> + * <p>This CBOR enables an issuer to determine the exact state of the credential it + * returns issuer-signed data for. + * + * <p> See {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for + * known feature versions. * * @return A collection of X.509 certificates for dynamic authentication keys that need issuer * certification. @@ -308,16 +343,136 @@ public abstract class IdentityCredential { * the authenticity * and integrity of the credential data fields. * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized. + * @deprecated Use {@link #storeStaticAuthenticationData(X509Certificate, Instant, byte[])} + * instead. */ + @Deprecated public abstract void storeStaticAuthenticationData( @NonNull X509Certificate authenticationKey, @NonNull byte[] staticAuthData) throws UnknownAuthenticationKeyException; /** + * Store authentication data associated with a dynamic authentication key. + * + * This should only be called for an authenticated key returned by + * {@link #getAuthKeysNeedingCertification()}. + * + * <p>This is only implemented in feature version 202101 or later. If not implemented, the call + * fails with {@link UnsupportedOperationException}. See + * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known + * feature versions. + * + * @param authenticationKey The dynamic authentication key for which certification and + * associated static + * authentication data is being provided. + * @param expirationDate The expiration date of the static authentication data. + * @param staticAuthData Static authentication data provided by the issuer that validates + * the authenticity + * and integrity of the credential data fields. + * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized. + */ + public void storeStaticAuthenticationData( + @NonNull X509Certificate authenticationKey, + @NonNull Instant expirationDate, + @NonNull byte[] staticAuthData) + throws UnknownAuthenticationKeyException { + throw new UnsupportedOperationException(); + } + + /** * Get the number of times the dynamic authentication keys have been used. * * @return int array of dynamic authentication key usage counts. */ public @NonNull abstract int[] getAuthenticationDataUsageCount(); + + /** + * Proves ownership of a credential. + * + * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey + * with payload set to {@code ProofOfDeletion} as defined below.</p> + * + * <p>The returned CBOR is the following:</p> + * <pre> + * ProofOfOwnership = [ + * "ProofOfOwnership", ; tstr + * tstr, ; DocType + * bstr, ; Challenge + * bool ; true if this is a test credential, should + * ; always be false. + * ] + * </pre> + * + * <p>This is only implemented in feature version 202101 or later. If not implemented, the call + * fails with {@link UnsupportedOperationException}. See + * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known + * feature versions. + * + * @param challenge is a non-empty byte array whose contents should be unique, fresh and + * provided by the issuing authority. The value provided is embedded in the + * generated CBOR and enables the issuing authority to verify that the + * returned proof is fresh. + * @return the COSE_Sign1 data structure above + */ + public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) { + throw new UnsupportedOperationException(); + } + + /** + * Deletes a credential. + * + * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey + * with payload set to {@code ProofOfDeletion} as defined below.</p> + * + * <pre> + * ProofOfDeletion = [ + * "ProofOfDeletion", ; tstr + * tstr, ; DocType + * bstr, ; Challenge + * bool ; true if this is a test credential, should + * ; always be false. + * ] + * </pre> + * + * <p>This is only implemented in feature version 202101 or later. If not implemented, the call + * fails with {@link UnsupportedOperationException}. See + * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known + * feature versions. + * + * @param challenge is a non-empty byte array whose contents should be unique, fresh and + * provided by the issuing authority. The value provided is embedded in the + * generated CBOR and enables the issuing authority to verify that the + * returned proof is fresh. + * @return the COSE_Sign1 data structure above + */ + public @NonNull byte[] delete(@NonNull byte[] challenge) { + throw new UnsupportedOperationException(); + } + + /** + * Updates the credential with new access control profiles and data items. + * + * <p>This method is similar to + * {@link WritableIdentityCredential#personalize(PersonalizationData)} except that it operates + * on an existing credential, see the documentation for that method for the format of the + * returned data. + * + * <p>If this call succeeds an side-effect is that all dynamic authentication keys for the + * credential are deleted. The application will need to use + * {@link #getAuthKeysNeedingCertification()} to generate replacement keys and return + * them for issuer certification. + * + * <p>This is only implemented in feature version 202101 or later. If not implemented, the call + * fails with {@link UnsupportedOperationException}. See + * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known + * feature versions. + * + * @param personalizationData The data to update, including access control profiles + * and data elements and their values, grouped into namespaces. + * @return A COSE_Sign1 data structure, see above. + */ + public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) { + throw new UnsupportedOperationException(); + } } diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java index 3843d9279900..6ccd0e892141 100644 --- a/identity/java/android/security/identity/IdentityCredentialStore.java +++ b/identity/java/android/security/identity/IdentityCredentialStore.java @@ -72,6 +72,17 @@ import java.lang.annotation.RetentionPolicy; * <p>Credentials provisioned to the direct access store should <strong>always</strong> use reader * authentication to protect data elements. The reason for this is user authentication or user * approval of data release is not possible when the device is off. + * + * <p>The Identity Credential API is designed to be able to evolve and change over time + * but still provide 100% backwards compatibility. This is complicated by the fact that + * there may be a version skew between the API used by the application and the version + * implemented in secure hardware. To solve this problem, the API provides for a way + * for the application to query which feature version the hardware implements (if any + * at all) using + * {@link android.content.pm#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} and + * {@link android.content.pm#FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS}. + * Methods which only work on certain feature versions are clearly documented as + * such. */ public abstract class IdentityCredentialStore { IdentityCredentialStore() {} @@ -193,7 +204,9 @@ public abstract class IdentityCredentialStore { * @param credentialName the name of the credential to delete. * @return {@code null} if the credential was not found, the COSE_Sign1 data structure above * if the credential was found and deleted. + * @deprecated Use {@link IdentityCredential#delete(byte[])} instead. */ + @Deprecated public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName); /** @hide */ @@ -201,5 +214,4 @@ public abstract class IdentityCredentialStore { @Retention(RetentionPolicy.SOURCE) public @interface Ciphersuite { } - } diff --git a/keystore/java/android/security/AuthTokenUtils.java b/keystore/java/android/security/AuthTokenUtils.java new file mode 100644 index 000000000000..e6376000673b --- /dev/null +++ b/keystore/java/android/security/AuthTokenUtils.java @@ -0,0 +1,75 @@ +/* + * 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.security; + +import android.annotation.NonNull; +import android.hardware.security.keymint.HardwareAuthToken; +import android.hardware.security.secureclock.Timestamp; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * @hide This Utils class provides method(s) for AuthToken conversion. + */ +public class AuthTokenUtils { + + private AuthTokenUtils(){ + } + + /** + * Build a HardwareAuthToken from a byte array + * @param array byte array representing an auth token + * @return HardwareAuthToken representation of an auth token + */ + public static @NonNull HardwareAuthToken toHardwareAuthToken(@NonNull byte[] array) { + final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken(); + + // First byte is version, which does not exist in HardwareAuthToken anymore + // Next 8 bytes is the challenge. + hardwareAuthToken.challenge = + ByteBuffer.wrap(array, 1, 8).order(ByteOrder.nativeOrder()).getLong(); + + // Next 8 bytes is the userId + hardwareAuthToken.userId = + ByteBuffer.wrap(array, 9, 8).order(ByteOrder.nativeOrder()).getLong(); + + // Next 8 bytes is the authenticatorId. + hardwareAuthToken.authenticatorId = + ByteBuffer.wrap(array, 17, 8).order(ByteOrder.nativeOrder()).getLong(); + + // while the other fields are in machine byte order, authenticatorType and timestamp + // are in network byte order. + // Next 4 bytes is the authenticatorType. + hardwareAuthToken.authenticatorType = + ByteBuffer.wrap(array, 25, 4).order(ByteOrder.BIG_ENDIAN).getInt(); + // Next 8 bytes is the timestamp. + final Timestamp timestamp = new Timestamp(); + timestamp.milliSeconds = + ByteBuffer.wrap(array, 29, 8).order(ByteOrder.BIG_ENDIAN).getLong(); + hardwareAuthToken.timestamp = timestamp; + + // Last 32 bytes is the mac, 37:69 + hardwareAuthToken.mac = new byte[32]; + System.arraycopy(array, 37 /* srcPos */, + hardwareAuthToken.mac, + 0 /* destPos */, + 32 /* length */); + + return hardwareAuthToken; + } +} diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java new file mode 100644 index 000000000000..21d23b1b2575 --- /dev/null +++ b/keystore/java/android/security/Authorization.java @@ -0,0 +1,107 @@ +/* + * 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.security; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.security.keymint.HardwareAuthToken; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; +import android.security.authorization.IKeystoreAuthorization; +import android.security.authorization.LockScreenEvent; +import android.system.keystore2.ResponseCode; +import android.util.Log; + +/** + * @hide This is the client side for IKeystoreAuthorization AIDL. + * It shall only be used by biometric authentication providers and Gatekeeper. + */ +public class Authorization { + private static final String TAG = "KeystoreAuthorization"; + private static IKeystoreAuthorization sIKeystoreAuthorization; + + public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR; + + public Authorization() { + sIKeystoreAuthorization = null; + } + + private static synchronized IKeystoreAuthorization getService() { + if (sIKeystoreAuthorization == null) { + sIKeystoreAuthorization = IKeystoreAuthorization.Stub.asInterface( + ServiceManager.checkService("android.security.authorization")); + } + return sIKeystoreAuthorization; + } + + /** + * Adds an auth token to keystore2. + * + * @param authToken created by Android authenticators. + * @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}. + */ + public int addAuthToken(@NonNull HardwareAuthToken authToken) { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; + try { + getService().addAuthToken(authToken); + return 0; + } catch (RemoteException e) { + Log.w(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } catch (ServiceSpecificException e) { + return e.errorCode; + } + } + + /** + * Add an auth token to Keystore 2.0 in the legacy serialized auth token format. + * @param authToken + * @return 0 if successful or a {@code ResponseCode}. + */ + public int addAuthToken(@NonNull byte[] authToken) { + return addAuthToken(AuthTokenUtils.toHardwareAuthToken(authToken)); + } + + /** + * Informs keystore2 about lock screen event. + * + * @param locked - whether it is a lock (true) or unlock (false) event + * @param syntheticPassword - if it is an unlock event with the password, pass the synthetic + * password provided by the LockSettingService + * + * @return 0 if successful or a {@code ResponseCode}. + */ + public int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId, + @Nullable byte[] syntheticPassword) { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; + try { + if (locked) { + getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null); + } else { + getService().onLockScreenEvent(LockScreenEvent.UNLOCK, userId, syntheticPassword); + } + return 0; + } catch (RemoteException e) { + Log.w(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } catch (ServiceSpecificException e) { + return e.errorCode; + } + } + +} diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 7abcfdc98bc6..9e1fb54bedbe 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -19,9 +19,9 @@ package android.security; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; -import com.android.org.bouncycastle.util.io.pem.PemObject; -import com.android.org.bouncycastle.util.io.pem.PemReader; -import com.android.org.bouncycastle.util.io.pem.PemWriter; +import com.android.internal.org.bouncycastle.util.io.pem.PemObject; +import com.android.internal.org.bouncycastle.util.io.pem.PemReader; +import com.android.internal.org.bouncycastle.util.io.pem.PemWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index c70c986fcd6b..e19d88c182ff 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -45,8 +45,8 @@ import android.security.keystore.KeystoreResponse; import android.security.keystore.UserNotAuthenticatedException; import android.util.Log; -import com.android.org.bouncycastle.asn1.ASN1InputStream; -import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; +import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -996,6 +996,7 @@ public class KeyStore { */ public int addAuthToken(byte[] authToken) { try { + new Authorization().addAuthToken(authToken); return mBinder.addAuthToken(authToken); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index 92d87aa0fed6..f7477bf92c81 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -23,6 +23,7 @@ import android.os.Build; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; +import android.security.keymaster.KeymasterDefs; import android.system.keystore2.IKeystoreService; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyEntryResponse; @@ -107,7 +108,7 @@ public class KeyStore2 { return request.execute(service); } catch (ServiceSpecificException e) { Log.e(TAG, "KeyStore exception", e); - throw new KeyStoreException(e.errorCode, ""); + throw getKeyStoreException(e.errorCode); } catch (RemoteException e) { if (firstTry) { Log.w(TAG, "Looks like we may have lost connection to the Keystore " @@ -274,4 +275,40 @@ public class KeyStore2 { } } + static KeyStoreException getKeyStoreException(int errorCode) { + if (errorCode > 0) { + // KeyStore layer error + switch (errorCode) { + case ResponseCode.LOCKED: + return new KeyStoreException(errorCode, "User authentication required"); + case ResponseCode.UNINITIALIZED: + return new KeyStoreException(errorCode, "Keystore not initialized"); + case ResponseCode.SYSTEM_ERROR: + return new KeyStoreException(errorCode, "System error"); + case ResponseCode.PERMISSION_DENIED: + return new KeyStoreException(errorCode, "Permission denied"); + case ResponseCode.KEY_NOT_FOUND: + return new KeyStoreException(errorCode, "Key not found"); + case ResponseCode.VALUE_CORRUPTED: + return new KeyStoreException(errorCode, "Key blob corrupted"); + case ResponseCode.KEY_PERMANENTLY_INVALIDATED: + return new KeyStoreException(errorCode, "Key permanently invalidated"); + default: + return new KeyStoreException(errorCode, String.valueOf(errorCode)); + } + } else { + // Keymaster layer error + switch (errorCode) { + case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT: + // The name of this parameter significantly differs between Keymaster and + // framework APIs. Use the framework wording to make life easier for developers. + return new KeyStoreException(errorCode, + "Invalid user authentication validity duration"); + default: + return new KeyStoreException(errorCode, + KeymasterDefs.getErrorMessage(errorCode)); + } + } + } + } diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java index 7ea9e1438845..a6552dddc630 100644 --- a/keystore/java/android/security/KeyStoreOperation.java +++ b/keystore/java/android/security/KeyStoreOperation.java @@ -73,8 +73,7 @@ public class KeyStoreOperation { ); } default: - // TODO Human readable string. Use something like KeyStore.getKeyStoreException - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } } catch (RemoteException e) { // Log exception and report invalid operation handle. diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java index 3ef4aa5b7ec3..372add9b7ecb 100644 --- a/keystore/java/android/security/KeyStoreSecurityLevel.java +++ b/keystore/java/android/security/KeyStoreSecurityLevel.java @@ -52,7 +52,7 @@ public class KeyStoreSecurityLevel { try { return request.execute(); } catch (ServiceSpecificException e) { - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } catch (RemoteException e) { // Log exception and report invalid operation handle. // This should prompt the caller drop the reference to this operation and retry. @@ -96,25 +96,26 @@ public class KeyStoreSecurityLevel { } catch (ServiceSpecificException e) { switch (e.errorCode) { case ResponseCode.BACKEND_BUSY: { + long backOffHint = (long) (Math.random() * 80 + 20); if (CompatChanges.isChangeEnabled( KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) { // Starting with Android S we inform the caller about the // backend being busy. - throw new BackendBusyException(); + throw new BackendBusyException(backOffHint); } else { // Before Android S operation creation must always succeed. So we // just have to retry. We do so with a randomized back-off between - // 50 and 250ms. + // 20 and 100ms. // It is a little awkward that we cannot break out of this loop // by interrupting this thread. But that is the expected behavior. // There is some comfort in the fact that interrupting a thread // also does not unblock a thread waiting for a binder transaction. - interruptedPreservingSleep((long) (Math.random() * 200 + 50)); + interruptedPreservingSleep(backOffHint); } break; } default: - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 6ad8d2c0aca3..334b1110d651 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -26,24 +26,24 @@ import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterCertificateChain; import android.security.keymaster.KeymasterDefs; -import com.android.org.bouncycastle.asn1.ASN1EncodableVector; -import com.android.org.bouncycastle.asn1.ASN1InputStream; -import com.android.org.bouncycastle.asn1.ASN1Integer; -import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier; -import com.android.org.bouncycastle.asn1.DERBitString; -import com.android.org.bouncycastle.asn1.DERNull; -import com.android.org.bouncycastle.asn1.DERSequence; -import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import com.android.org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import com.android.org.bouncycastle.asn1.x509.Certificate; -import com.android.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import com.android.org.bouncycastle.asn1.x509.TBSCertificate; -import com.android.org.bouncycastle.asn1.x509.Time; -import com.android.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; -import com.android.org.bouncycastle.asn1.x9.X9ObjectIdentifiers; -import com.android.org.bouncycastle.jce.X509Principal; -import com.android.org.bouncycastle.jce.provider.X509CertificateObject; -import com.android.org.bouncycastle.x509.X509V3CertificateGenerator; +import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector; +import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; +import com.android.internal.org.bouncycastle.asn1.ASN1Integer; +import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier; +import com.android.internal.org.bouncycastle.asn1.DERBitString; +import com.android.internal.org.bouncycastle.asn1.DERNull; +import com.android.internal.org.bouncycastle.asn1.DERSequence; +import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import com.android.internal.org.bouncycastle.asn1.x509.Certificate; +import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import com.android.internal.org.bouncycastle.asn1.x509.TBSCertificate; +import com.android.internal.org.bouncycastle.asn1.x509.Time; +import com.android.internal.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; +import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import com.android.internal.org.bouncycastle.jce.X509Principal; +import com.android.internal.org.bouncycastle.jce.provider.X509CertificateObject; +import com.android.internal.org.bouncycastle.x509.X509V3CertificateGenerator; import libcore.util.EmptyArray; diff --git a/keystore/java/android/security/keystore/BackendBusyException.java b/keystore/java/android/security/keystore/BackendBusyException.java index 1a88469d7e54..a813e939a720 100644 --- a/keystore/java/android/security/keystore/BackendBusyException.java +++ b/keystore/java/android/security/keystore/BackendBusyException.java @@ -16,37 +16,66 @@ package android.security.keystore; +import android.annotation.DurationMillisLong; import android.annotation.NonNull; import java.security.ProviderException; /** * Indicates a transient error that prevented a key operation from being created. - * Callers should try again with a back-off period of 10-30 milliseconds. + * Callers should try again with a back-off period of {@link #getBackOffHintMillis()} + * milliseconds. */ public class BackendBusyException extends ProviderException { + private final long mBackOffHintMillis; + /** * Constructs a new {@code BackendBusyException} without detail message and cause. + * */ - public BackendBusyException() { + public BackendBusyException(@DurationMillisLong long backOffHintMillis) { super("The keystore backend has no operation slots available. Retry later."); + if (backOffHintMillis < 0) { + throw new IllegalArgumentException("Back-off hint cannot be negative."); + } + mBackOffHintMillis = backOffHintMillis; } /** * Constructs a new {@code BackendBusyException} with the provided detail message and * no cause. */ - public BackendBusyException(@NonNull String message) { + public BackendBusyException(@DurationMillisLong long backOffHintMillis, + @NonNull String message) { super(message); + if (backOffHintMillis < 0) { + throw new IllegalArgumentException("Back-off hint cannot be negative."); + } + mBackOffHintMillis = backOffHintMillis; } /** * Constructs a new {@code BackendBusyException} with the provided detail message and * cause. */ - public BackendBusyException(@NonNull String message, @NonNull Throwable cause) { + public BackendBusyException(@DurationMillisLong long backOffHintMillis, + @NonNull String message, @NonNull Throwable cause) { super(message, cause); + if (backOffHintMillis < 0) { + throw new IllegalArgumentException("Back-off hint cannot be negative."); + } + mBackOffHintMillis = backOffHintMillis; } + /** + * When retrying to start a Keystore operation after receiving this exception, this can be + * used to determine how long to wait before retrying. It is not guaranteed that the operation + * will succeeds after this time. Multiple retries may be necessary if the system is congested. + * + * @return Number of milliseconds to back off before retrying. + */ + public @DurationMillisLong long getBackOffHintMillis() { + return mBackOffHintMillis; + } } diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index 5928540b19bf..014d6882be8d 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -67,6 +67,7 @@ public abstract class KeyProperties { PURPOSE_SIGN, PURPOSE_VERIFY, PURPOSE_WRAP_KEY, + PURPOSE_AGREE_KEY, }) public @interface PurposeEnum {} @@ -96,6 +97,11 @@ public abstract class KeyProperties { public static final int PURPOSE_WRAP_KEY = 1 << 5; /** + * Purpose of key: creating a shared ECDH secret through key agreement. + */ + public static final int PURPOSE_AGREE_KEY = 1 << 6; + + /** * @hide */ public static abstract class Purpose { @@ -113,6 +119,8 @@ public abstract class KeyProperties { return KeymasterDefs.KM_PURPOSE_VERIFY; case PURPOSE_WRAP_KEY: return KeymasterDefs.KM_PURPOSE_WRAP; + case PURPOSE_AGREE_KEY: + return KeymasterDefs.KM_PURPOSE_AGREE_KEY; default: throw new IllegalArgumentException("Unknown purpose: " + purpose); } @@ -130,6 +138,8 @@ public abstract class KeyProperties { return PURPOSE_VERIFY; case KeymasterDefs.KM_PURPOSE_WRAP: return PURPOSE_WRAP_KEY; + case KeymasterDefs.KM_PURPOSE_AGREE_KEY: + return PURPOSE_AGREE_KEY; default: throw new IllegalArgumentException("Unknown purpose: " + purpose); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java index 6ddaa704afa8..b631999c2c54 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java @@ -38,9 +38,10 @@ public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey impleme public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor, @NonNull KeyMetadata metadata, + @NonNull byte[] x509EncodedForm, @NonNull KeyStoreSecurityLevel securityLevel, @NonNull ECParameterSpec params, @NonNull ECPoint w) { - super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_EC, securityLevel); + super(descriptor, metadata, x509EncodedForm, KeyProperties.KEY_ALGORITHM_EC, securityLevel); mParams = params; mW = w; } @@ -48,7 +49,7 @@ public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey impleme public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor, @NonNull KeyMetadata metadata, @NonNull KeyStoreSecurityLevel securityLevel, @NonNull ECPublicKey info) { - this(descriptor, metadata, securityLevel, info.getParams(), info.getW()); + this(descriptor, metadata, info.getEncoded(), securityLevel, info.getParams(), info.getW()); if (!"X.509".equalsIgnoreCase(info.getFormat())) { throw new IllegalArgumentException( "Unsupported key export format: " + info.getFormat()); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java new file mode 100644 index 000000000000..fc963a88c4d1 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java @@ -0,0 +1,240 @@ +/* + * 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 android.security.keystore2; + +import android.hardware.security.keymint.Algorithm; +import android.hardware.security.keymint.KeyParameter; +import android.hardware.security.keymint.KeyPurpose; +import android.hardware.security.keymint.Tag; +import android.security.KeyStoreException; +import android.security.KeyStoreOperation; +import android.security.keystore.KeyStoreCryptoOperation; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.ArrayList; +import java.util.List; + +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +/** + * {@link KeyAgreementSpi} which provides an ECDH implementation backed by Android KeyStore. + * + * @hide + */ +public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi + implements KeyStoreCryptoOperation { + + private static final String TAG = "AndroidKeyStoreKeyAgreementSpi"; + + /** + * ECDH implementation. + * + * @hide + */ + public static class ECDH extends AndroidKeyStoreKeyAgreementSpi { + public ECDH() { + super(Algorithm.EC); + } + } + + private final int mKeymintAlgorithm; + + // Fields below are populated by engineInit and should be preserved after engineDoFinal. + private AndroidKeyStorePrivateKey mKey; + private PublicKey mOtherPartyKey; + + // Fields below are reset when engineDoFinal succeeds. + private KeyStoreOperation mOperation; + private long mOperationHandle; + + protected AndroidKeyStoreKeyAgreementSpi(int keymintAlgorithm) { + resetAll(); + + mKeymintAlgorithm = keymintAlgorithm; + } + + @Override + protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { + resetAll(); + + if (key == null) { + throw new InvalidKeyException("key == null"); + } else if (!(key instanceof AndroidKeyStorePrivateKey)) { + throw new InvalidKeyException( + "Only Android KeyStore private keys supported. Key: " + key); + } + // Checking the correct KEY_PURPOSE and algorithm is done by the Keymint implementation in + // ensureKeystoreOperationInitialized() below. + mKey = (AndroidKeyStorePrivateKey) key; + + boolean success = false; + try { + ensureKeystoreOperationInitialized(); + success = true; + } finally { + if (!success) { + resetAll(); + } + } + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException( + "Unsupported algorithm parameters: " + params); + } + engineInit(key, random); + } + + @Override + protected Key engineDoPhase(Key key, boolean lastPhase) + throws InvalidKeyException, IllegalStateException { + ensureKeystoreOperationInitialized(); + + if (key == null) { + throw new InvalidKeyException("key == null"); + } else if (!(key instanceof PublicKey)) { + throw new InvalidKeyException("Only public keys supported. Key: " + key); + } else if (!lastPhase) { + throw new IllegalStateException( + "Only one other party supported. lastPhase must be set to true."); + } else if (mOtherPartyKey != null) { + throw new IllegalStateException( + "Only one other party supported. doPhase() must only be called exactly once."); + } + // The other party key will be passed as part of the doFinal() call, to prevent an + // additional IPC. + mOtherPartyKey = (PublicKey) key; + + return null; // No intermediate key + } + + @Override + protected byte[] engineGenerateSecret() throws IllegalStateException { + try { + ensureKeystoreOperationInitialized(); + } catch (InvalidKeyException e) { + throw new IllegalStateException("Not initialized", e); + } + + if (mOtherPartyKey == null) { + throw new IllegalStateException("Other party key not provided. Call doPhase() first."); + } + byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded(); + + try { + return mOperation.finish(otherPartyKeyEncoded, null); + } catch (KeyStoreException e) { + throw new ProviderException("Keystore operation failed", e); + } finally { + resetWhilePreservingInitState(); + } + } + + @Override + protected SecretKey engineGenerateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { + byte[] generatedSecret = engineGenerateSecret(); + + return new SecretKeySpec(generatedSecret, algorithm); + } + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) + throws IllegalStateException, ShortBufferException { + byte[] generatedSecret = engineGenerateSecret(); + + if (generatedSecret.length > sharedSecret.length - offset) { + throw new ShortBufferException("Needed: " + generatedSecret.length); + } + System.arraycopy(generatedSecret, 0, sharedSecret, offset, generatedSecret.length); + return generatedSecret.length; + } + + @Override + public long getOperationHandle() { + return mOperationHandle; + } + + @Override + protected void finalize() throws Throwable { + try { + resetAll(); + } finally { + super.finalize(); + } + } + + private void resetWhilePreservingInitState() { + KeyStoreCryptoOperationUtils.abortOperation(mOperation); + mOperationHandle = 0; + mOperation = null; + mOtherPartyKey = null; + } + + private void resetAll() { + resetWhilePreservingInitState(); + mKey = null; + } + + private void ensureKeystoreOperationInitialized() + throws InvalidKeyException, IllegalStateException { + if (mKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (mOperation != null) { + return; + } + + // We don't need to explicitly pass in any other parameters here, as they're part of the + // private key that is available to Keymint. + List<KeyParameter> parameters = new ArrayList<>(); + parameters.add(KeyStore2ParameterUtils.makeEnum( + Tag.PURPOSE, KeyPurpose.AGREE_KEY + )); + + try { + mOperation = + mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters); + } catch (KeyStoreException keyStoreException) { + // If necessary, throw an exception due to KeyStore operation having failed. + InvalidKeyException e = + KeyStoreCryptoOperationUtils.getInvalidKeyException(mKey, keyStoreException); + if (e != null) { + throw e; + } + } + + // Set the operation handle. This will be a random number, or the operation challenge if + // user authentication is required. If we got a challenge we check if the authorization can + // possibly succeed. + mOperationHandle = + KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(mOperation, mKey); + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 403da189262d..75ac61a22cab 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -94,6 +94,9 @@ public class AndroidKeyStoreProvider extends Provider { put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede"); } + // javax.crypto.KeyAgreement + put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH"); + // java.security.SecretKeyFactory putSecretKeyFactoryImpl("AES"); if (supports3DES) { @@ -360,6 +363,11 @@ public class AndroidKeyStoreProvider extends Provider { } } + if (response.iSecurityLevel == null) { + // This seems to be a pure certificate entry, nothing to return here. + return null; + } + Integer keymasterAlgorithm = null; // We just need one digest for the algorithm name int keymasterDigest = -1; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java index 49dd77e3a3db..db3e567cb6b4 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java @@ -32,13 +32,15 @@ import java.security.PublicKey; public abstract class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey { private final byte[] mCertificate; private final byte[] mCertificateChain; + private final byte[] mEncoded; public AndroidKeyStorePublicKey(@NonNull KeyDescriptor descriptor, - @NonNull KeyMetadata metadata, @NonNull String algorithm, - @NonNull KeyStoreSecurityLevel securityLevel) { + @NonNull KeyMetadata metadata, @NonNull byte[] x509EncodedForm, + @NonNull String algorithm, @NonNull KeyStoreSecurityLevel securityLevel) { super(descriptor, metadata.key.nspace, metadata.authorizations, algorithm, securityLevel); mCertificate = metadata.certificate; mCertificateChain = metadata.certificateChain; + mEncoded = x509EncodedForm; } abstract AndroidKeyStorePrivateKey getPrivateKey(); @@ -50,7 +52,7 @@ public abstract class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implem @Override public byte[] getEncoded() { - return ArrayUtils.cloneIfNotEmpty(mCertificate); + return ArrayUtils.cloneIfNotEmpty(mEncoded); } @Override diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java index b578ea9baa06..9fe6cf3c113f 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java @@ -36,9 +36,11 @@ public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implem public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor, @NonNull KeyMetadata metadata, + @NonNull byte[] x509EncodedForm, @NonNull KeyStoreSecurityLevel securityLevel, @NonNull BigInteger modulus, @NonNull BigInteger publicExponent) { - super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_RSA, securityLevel); + super(descriptor, metadata, x509EncodedForm, KeyProperties.KEY_ALGORITHM_RSA, + securityLevel); mModulus = modulus; mPublicExponent = publicExponent; } @@ -46,7 +48,8 @@ public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implem public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor, @NonNull KeyMetadata metadata, @NonNull KeyStoreSecurityLevel securityLevel, @NonNull RSAPublicKey info) { - this(descriptor, metadata, securityLevel, info.getModulus(), info.getPublicExponent()); + this(descriptor, metadata, info.getEncoded(), securityLevel, info.getModulus(), + info.getPublicExponent()); if (!"X.509".equalsIgnoreCase(info.getFormat())) { throw new IllegalArgumentException( "Unsupported key export format: " + info.getFormat()); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 5e7f6482ebed..07169cedc1d9 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -490,7 +490,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { int[] keymasterEncryptionPaddings = KeyProperties.EncryptionPadding.allToKeymaster( spec.getEncryptionPaddings()); - if (((spec.getPurposes() & KeyProperties.PURPOSE_DECRYPT) != 0) + if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) && (spec.isRandomizedEncryptionRequired())) { for (int keymasterPadding : keymasterEncryptionPaddings) { if (!KeymasterUtils diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java index 6c733ba712d5..33e8dede9f5c 100644 --- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationChunkedStreamer.java @@ -139,7 +139,9 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS int inputConsumed = ArrayUtils.copy(input, inputOffset, mChunk, mChunkLength, inputLength); inputLength -= inputConsumed; - inputOffset += inputOffset; + inputOffset += inputConsumed; + mChunkLength += inputConsumed; + if (mChunkLength < mChunkSizeMax) return output; byte[] o = mKeyStoreStream.update(mChunk); if (o != null) { output = ArrayUtils.concat(output, o); diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp index 6c6c5c9f4e22..8a10599b498f 100644 --- a/libs/androidfw/LocaleDataTables.cpp +++ b/libs/androidfw/LocaleDataTables.cpp @@ -64,42 +64,43 @@ const char SCRIPT_CODES[][4] = { /* 60 */ {'N', 'k', 'o', 'o'}, /* 61 */ {'N', 's', 'h', 'u'}, /* 62 */ {'O', 'g', 'a', 'm'}, - /* 63 */ {'O', 'r', 'k', 'h'}, - /* 64 */ {'O', 'r', 'y', 'a'}, - /* 65 */ {'O', 's', 'g', 'e'}, - /* 66 */ {'P', 'a', 'u', 'c'}, - /* 67 */ {'P', 'h', 'l', 'i'}, - /* 68 */ {'P', 'h', 'n', 'x'}, - /* 69 */ {'P', 'l', 'r', 'd'}, - /* 70 */ {'P', 'r', 't', 'i'}, - /* 71 */ {'R', 'u', 'n', 'r'}, - /* 72 */ {'S', 'a', 'm', 'r'}, - /* 73 */ {'S', 'a', 'r', 'b'}, - /* 74 */ {'S', 'a', 'u', 'r'}, - /* 75 */ {'S', 'g', 'n', 'w'}, - /* 76 */ {'S', 'i', 'n', 'h'}, - /* 77 */ {'S', 'o', 'g', 'd'}, - /* 78 */ {'S', 'o', 'r', 'a'}, - /* 79 */ {'S', 'o', 'y', 'o'}, - /* 80 */ {'S', 'y', 'r', 'c'}, - /* 81 */ {'T', 'a', 'l', 'e'}, - /* 82 */ {'T', 'a', 'l', 'u'}, - /* 83 */ {'T', 'a', 'm', 'l'}, - /* 84 */ {'T', 'a', 'n', 'g'}, - /* 85 */ {'T', 'a', 'v', 't'}, - /* 86 */ {'T', 'e', 'l', 'u'}, - /* 87 */ {'T', 'f', 'n', 'g'}, - /* 88 */ {'T', 'h', 'a', 'a'}, - /* 89 */ {'T', 'h', 'a', 'i'}, - /* 90 */ {'T', 'i', 'b', 't'}, - /* 91 */ {'U', 'g', 'a', 'r'}, - /* 92 */ {'V', 'a', 'i', 'i'}, - /* 93 */ {'W', 'c', 'h', 'o'}, - /* 94 */ {'X', 'p', 'e', 'o'}, - /* 95 */ {'X', 's', 'u', 'x'}, - /* 96 */ {'Y', 'i', 'i', 'i'}, - /* 97 */ {'~', '~', '~', 'A'}, - /* 98 */ {'~', '~', '~', 'B'}, + /* 63 */ {'O', 'l', 'c', 'k'}, + /* 64 */ {'O', 'r', 'k', 'h'}, + /* 65 */ {'O', 'r', 'y', 'a'}, + /* 66 */ {'O', 's', 'g', 'e'}, + /* 67 */ {'P', 'a', 'u', 'c'}, + /* 68 */ {'P', 'h', 'l', 'i'}, + /* 69 */ {'P', 'h', 'n', 'x'}, + /* 70 */ {'P', 'l', 'r', 'd'}, + /* 71 */ {'P', 'r', 't', 'i'}, + /* 72 */ {'R', 'u', 'n', 'r'}, + /* 73 */ {'S', 'a', 'm', 'r'}, + /* 74 */ {'S', 'a', 'r', 'b'}, + /* 75 */ {'S', 'a', 'u', 'r'}, + /* 76 */ {'S', 'g', 'n', 'w'}, + /* 77 */ {'S', 'i', 'n', 'h'}, + /* 78 */ {'S', 'o', 'g', 'd'}, + /* 79 */ {'S', 'o', 'r', 'a'}, + /* 80 */ {'S', 'o', 'y', 'o'}, + /* 81 */ {'S', 'y', 'r', 'c'}, + /* 82 */ {'T', 'a', 'l', 'e'}, + /* 83 */ {'T', 'a', 'l', 'u'}, + /* 84 */ {'T', 'a', 'm', 'l'}, + /* 85 */ {'T', 'a', 'n', 'g'}, + /* 86 */ {'T', 'a', 'v', 't'}, + /* 87 */ {'T', 'e', 'l', 'u'}, + /* 88 */ {'T', 'f', 'n', 'g'}, + /* 89 */ {'T', 'h', 'a', 'a'}, + /* 90 */ {'T', 'h', 'a', 'i'}, + /* 91 */ {'T', 'i', 'b', 't'}, + /* 92 */ {'U', 'g', 'a', 'r'}, + /* 93 */ {'V', 'a', 'i', 'i'}, + /* 94 */ {'W', 'c', 'h', 'o'}, + /* 95 */ {'X', 'p', 'e', 'o'}, + /* 96 */ {'X', 's', 'u', 'x'}, + /* 97 */ {'Y', 'i', 'i', 'i'}, + /* 98 */ {'~', '~', '~', 'A'}, + /* 99 */ {'~', '~', '~', 'B'}, }; @@ -120,7 +121,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x80600000u, 46u}, // ada -> Latn {0x90600000u, 46u}, // ade -> Latn {0xA4600000u, 46u}, // adj -> Latn - {0xBC600000u, 90u}, // adp -> Tibt + {0xBC600000u, 91u}, // adp -> Tibt {0xE0600000u, 17u}, // ady -> Cyrl {0xE4600000u, 46u}, // adz -> Latn {0x61650000u, 4u}, // ae -> Avst @@ -138,7 +139,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB8E00000u, 0u}, // aho -> Ahom {0x99200000u, 46u}, // ajg -> Latn {0x616B0000u, 46u}, // ak -> Latn - {0xA9400000u, 95u}, // akk -> Xsux + {0xA9400000u, 96u}, // akk -> Xsux {0x81600000u, 46u}, // ala -> Latn {0xA1600000u, 46u}, // ali -> Latn {0xB5600000u, 46u}, // aln -> Latn @@ -163,7 +164,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xC9E00000u, 46u}, // aps -> Latn {0xE5E00000u, 46u}, // apz -> Latn {0x61720000u, 1u}, // ar -> Arab - {0x61725842u, 98u}, // ar-XB -> ~~~B + {0x61725842u, 99u}, // ar-XB -> ~~~B {0x8A200000u, 2u}, // arc -> Armi {0x9E200000u, 46u}, // arh -> Latn {0xB6200000u, 46u}, // arn -> Latn @@ -174,7 +175,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xE6200000u, 1u}, // arz -> Arab {0x61730000u, 7u}, // as -> Beng {0x82400000u, 46u}, // asa -> Latn - {0x92400000u, 75u}, // ase -> Sgnw + {0x92400000u, 76u}, // ase -> Sgnw {0x9A400000u, 46u}, // asg -> Latn {0xBA400000u, 46u}, // aso -> Latn {0xCE400000u, 46u}, // ast -> Latn @@ -231,7 +232,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xDC810000u, 46u}, // bex -> Latn {0xE4810000u, 46u}, // bez -> Latn {0x8CA10000u, 46u}, // bfd -> Latn - {0xC0A10000u, 83u}, // bfq -> Taml + {0xC0A10000u, 84u}, // bfq -> Taml {0xCCA10000u, 1u}, // bft -> Arab {0xE0A10000u, 18u}, // bfy -> Deva {0x62670000u, 17u}, // bg -> Cyrl @@ -265,7 +266,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xC1410000u, 46u}, // bkq -> Latn {0xD1410000u, 46u}, // bku -> Latn {0xD5410000u, 46u}, // bkv -> Latn - {0xCD610000u, 85u}, // blt -> Tavt + {0xCD610000u, 86u}, // blt -> Tavt {0x626D0000u, 46u}, // bm -> Latn {0x9D810000u, 46u}, // bmh -> Latn {0xA9810000u, 46u}, // bmk -> Latn @@ -275,7 +276,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x99A10000u, 46u}, // bng -> Latn {0xB1A10000u, 46u}, // bnm -> Latn {0xBDA10000u, 46u}, // bnp -> Latn - {0x626F0000u, 90u}, // bo -> Tibt + {0x626F0000u, 91u}, // bo -> Tibt {0xA5C10000u, 46u}, // boj -> Latn {0xB1C10000u, 46u}, // bom -> Latn {0xB5C10000u, 46u}, // bon -> Latn @@ -322,6 +323,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x9F210000u, 46u}, // bzh -> Latn {0xDB210000u, 46u}, // bzw -> Latn {0x63610000u, 46u}, // ca -> Latn + {0x8C020000u, 46u}, // cad -> Latn {0xB4020000u, 46u}, // can -> Latn {0xA4220000u, 46u}, // cbj -> Latn {0x9C420000u, 46u}, // cch -> Latn @@ -346,7 +348,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xE1420000u, 46u}, // cky -> Latn {0x81620000u, 46u}, // cla -> Latn {0x91820000u, 46u}, // cme -> Latn - {0x99820000u, 79u}, // cmg -> Soyo + {0x99820000u, 80u}, // cmg -> Soyo {0x636F0000u, 46u}, // co -> Latn {0xBDC20000u, 15u}, // cop -> Copt {0xC9E20000u, 46u}, // cps -> Latn @@ -360,7 +362,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x63730000u, 46u}, // cs -> Latn {0x86420000u, 46u}, // csb -> Latn {0xDA420000u, 10u}, // csw -> Cans - {0x8E620000u, 66u}, // ctd -> Pauc + {0x8E620000u, 67u}, // ctd -> Pauc {0x63750000u, 17u}, // cu -> Cyrl {0x63760000u, 17u}, // cv -> Cyrl {0x63790000u, 46u}, // cy -> Latn @@ -389,7 +391,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x91230000u, 46u}, // dje -> Latn {0xA5A30000u, 46u}, // dnj -> Latn {0x85C30000u, 46u}, // dob -> Latn - {0xA1C30000u, 1u}, // doi -> Arab + {0xA1C30000u, 18u}, // doi -> Deva {0xBDC30000u, 46u}, // dop -> Latn {0xD9C30000u, 46u}, // dow -> Latn {0x9E230000u, 56u}, // drh -> Mong @@ -404,12 +406,12 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x8A830000u, 46u}, // duc -> Latn {0x8E830000u, 46u}, // dud -> Latn {0x9A830000u, 46u}, // dug -> Latn - {0x64760000u, 88u}, // dv -> Thaa + {0x64760000u, 89u}, // dv -> Thaa {0x82A30000u, 46u}, // dva -> Latn {0xDAC30000u, 46u}, // dww -> Latn {0xBB030000u, 46u}, // dyo -> Latn {0xD3030000u, 46u}, // dyu -> Latn - {0x647A0000u, 90u}, // dz -> Tibt + {0x647A0000u, 91u}, // dz -> Tibt {0x9B230000u, 46u}, // dzg -> Latn {0xD0240000u, 46u}, // ebu -> Latn {0x65650000u, 46u}, // ee -> Latn @@ -422,7 +424,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x81840000u, 46u}, // ema -> Latn {0xA1840000u, 46u}, // emi -> Latn {0x656E0000u, 46u}, // en -> Latn - {0x656E5841u, 97u}, // en-XA -> ~~~A + {0x656E5841u, 98u}, // en-XA -> ~~~A {0xB5A40000u, 46u}, // enn -> Latn {0xC1A40000u, 46u}, // enq -> Latn {0x656F0000u, 46u}, // eo -> Latn @@ -438,6 +440,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x65750000u, 46u}, // eu -> Latn {0xBAC40000u, 46u}, // ewo -> Latn {0xCEE40000u, 46u}, // ext -> Latn + {0x83240000u, 46u}, // eza -> Latn {0x66610000u, 1u}, // fa -> Arab {0x80050000u, 46u}, // faa -> Latn {0x84050000u, 46u}, // fab -> Latn @@ -521,7 +524,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x95C60000u, 20u}, // gof -> Ethi {0xA1C60000u, 46u}, // goi -> Latn {0xB1C60000u, 18u}, // gom -> Deva - {0xB5C60000u, 86u}, // gon -> Telu + {0xB5C60000u, 87u}, // gon -> Telu {0xC5C60000u, 46u}, // gor -> Latn {0xC9C60000u, 46u}, // gos -> Latn {0xCDC60000u, 24u}, // got -> Goth @@ -566,7 +569,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xAD070000u, 46u}, // hil -> Latn {0x81670000u, 46u}, // hla -> Latn {0xD1670000u, 32u}, // hlu -> Hluw - {0x8D870000u, 69u}, // hmd -> Plrd + {0x8D870000u, 70u}, // hmd -> Plrd {0xCD870000u, 46u}, // hmt -> Latn {0x8DA70000u, 1u}, // hnd -> Arab {0x91A70000u, 18u}, // hne -> Deva @@ -601,7 +604,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x69670000u, 46u}, // ig -> Latn {0x84C80000u, 46u}, // igb -> Latn {0x90C80000u, 46u}, // ige -> Latn - {0x69690000u, 96u}, // ii -> Yiii + {0x69690000u, 97u}, // ii -> Yiii {0xA5280000u, 46u}, // ijj -> Latn {0x696B0000u, 46u}, // ik -> Latn {0xA9480000u, 46u}, // ikk -> Latn @@ -626,6 +629,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x6A610000u, 36u}, // ja -> Jpan {0x84090000u, 46u}, // jab -> Latn {0xB0090000u, 46u}, // jam -> Latn + {0xC4090000u, 46u}, // jar -> Latn {0xB8290000u, 46u}, // jbo -> Latn {0xD0290000u, 46u}, // jbu -> Latn {0xB4890000u, 46u}, // jen -> Latn @@ -661,7 +665,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x906A0000u, 46u}, // kde -> Latn {0x9C6A0000u, 1u}, // kdh -> Arab {0xAC6A0000u, 46u}, // kdl -> Latn - {0xCC6A0000u, 89u}, // kdt -> Thai + {0xCC6A0000u, 90u}, // kdt -> Thai {0x808A0000u, 46u}, // kea -> Latn {0xB48A0000u, 46u}, // ken -> Latn {0xE48A0000u, 46u}, // kez -> Latn @@ -673,7 +677,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x94CA0000u, 46u}, // kgf -> Latn {0xBCCA0000u, 46u}, // kgp -> Latn {0x80EA0000u, 46u}, // kha -> Latn - {0x84EA0000u, 82u}, // khb -> Talu + {0x84EA0000u, 83u}, // khb -> Talu {0xB4EA0000u, 18u}, // khn -> Deva {0xC0EA0000u, 46u}, // khq -> Latn {0xC8EA0000u, 46u}, // khs -> Latn @@ -766,7 +770,8 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x82EA0000u, 46u}, // kxa -> Latn {0x8AEA0000u, 20u}, // kxc -> Ethi {0x92EA0000u, 46u}, // kxe -> Latn - {0xB2EA0000u, 89u}, // kxm -> Thai + {0xAEEA0000u, 18u}, // kxl -> Deva + {0xB2EA0000u, 90u}, // kxm -> Thai {0xBEEA0000u, 1u}, // kxp -> Arab {0xDAEA0000u, 46u}, // kxw -> Latn {0xE6EA0000u, 46u}, // kxz -> Latn @@ -775,6 +780,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x6B795452u, 46u}, // ky-TR -> Latn {0x930A0000u, 46u}, // kye -> Latn {0xDF0A0000u, 46u}, // kyx -> Latn + {0x9F2A0000u, 1u}, // kzh -> Arab {0xA72A0000u, 46u}, // kzj -> Latn {0xC72A0000u, 46u}, // kzr -> Latn {0xCF2A0000u, 46u}, // kzt -> Latn @@ -790,7 +796,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xD02B0000u, 46u}, // lbu -> Latn {0xD82B0000u, 46u}, // lbw -> Latn {0xB04B0000u, 46u}, // lcm -> Latn - {0xBC4B0000u, 89u}, // lcp -> Thai + {0xBC4B0000u, 90u}, // lcp -> Thai {0x846B0000u, 46u}, // ldb -> Latn {0x8C8B0000u, 46u}, // led -> Latn {0x908B0000u, 46u}, // lee -> Latn @@ -814,7 +820,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xCD4B0000u, 46u}, // lkt -> Latn {0x916B0000u, 46u}, // lle -> Latn {0xB56B0000u, 46u}, // lln -> Latn - {0xB58B0000u, 86u}, // lmn -> Telu + {0xB58B0000u, 87u}, // lmn -> Telu {0xB98B0000u, 46u}, // lmo -> Latn {0xBD8B0000u, 46u}, // lmp -> Latn {0x6C6E0000u, 46u}, // ln -> Latn @@ -836,7 +842,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xE28B0000u, 46u}, // luy -> Latn {0xE68B0000u, 1u}, // luz -> Arab {0x6C760000u, 46u}, // lv -> Latn - {0xAECB0000u, 89u}, // lwl -> Thai + {0xAECB0000u, 90u}, // lwl -> Thai {0x9F2B0000u, 28u}, // lzh -> Hans {0xE72B0000u, 46u}, // lzz -> Latn {0x8C0C0000u, 46u}, // mad -> Latn @@ -927,7 +933,6 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xBA2C0000u, 57u}, // mro -> Mroo {0x6D730000u, 46u}, // ms -> Latn {0x6D734343u, 1u}, // ms-CC -> Arab - {0x6D734944u, 1u}, // ms-ID -> Arab {0x6D740000u, 46u}, // mt -> Latn {0x8A6C0000u, 46u}, // mtc -> Latn {0x966C0000u, 46u}, // mtf -> Latn @@ -1006,11 +1011,11 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x9DAD0000u, 46u}, // nnh -> Latn {0xA9AD0000u, 46u}, // nnk -> Latn {0xB1AD0000u, 46u}, // nnm -> Latn - {0xBDAD0000u, 93u}, // nnp -> Wcho + {0xBDAD0000u, 94u}, // nnp -> Wcho {0x6E6F0000u, 46u}, // no -> Latn {0x8DCD0000u, 44u}, // nod -> Lana {0x91CD0000u, 18u}, // noe -> Deva - {0xB5CD0000u, 71u}, // non -> Runr + {0xB5CD0000u, 72u}, // non -> Runr {0xBDCD0000u, 46u}, // nop -> Latn {0xD1CD0000u, 46u}, // nou -> Latn {0xBA0D0000u, 60u}, // nqo -> Nkoo @@ -1044,18 +1049,18 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB5AE0000u, 46u}, // onn -> Latn {0xC9AE0000u, 46u}, // ons -> Latn {0xB1EE0000u, 46u}, // opm -> Latn - {0x6F720000u, 64u}, // or -> Orya + {0x6F720000u, 65u}, // or -> Orya {0xBA2E0000u, 46u}, // oro -> Latn {0xD22E0000u, 1u}, // oru -> Arab {0x6F730000u, 17u}, // os -> Cyrl - {0x824E0000u, 65u}, // osa -> Osge + {0x824E0000u, 66u}, // osa -> Osge {0x826E0000u, 1u}, // ota -> Arab - {0xAA6E0000u, 63u}, // otk -> Orkh + {0xAA6E0000u, 64u}, // otk -> Orkh {0xB32E0000u, 46u}, // ozm -> Latn {0x70610000u, 27u}, // pa -> Guru {0x7061504Bu, 1u}, // pa-PK -> Arab {0x980F0000u, 46u}, // pag -> Latn - {0xAC0F0000u, 67u}, // pal -> Phli + {0xAC0F0000u, 68u}, // pal -> Phli {0xB00F0000u, 46u}, // pam -> Latn {0xBC0F0000u, 46u}, // pap -> Latn {0xD00F0000u, 46u}, // pau -> Latn @@ -1065,11 +1070,11 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x886F0000u, 46u}, // pdc -> Latn {0xCC6F0000u, 46u}, // pdt -> Latn {0x8C8F0000u, 46u}, // ped -> Latn - {0xB88F0000u, 94u}, // peo -> Xpeo + {0xB88F0000u, 95u}, // peo -> Xpeo {0xDC8F0000u, 46u}, // pex -> Latn {0xACAF0000u, 46u}, // pfl -> Latn {0xACEF0000u, 1u}, // phl -> Arab - {0xB4EF0000u, 68u}, // phn -> Phnx + {0xB4EF0000u, 69u}, // phn -> Phnx {0xAD0F0000u, 46u}, // pil -> Latn {0xBD0F0000u, 46u}, // pip -> Latn {0x814F0000u, 8u}, // pka -> Brah @@ -1105,7 +1110,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB4D10000u, 46u}, // rgn -> Latn {0x98F10000u, 1u}, // rhg -> Arab {0x81110000u, 46u}, // ria -> Latn - {0x95110000u, 87u}, // rif -> Tfng + {0x95110000u, 88u}, // rif -> Tfng {0x95114E4Cu, 46u}, // rif-NL -> Latn {0xC9310000u, 18u}, // rjs -> Deva {0xCD510000u, 7u}, // rkt -> Beng @@ -1135,9 +1140,9 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x9C120000u, 17u}, // sah -> Cyrl {0xC0120000u, 46u}, // saq -> Latn {0xC8120000u, 46u}, // sas -> Latn - {0xCC120000u, 46u}, // sat -> Latn + {0xCC120000u, 63u}, // sat -> Olck {0xD4120000u, 46u}, // sav -> Latn - {0xE4120000u, 74u}, // saz -> Saur + {0xE4120000u, 75u}, // saz -> Saur {0x80320000u, 46u}, // sba -> Latn {0x90320000u, 46u}, // sbe -> Latn {0xBC320000u, 46u}, // sbp -> Latn @@ -1161,11 +1166,11 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xD8D20000u, 20u}, // sgw -> Ethi {0xE4D20000u, 46u}, // sgz -> Latn {0x73680000u, 46u}, // sh -> Latn - {0xA0F20000u, 87u}, // shi -> Tfng + {0xA0F20000u, 88u}, // shi -> Tfng {0xA8F20000u, 46u}, // shk -> Latn {0xB4F20000u, 58u}, // shn -> Mymr {0xD0F20000u, 1u}, // shu -> Arab - {0x73690000u, 76u}, // si -> Sinh + {0x73690000u, 77u}, // si -> Sinh {0x8D120000u, 46u}, // sid -> Latn {0x99120000u, 46u}, // sig -> Latn {0xAD120000u, 46u}, // sil -> Latn @@ -1184,7 +1189,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x81920000u, 46u}, // sma -> Latn {0xA5920000u, 46u}, // smj -> Latn {0xB5920000u, 46u}, // smn -> Latn - {0xBD920000u, 72u}, // smp -> Samr + {0xBD920000u, 73u}, // smp -> Samr {0xC1920000u, 46u}, // smq -> Latn {0xC9920000u, 46u}, // sms -> Latn {0x736E0000u, 46u}, // sn -> Latn @@ -1194,10 +1199,10 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xDDB20000u, 46u}, // snx -> Latn {0xE1B20000u, 46u}, // sny -> Latn {0x736F0000u, 46u}, // so -> Latn - {0x99D20000u, 77u}, // sog -> Sogd + {0x99D20000u, 78u}, // sog -> Sogd {0xA9D20000u, 46u}, // sok -> Latn {0xC1D20000u, 46u}, // soq -> Latn - {0xD1D20000u, 89u}, // sou -> Thai + {0xD1D20000u, 90u}, // sou -> Thai {0xE1D20000u, 46u}, // soy -> Latn {0x8DF20000u, 46u}, // spd -> Latn {0xADF20000u, 46u}, // spl -> Latn @@ -1208,7 +1213,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x7372524Fu, 46u}, // sr-RO -> Latn {0x73725255u, 46u}, // sr-RU -> Latn {0x73725452u, 46u}, // sr-TR -> Latn - {0x86320000u, 78u}, // srb -> Sora + {0x86320000u, 79u}, // srb -> Sora {0xB6320000u, 46u}, // srn -> Latn {0xC6320000u, 46u}, // srr -> Latn {0xDE320000u, 18u}, // srx -> Deva @@ -1235,9 +1240,9 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB6F20000u, 46u}, // sxn -> Latn {0xDAF20000u, 46u}, // sxw -> Latn {0xAF120000u, 7u}, // syl -> Beng - {0xC7120000u, 80u}, // syr -> Syrc + {0xC7120000u, 81u}, // syr -> Syrc {0xAF320000u, 46u}, // szl -> Latn - {0x74610000u, 83u}, // ta -> Taml + {0x74610000u, 84u}, // ta -> Taml {0xA4130000u, 18u}, // taj -> Deva {0xAC130000u, 46u}, // tal -> Latn {0xB4130000u, 46u}, // tan -> Latn @@ -1251,11 +1256,11 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xE4330000u, 46u}, // tbz -> Latn {0xA0530000u, 46u}, // tci -> Latn {0xE0530000u, 42u}, // tcy -> Knda - {0x8C730000u, 81u}, // tdd -> Tale + {0x8C730000u, 82u}, // tdd -> Tale {0x98730000u, 18u}, // tdg -> Deva {0x9C730000u, 18u}, // tdh -> Deva {0xD0730000u, 46u}, // tdu -> Latn - {0x74650000u, 86u}, // te -> Telu + {0x74650000u, 87u}, // te -> Telu {0x8C930000u, 46u}, // ted -> Latn {0xB0930000u, 46u}, // tem -> Latn {0xB8930000u, 46u}, // teo -> Latn @@ -1266,7 +1271,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x88D30000u, 46u}, // tgc -> Latn {0xB8D30000u, 46u}, // tgo -> Latn {0xD0D30000u, 46u}, // tgu -> Latn - {0x74680000u, 89u}, // th -> Thai + {0x74680000u, 90u}, // th -> Thai {0xACF30000u, 18u}, // thl -> Deva {0xC0F30000u, 18u}, // thq -> Deva {0xC4F30000u, 18u}, // thr -> Deva @@ -1305,14 +1310,14 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x8E530000u, 25u}, // tsd -> Grek {0x96530000u, 18u}, // tsf -> Deva {0x9A530000u, 46u}, // tsg -> Latn - {0xA6530000u, 90u}, // tsj -> Tibt + {0xA6530000u, 91u}, // tsj -> Tibt {0xDA530000u, 46u}, // tsw -> Latn {0x74740000u, 17u}, // tt -> Cyrl {0x8E730000u, 46u}, // ttd -> Latn {0x92730000u, 46u}, // tte -> Latn {0xA6730000u, 46u}, // ttj -> Latn {0xC6730000u, 46u}, // ttr -> Latn - {0xCA730000u, 89u}, // tts -> Thai + {0xCA730000u, 90u}, // tts -> Thai {0xCE730000u, 46u}, // ttt -> Latn {0x9E930000u, 46u}, // tuh -> Latn {0xAE930000u, 46u}, // tul -> Latn @@ -1323,7 +1328,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xD2B30000u, 46u}, // tvu -> Latn {0x9ED30000u, 46u}, // twh -> Latn {0xC2D30000u, 46u}, // twq -> Latn - {0x9AF30000u, 84u}, // txg -> Tang + {0x9AF30000u, 85u}, // txg -> Tang {0x74790000u, 46u}, // ty -> Latn {0x83130000u, 46u}, // tya -> Latn {0xD7130000u, 17u}, // tyv -> Cyrl @@ -1333,7 +1338,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x75670000u, 1u}, // ug -> Arab {0x75674B5Au, 17u}, // ug-KZ -> Cyrl {0x75674D4Eu, 17u}, // ug-MN -> Cyrl - {0x80D40000u, 91u}, // uga -> Ugar + {0x80D40000u, 92u}, // uga -> Ugar {0x756B0000u, 17u}, // uk -> Cyrl {0xA1740000u, 46u}, // uli -> Latn {0x85940000u, 46u}, // umb -> Latn @@ -1346,6 +1351,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xCE340000u, 46u}, // urt -> Latn {0xDA340000u, 46u}, // urw -> Latn {0x82540000u, 46u}, // usa -> Latn + {0x9E740000u, 46u}, // uth -> Latn {0xC6740000u, 46u}, // utr -> Latn {0x9EB40000u, 46u}, // uvh -> Latn {0xAEB40000u, 46u}, // uvl -> Latn @@ -1353,7 +1359,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x757A4146u, 1u}, // uz-AF -> Arab {0x757A434Eu, 17u}, // uz-CN -> Cyrl {0x98150000u, 46u}, // vag -> Latn - {0xA0150000u, 92u}, // vai -> Vaii + {0xA0150000u, 93u}, // vai -> Vaii {0xB4150000u, 46u}, // van -> Latn {0x76650000u, 46u}, // ve -> Latn {0x88950000u, 46u}, // vec -> Latn @@ -1376,7 +1382,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xB4160000u, 46u}, // wan -> Latn {0xC4160000u, 46u}, // war -> Latn {0xBC360000u, 46u}, // wbp -> Latn - {0xC0360000u, 86u}, // wbq -> Telu + {0xC0360000u, 87u}, // wbq -> Telu {0xC4360000u, 18u}, // wbr -> Deva {0xA0560000u, 46u}, // wci -> Latn {0xC4960000u, 46u}, // wer -> Latn @@ -1418,9 +1424,9 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0xC5B70000u, 18u}, // xnr -> Deva {0x99D70000u, 46u}, // xog -> Latn {0xB5D70000u, 46u}, // xon -> Latn - {0xC5F70000u, 70u}, // xpr -> Prti + {0xC5F70000u, 71u}, // xpr -> Prti {0x86370000u, 46u}, // xrb -> Latn - {0x82570000u, 73u}, // xsa -> Sarb + {0x82570000u, 74u}, // xsa -> Sarb {0xA2570000u, 46u}, // xsi -> Latn {0xB2570000u, 46u}, // xsm -> Latn {0xC6570000u, 18u}, // xsr -> Deva @@ -1461,7 +1467,7 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x98190000u, 46u}, // zag -> Latn {0xA4790000u, 1u}, // zdj -> Arab {0x80990000u, 46u}, // zea -> Latn - {0x9CD90000u, 87u}, // zgh -> Tfng + {0x9CD90000u, 88u}, // zgh -> Tfng {0x7A680000u, 28u}, // zh -> Hans {0x7A684155u, 29u}, // zh-AU -> Hant {0x7A68424Eu, 29u}, // zh-BN -> Hant @@ -1470,7 +1476,6 @@ const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({ {0x7A68484Bu, 29u}, // zh-HK -> Hant {0x7A684944u, 29u}, // zh-ID -> Hant {0x7A684D4Fu, 29u}, // zh-MO -> Hant - {0x7A684D59u, 29u}, // zh-MY -> Hant {0x7A685041u, 29u}, // zh-PA -> Hant {0x7A685046u, 29u}, // zh-PF -> Hant {0x7A685048u, 29u}, // zh-PH -> Hant @@ -1592,6 +1597,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xD701434D4C61746ELLU, // byv_Latn_CM 0x93214D4C4C61746ELLU, // bze_Latn_ML 0x636145534C61746ELLU, // ca_Latn_ES + 0x8C0255534C61746ELLU, // cad_Latn_US 0x9C424E474C61746ELLU, // cch_Latn_NG 0xBC42424443616B6DLLU, // ccp_Cakm_BD 0x636552554379726CLLU, // ce_Cyrl_RU @@ -1627,6 +1633,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x637652554379726CLLU, // cv_Cyrl_RU 0x637947424C61746ELLU, // cy_Latn_GB 0x6461444B4C61746ELLU, // da_Latn_DK + 0x940343494C61746ELLU, // daf_Latn_CI 0xA80355534C61746ELLU, // dak_Latn_US 0xC40352554379726CLLU, // dar_Cyrl_RU 0xD4034B454C61746ELLU, // dav_Latn_KE @@ -1636,7 +1643,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xC4C343414C61746ELLU, // dgr_Latn_CA 0x91234E454C61746ELLU, // dje_Latn_NE 0xA5A343494C61746ELLU, // dnj_Latn_CI - 0xA1C3494E41726162LLU, // doi_Arab_IN + 0xA1C3494E44657661LLU, // doi_Deva_IN 0x9E23434E4D6F6E67LLU, // drh_Mong_CN 0x864344454C61746ELLU, // dsb_Latn_DE 0xB2634D4C4C61746ELLU, // dtm_Latn_ML @@ -1839,6 +1846,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0xC6AA49444C61746ELLU, // kvr_Latn_ID 0xDEAA504B41726162LLU, // kvx_Arab_PK 0x6B7747424C61746ELLU, // kw_Latn_GB + 0xAEEA494E44657661LLU, // kxl_Deva_IN 0xB2EA544854686169LLU, // kxm_Thai_TH 0xBEEA504B41726162LLU, // kxp_Arab_PK 0x6B79434E41726162LLU, // ky_Arab_CN @@ -2047,7 +2055,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x9C1252554379726CLLU, // sah_Cyrl_RU 0xC0124B454C61746ELLU, // saq_Latn_KE 0xC81249444C61746ELLU, // sas_Latn_ID - 0xCC12494E4C61746ELLU, // sat_Latn_IN + 0xCC12494E4F6C636BLLU, // sat_Olck_IN 0xD412534E4C61746ELLU, // sav_Latn_SN 0xE412494E53617572LLU, // saz_Saur_IN 0xBC32545A4C61746ELLU, // sbp_Latn_TZ @@ -2149,6 +2157,7 @@ std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({ 0x747254524C61746ELLU, // tr_Latn_TR 0xD23354524C61746ELLU, // tru_Latn_TR 0xD63354574C61746ELLU, // trv_Latn_TW + 0xDA33504B41726162LLU, // trw_Arab_PK 0x74735A414C61746ELLU, // ts_Latn_ZA 0x8E5347524772656BLLU, // tsd_Grek_GR 0x96534E5044657661LLU, // tsf_Deva_NP diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index bce70e2aae9e..223382731bc0 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -30,6 +30,7 @@ #include <memory> #include <set> #include <type_traits> +#include <vector> #include <android-base/macros.h> #include <androidfw/ByteBucketArray.h> @@ -1029,7 +1030,7 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ // But we don't want to hit the cache, so instead we will have a // local temporary allocation for the conversions. size_t convBufferLen = strLen + 4; - char16_t* convBuffer = (char16_t*)calloc(convBufferLen, sizeof(char16_t)); + std::vector<char16_t> convBuffer(convBufferLen); ssize_t l = 0; ssize_t h = mHeader->stringCount-1; @@ -1043,8 +1044,8 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ } if (s.has_value()) { char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()), - s->size(), convBuffer, convBufferLen); - c = strzcmp16(convBuffer, end-convBuffer, str, strLen); + s->size(), convBuffer.data(), convBufferLen); + c = strzcmp16(convBuffer.data(), end-convBuffer.data(), str, strLen); } if (kDebugStringPoolNoisy) { ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", @@ -1054,7 +1055,6 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ if (kDebugStringPoolNoisy) { ALOGI("MATCH!"); } - free(convBuffer); return mid; } else if (c < 0) { l = mid + 1; @@ -1062,7 +1062,6 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ h = mid - 1; } } - free(convBuffer); } else { // It is unusual to get the ID from an unsorted string block... // most often this happens because we want to get IDs for style diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index 158c3493a90c..2edd48c5a41b 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -102,12 +102,12 @@ bool SkiaDisplayList::prepareListAndChildren( bool hasBackwardProjectedNodesSubtree = false; for (auto& child : mChildNodes) { - hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); RenderNode* childNode = child.getRenderNode(); Matrix4 mat4(child.getRecordedMatrix()); info.damageAccumulator->pushTransform(&mat4); info.hasBackwardProjectedNodes = false; childFn(childNode, observer, info, functorsNeedLayer); + hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes; info.damageAccumulator->popTransform(); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index d67cf8c9c73f..621f2863fa03 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -301,7 +301,8 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch } sk_sp<SkImage> image = bitmap.makeImage(); - applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + applyLooper(get_looper(paint), *filterBitmap(std::move(filteredPaint)), + [&](SkScalar x, SkScalar y, const SkPaint& p) { mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), &p, bitmap.palette()); }); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 7dff0c2b9380..d22e97c231fd 100755 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5434,8 +5434,12 @@ public class AudioManager { public boolean setAdditionalOutputDeviceDelay( @NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) { Objects.requireNonNull(device); - // Implement the setter in r-dev or r-tv-dev as needed. - return false; + try { + return getService().setAdditionalOutputDeviceDelay( + new AudioDeviceAttributes(device), delayMillis); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -5450,8 +5454,11 @@ public class AudioManager { @IntRange(from = 0) public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { Objects.requireNonNull(device); - // Implement the getter in r-dev or r-tv-dev as needed. - return 0; + try { + return getService().getAdditionalOutputDeviceDelay(new AudioDeviceAttributes(device)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -5468,8 +5475,12 @@ public class AudioManager { @IntRange(from = 0) public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { Objects.requireNonNull(device); - // Implement the getter in r-dev or r-tv-dev as needed. - return 0; + try { + return getService().getMaxAdditionalOutputDeviceDelay( + new AudioDeviceAttributes(device)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index b2c2c4b1bbb4..d7ef4549ca3f 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1269,10 +1269,12 @@ public class AudioTrack extends PlayerBase // native code figure out the minimum buffer size. if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) { int bytesPerSample = 1; - try { - bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding()); - } catch (IllegalArgumentException e) { - // do nothing + if (AudioFormat.isEncodingLinearFrames(mFormat.getEncoding())) { + try { + bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding()); + } catch (IllegalArgumentException e) { + // do nothing + } } mBufferSizeInBytes = mFormat.getChannelCount() * bytesPerSample; } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index ebaa3162d0e4..ed48b569b166 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -330,4 +330,10 @@ interface IAudioService { oneway void unregisterCommunicationDeviceDispatcher( ICommunicationDeviceDispatcher dispatcher); + + boolean setAdditionalOutputDeviceDelay(in AudioDeviceAttributes device, long delayMillis); + + long getAdditionalOutputDeviceDelay(in AudioDeviceAttributes device); + + long getMaxAdditionalOutputDeviceDelay(in AudioDeviceAttributes device); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index 068f9689d06f..4b8a8adade1f 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -73,6 +73,8 @@ interface IMediaRouterService { void unregisterManager(IMediaRouter2Manager manager); void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, in MediaRoute2Info route, int volume); + void startScan(IMediaRouter2Manager manager); + void stopScan(IMediaRouter2Manager manager); void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId, in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route); diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 4b09a5f19fb0..68237de2ca98 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -147,6 +147,36 @@ public final class MediaRouter2Manager { } /** + * Starts scanning remote routes. + * @see #stopScan(String) + */ + public void startScan() { + Client client = getOrCreateClient(); + if (client != null) { + try { + mMediaRouterService.startScan(client); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to get sessions. Service probably died.", ex); + } + } + } + + /** + * Stops scanning remote routes to reduce resource consumption. + * @see #startScan(String) + */ + public void stopScan() { + Client client = getOrCreateClient(); + if (client != null) { + try { + mMediaRouterService.stopScan(client); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to get sessions. Service probably died.", ex); + } + } + } + + /** * Gets a {@link android.media.session.MediaController} associated with the * given routing session. * If there is no matching media session, {@code null} is returned. diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java index 68f2964dbeb2..2f952474b7f0 100644 --- a/media/java/android/media/RouteDiscoveryPreference.java +++ b/media/java/android/media/RouteDiscoveryPreference.java @@ -153,6 +153,7 @@ public final class RouteDiscoveryPreference implements Parcelable { return false; } RouteDiscoveryPreference other = (RouteDiscoveryPreference) o; + //TODO: Make this order-free return Objects.equals(mPreferredFeatures, other.mPreferredFeatures) && mShouldPerformActiveScan == other.mShouldPerformActiveScan; } diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index 1fbb67260895..5d7fdff70f5c 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -91,6 +91,8 @@ interface ITvInputManager { // For the recording session void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId); void stopRecording(in IBinder sessionToken, int userId); + void pauseRecording(in IBinder sessionToken, in Bundle params, int userId); + void resumeRecording(in IBinder sessionToken, in Bundle params, int userId); // For TV input hardware binding List<TvInputHardwareInfo> getHardwareList(); diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl index 24b87d50b33e..158cf211d9f0 100644 --- a/media/java/android/media/tv/ITvInputSession.aidl +++ b/media/java/android/media/tv/ITvInputSession.aidl @@ -58,4 +58,6 @@ oneway interface ITvInputSession { // For the recording session void startRecording(in Uri programUri, in Bundle params); void stopRecording(); + void pauseRecording(in Bundle params); + void resumeRecording(in Bundle params); } diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index e89d33d70d5c..abccf8da9cfc 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -68,6 +68,8 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 19; private static final int DO_START_RECORDING = 20; private static final int DO_STOP_RECORDING = 21; + private static final int DO_PAUSE_RECORDING = 22; + private static final int DO_RESUME_RECORDING = 23; private final boolean mIsRecordingSession; private final HandlerCaller mCaller; @@ -224,6 +226,14 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mTvInputRecordingSessionImpl.stopRecording(); break; } + case DO_PAUSE_RECORDING: { + mTvInputRecordingSessionImpl.pauseRecording((Bundle) msg.obj); + break; + } + case DO_RESUME_RECORDING: { + mTvInputRecordingSessionImpl.resumeRecording((Bundle) msg.obj); + break; + } default: { Log.w(TAG, "Unhandled message code: " + msg.what); break; @@ -363,6 +373,16 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_STOP_RECORDING)); } + @Override + public void pauseRecording(@Nullable Bundle params) { + mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_PAUSE_RECORDING, params)); + } + + @Override + public void resumeRecording(@Nullable Bundle params) { + mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RESUME_RECORDING, params)); + } + private final class TvInputEventReceiver extends InputEventReceiver { public TvInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java index b12f7c551288..0bedbd3c1f46 100644 --- a/media/java/android/media/tv/TvInputHardwareInfo.java +++ b/media/java/android/media/tv/TvInputHardwareInfo.java @@ -188,6 +188,20 @@ public final class TvInputHardwareInfo implements Parcelable { mCableConnectionStatus = source.readInt(); } + /** @hide */ + public Builder toBuilder() { + Builder newBuilder = new Builder() + .deviceId(mDeviceId) + .type(mType) + .audioType(mAudioType) + .audioAddress(mAudioAddress) + .cableConnectionStatus(mCableConnectionStatus); + if (mType == TV_INPUT_TYPE_HDMI) { + newBuilder.hdmiPortId(mHdmiPortId); + } + return newBuilder; + } + public static final class Builder { private Integer mDeviceId = null; private Integer mType = null; diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 195ad5bc10f9..54cb2bff5566 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -143,6 +143,7 @@ public final class TvInputInfo implements Parcelable { // Attributes from XML meta data. private final String mSetupActivity; private final boolean mCanRecord; + private final boolean mCanPauseRecording; private final int mTunerCount; // Attributes specific to HDMI @@ -264,8 +265,8 @@ public final class TvInputInfo implements Parcelable { private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput, CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected, - String setupActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo, - boolean isConnectedToHdmiSwitch, + String setupActivity, boolean canRecord, boolean canPauseRecording, int tunerCount, + HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, @HdmiAddressRelativePosition int hdmiConnectionRelativePosition, String parentId, Bundle extras) { mService = service; @@ -279,6 +280,7 @@ public final class TvInputInfo implements Parcelable { mIconDisconnected = iconDisconnected; mSetupActivity = setupActivity; mCanRecord = canRecord; + mCanPauseRecording = canPauseRecording; mTunerCount = tunerCount; mHdmiDeviceInfo = hdmiDeviceInfo; mIsConnectedToHdmiSwitch = isConnectedToHdmiSwitch; @@ -386,6 +388,14 @@ public final class TvInputInfo implements Parcelable { } /** + * Returns {@code true} if this TV input can pause recording TV programs, + * {@code false} otherwise. + */ + public boolean canPauseRecording() { + return mCanPauseRecording; + } + + /** * Returns domain-specific extras associated with this TV input. */ public Bundle getExtras() { @@ -571,6 +581,7 @@ public final class TvInputInfo implements Parcelable { && Objects.equals(mIconDisconnected, obj.mIconDisconnected) && TextUtils.equals(mSetupActivity, obj.mSetupActivity) && mCanRecord == obj.mCanRecord + && mCanPauseRecording == obj.mCanPauseRecording && mTunerCount == obj.mTunerCount && Objects.equals(mHdmiDeviceInfo, obj.mHdmiDeviceInfo) && mIsConnectedToHdmiSwitch == obj.mIsConnectedToHdmiSwitch @@ -606,6 +617,7 @@ public final class TvInputInfo implements Parcelable { dest.writeParcelable(mIconDisconnected, flags); dest.writeString(mSetupActivity); dest.writeByte(mCanRecord ? (byte) 1 : 0); + dest.writeByte(mCanPauseRecording ? (byte) 1 : 0); dest.writeInt(mTunerCount); dest.writeParcelable(mHdmiDeviceInfo, flags); dest.writeByte(mIsConnectedToHdmiSwitch ? (byte) 1 : 0); @@ -648,6 +660,7 @@ public final class TvInputInfo implements Parcelable { mIconDisconnected = in.readParcelable(null); mSetupActivity = in.readString(); mCanRecord = in.readByte() == 1; + mCanPauseRecording = in.readByte() == 1; mTunerCount = in.readInt(); mHdmiDeviceInfo = in.readParcelable(null); mIsConnectedToHdmiSwitch = in.readByte() == 1; @@ -695,6 +708,7 @@ public final class TvInputInfo implements Parcelable { private Icon mIconDisconnected; private String mSetupActivity; private Boolean mCanRecord; + private Boolean mCanPauseRecording; private Integer mTunerCount; private TvInputHardwareInfo mTvInputHardwareInfo; private HdmiDeviceInfo mHdmiDeviceInfo; @@ -879,6 +893,18 @@ public final class TvInputInfo implements Parcelable { } /** + * Sets whether this TV input can pause recording TV programs or not. + * + * @param canPauseRecording Whether this TV input can pause recording TV programs. + * @return This Builder object to allow for chaining of calls to builder methods. + */ + @NonNull + public Builder setCanPauseRecording(boolean canPauseRecording) { + this.mCanPauseRecording = canPauseRecording; + return this; + } + + /** * Sets domain-specific extras associated with this TV input. * * @param extras Domain-specific extras associated with this TV input. Keys <em>must</em> be @@ -927,7 +953,9 @@ public final class TvInputInfo implements Parcelable { parseServiceMetadata(type); return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId, mIcon, mIconStandby, mIconDisconnected, mSetupActivity, - mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount, + mCanRecord == null ? false : mCanRecord, + mCanPauseRecording == null ? false : mCanPauseRecording, + mTunerCount == null ? 0 : mTunerCount, mHdmiDeviceInfo, isConnectedToHdmiSwitch, hdmiConnectionRelativePosition, mParentId, mExtras); } @@ -997,6 +1025,12 @@ public final class TvInputInfo implements Parcelable { mTunerCount = sa.getInt( com.android.internal.R.styleable.TvInputService_tunerCount, 1); } + if (mCanPauseRecording == null) { + mCanPauseRecording = sa.getBoolean( + com.android.internal.R.styleable.TvInputService_canPauseRecording, + false); + } + sa.recycle(); } catch (IOException | XmlPullParserException e) { throw new IllegalStateException("Failed reading meta-data for " + si.packageName, e); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 98a01a4cb449..6341dc263efd 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -2476,6 +2476,40 @@ public final class TvInputManager { } /** + * Pauses TV program recording in the current recording session. + * + * @param params A set of extra parameters which might be handled with this event. + */ + void pauseRecording(@NonNull Bundle params) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.pauseRecording(mToken, params, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Resumes TV program recording in the current recording session. + * + * @param params A set of extra parameters which might be handled with this event. + */ + void resumeRecording(@NonNull Bundle params) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.resumeRecording(mToken, params, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle) * TvInputService.Session.appPrivateCommand()} on the current TvView. * diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index abbf4780bcc1..0fe9d504b951 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -1852,6 +1852,28 @@ public abstract class TvInputService extends Service { /** + * Called when the application requests to pause TV program recording. Recording must pause + * immediately when this method is called. + * + * If the pause request cannot be fulfilled, the session must call + * {@link #notifyError(int)}. + * + * @param params Domain-specific data for recording request. + */ + public void onPauseRecording(@NonNull Bundle params) { } + + /** + * Called when the application requests to resume TV program recording. Recording must + * resume immediately when this method is called. + * + * If the resume request cannot be fulfilled, the session must call + * {@link #notifyError(int)}. + * + * @param params Domain-specific data for recording request. + */ + public void onResumeRecording(@NonNull Bundle params) { } + + /** * Called when the application requests to release all the resources held by this recording * session. */ @@ -1903,6 +1925,22 @@ public abstract class TvInputService extends Service { } /** + * Calls {@link #onPauseRecording(Bundle)}. + * + */ + void pauseRecording(@NonNull Bundle params) { + onPauseRecording(params); + } + + /** + * Calls {@link #onResumeRecording(Bundle)}. + * + */ + void resumeRecording(@NonNull Bundle params) { + onResumeRecording(params); + } + + /** * Calls {@link #onAppPrivateCommand(String, Bundle)}. */ void appPrivateCommand(String action, Bundle data) { diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java index 23fadac8a72b..180e2bd6845b 100644 --- a/media/java/android/media/tv/TvRecordingClient.java +++ b/media/java/android/media/tv/TvRecordingClient.java @@ -30,6 +30,7 @@ import android.util.Log; import android.util.Pair; import java.util.ArrayDeque; +import java.util.Objects; import java.util.Queue; /** @@ -49,6 +50,8 @@ public class TvRecordingClient { private boolean mIsRecordingStarted; private boolean mIsTuned; + private boolean mIsPaused; + private boolean mIsRecordingStopping; private final Queue<Pair<String, Bundle>> mPendingAppPrivateCommands = new ArrayDeque<>(); /** @@ -113,17 +116,22 @@ public class TvRecordingClient { if (TextUtils.isEmpty(inputId)) { throw new IllegalArgumentException("inputId cannot be null or an empty string"); } - if (mIsRecordingStarted) { + if (mIsRecordingStarted && !mIsPaused) { throw new IllegalStateException("tune failed - recording already started"); } if (mSessionCallback != null && TextUtils.equals(mSessionCallback.mInputId, inputId)) { if (mSession != null) { + mSessionCallback.mChannelUri = channelUri; mSession.tune(channelUri, params); } else { mSessionCallback.mChannelUri = channelUri; mSessionCallback.mConnectionParams = params; } + mIsTuned = false; } else { + if (mIsPaused) { + throw new IllegalStateException("tune failed - inputId is changed during pause"); + } resetInternal(); mSessionCallback = new MySessionCallback(inputId, channelUri, params); if (mTvInputManager != null) { @@ -148,6 +156,8 @@ public class TvRecordingClient { mSession.release(); mIsTuned = false; mIsRecordingStarted = false; + mIsPaused = false; + mIsRecordingStopping = false; mSession = null; } } @@ -169,7 +179,8 @@ public class TvRecordingClient { * * @param programUri The URI for the TV program to record, built by * {@link TvContract#buildProgramUri(long)}. Can be {@code null}. - * @throws IllegalStateException If {@link #tune} request hasn't been handled yet. + * @throws IllegalStateException If {@link #tune} request hasn't been handled yet or during + * pause. */ public void startRecording(@Nullable Uri programUri) { startRecording(programUri, Bundle.EMPTY); @@ -195,11 +206,16 @@ public class TvRecordingClient { * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped * name, i.e. prefixed with a package name you own, so that different developers will * not create conflicting keys. - * @throws IllegalStateException If {@link #tune} request hasn't been handled yet. + * @throws IllegalStateException If {@link #tune} request hasn't been handled yet or during + * pause. */ public void startRecording(@Nullable Uri programUri, @NonNull Bundle params) { - if (!mIsTuned) { - throw new IllegalStateException("startRecording failed - not yet tuned"); + if (mIsRecordingStopping || !mIsTuned || mIsPaused) { + throw new IllegalStateException("startRecording failed -" + + "recording not yet stopped or not yet tuned or paused"); + } + if (mIsRecordingStarted) { + Log.w(TAG, "startRecording failed - recording already started"); } if (mSession != null) { mSession.startRecording(programUri, params); @@ -225,6 +241,103 @@ public class TvRecordingClient { } if (mSession != null) { mSession.stopRecording(); + if (mIsRecordingStarted) { + mIsRecordingStopping = true; + } + } + } + + /** + * Pause TV program recording in the current recording session. Recording is expected to pause + * immediately when this method is called. If recording has not yet started in the current + * recording session, this method does nothing. + * + * <p>In pause status, the application can tune during recording. To continue recording, + * please call {@link TvRecordingClient#resumeRecording()} to resume instead of + * {@link TvRecordingClient#startRecording(Uri)}. Application can stop + * the recording with {@link TvRecordingClient#stopRecording()} in recording pause status. + * + * <p>If the pause request cannot be fulfilled, the recording session will respond by calling + * {@link RecordingCallback#onError(int)}. + */ + public void pauseRecording() { + pauseRecording(Bundle.EMPTY); + } + + /** + * Pause TV program recording in the current recording session. Recording is expected to pause + * immediately when this method is called. If recording has not yet started in the current + * recording session, this method does nothing. + * + * <p>In pause status, the application can tune during recording. To continue recording, + * please call {@link TvRecordingClient#resumeRecording()} to resume instead of + * {@link TvRecordingClient#startRecording(Uri)}. Application can stop + * the recording with {@link TvRecordingClient#stopRecording()} in recording pause status. + * + * <p>If the pause request cannot be fulfilled, the recording session will respond by calling + * {@link RecordingCallback#onError(int)}. + * + * @param params Domain-specific data for this request. + */ + public void pauseRecording(@NonNull Bundle params) { + if (!mIsRecordingStarted || mIsRecordingStopping) { + throw new IllegalStateException( + "pauseRecording failed - recording not yet started or stopping"); + } + TvInputInfo info = mTvInputManager.getTvInputInfo(mSessionCallback.mInputId); + if (info == null || !info.canPauseRecording()) { + throw new UnsupportedOperationException( + "pauseRecording failed - operation not supported"); + } + if (mIsPaused) { + Log.w(TAG, "pauseRecording failed - recording already paused"); + } + if (mSession != null) { + mSession.pauseRecording(params); + mIsPaused = true; + } + } + + /** + * Resume TV program recording only in recording pause status in the current recording session. + * Recording is expected to resume immediately when this method is called. If recording has not + * yet paused in the current recording session, this method does nothing. + * + * <p>When record is resumed, the recording is continue and can not re-tune. Application can + * stop the recording with {@link TvRecordingClient#stopRecording()} after record resumed. + * + * <p>If the pause request cannot be fulfilled, the recording session will respond by calling + * {@link RecordingCallback#onError(int)}. + */ + public void resumeRecording() { + resumeRecording(Bundle.EMPTY); + } + + /** + * Resume TV program recording only in recording pause status in the current recording session. + * Recording is expected to resume immediately when this method is called. If recording has not + * yet paused in the current recording session, this method does nothing. + * + * <p>When record is resumed, the recording is continues and can not re-tune. Application can + * stop the recording with {@link TvRecordingClient#stopRecording()} after record resumed. + * + * <p>If the resume request cannot be fulfilled, the recording session will respond by calling + * {@link RecordingCallback#onError(int)}. + * + * @param params Domain-specific data for this request. + */ + public void resumeRecording(@NonNull Bundle params) { + if (!mIsRecordingStarted || mIsRecordingStopping || !mIsTuned) { + throw new IllegalStateException( + "resumeRecording failed - recording not yet started or stopping or " + + "not yet tuned"); + } + if (!mIsPaused) { + Log.w(TAG, "resumeRecording failed - recording not yet paused"); + } + if (mSession != null) { + mSession.resumeRecording(params); + mIsPaused = false; } } @@ -367,6 +480,10 @@ public class TvRecordingClient { Log.w(TAG, "onTuned - session not created"); return; } + if (mIsTuned || !Objects.equals(mChannelUri, channelUri)) { + Log.w(TAG, "onTuned - already tuned or not yet tuned to last channel"); + return; + } mIsTuned = true; mCallback.onTuned(channelUri); } @@ -382,6 +499,8 @@ public class TvRecordingClient { } mIsTuned = false; mIsRecordingStarted = false; + mIsPaused = false; + mIsRecordingStopping = false; mSessionCallback = null; mSession = null; if (mCallback != null) { @@ -398,7 +517,13 @@ public class TvRecordingClient { Log.w(TAG, "onRecordingStopped - session not created"); return; } + if (!mIsRecordingStarted) { + Log.w(TAG, "onRecordingStopped - recording not yet started"); + return; + } mIsRecordingStarted = false; + mIsPaused = false; + mIsRecordingStopping = false; mCallback.onRecordingStopped(recordedProgramUri); } diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java index 887116725961..c4b622d0fba9 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java +++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java @@ -47,6 +47,7 @@ public class DvrRecorder implements AutoCloseable { private static int sInstantId = 0; private int mSegmentId = 0; private int mOverflow; + private Boolean mIsStopped = null; private native int nativeAttachFilter(Filter filter); private native int nativeDetachFilter(Filter filter); @@ -135,7 +136,13 @@ public class DvrRecorder implements AutoCloseable { .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD, FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0); - return nativeStartDvr(); + synchronized (mIsStopped) { + int result = nativeStartDvr(); + if (result == Tuner.RESULT_SUCCESS) { + mIsStopped = false; + } + return result; + } } /** @@ -152,7 +159,13 @@ public class DvrRecorder implements AutoCloseable { .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD, FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mOverflow); - return nativeStopDvr(); + synchronized (mIsStopped) { + int result = nativeStopDvr(); + if (result == Tuner.RESULT_SUCCESS) { + mIsStopped = true; + } + return result; + } } /** @@ -164,7 +177,13 @@ public class DvrRecorder implements AutoCloseable { */ @Result public int flush() { - return nativeFlushDvr(); + synchronized (mIsStopped) { + if (mIsStopped) { + return nativeFlushDvr(); + } + Log.w(TAG, "Cannot flush non-stopped Record DVR."); + return Tuner.RESULT_INVALID_STATE; + } } /** diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 5daf8b0f88f8..694b93919cde 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -169,8 +169,9 @@ static fields_t gFields; static int IP_V4_LENGTH = 4; static int IP_V6_LENGTH = 16; -void DestroyCallback(const C2Buffer * /* buf */, void *arg) { +void DestroyCallback(const C2Buffer * buf, void *arg) { android::sp<android::MediaEvent> event = (android::MediaEvent *)arg; + android::Mutex::Autolock autoLock(event->mLock); if (event->mLinearBlockObj != NULL) { JNIEnv *env = android::AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(event->mLinearBlockObj); @@ -179,6 +180,7 @@ void DestroyCallback(const C2Buffer * /* buf */, void *arg) { event->mAvHandleRefCnt--; event->finalize(); + event->decStrong(buf); } namespace android { @@ -369,6 +371,7 @@ jobject MediaEvent::getLinearBlock() { pC2Buffer->setInfo(info); } pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this); + incStrong(pC2Buffer.get()); jobject linearBlock = env->NewObject( env->FindClass("android/media/MediaCodec$LinearBlock"), @@ -3646,6 +3649,7 @@ static jobject android_media_tv_Tuner_media_event_get_linear_block( ALOGD("Failed get MediaEvent"); return NULL; } + android::Mutex::Autolock autoLock(mediaEventSp->mLock); return mediaEventSp->getLinearBlock(); } diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h index 0f930b5443e4..742db34b74a7 100644 --- a/media/native/midi/include/amidi/AMidi.h +++ b/media/native/midi/include/amidi/AMidi.h @@ -61,8 +61,6 @@ enum { AMIDI_DEVICE_TYPE_BLUETOOTH = 3 /* A MIDI device connected via BlueTooth */ }; -#if __ANDROID_API__ >= 29 - /* * Device API */ @@ -249,8 +247,6 @@ media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPor */ void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29); -#endif /* __ANDROID_API__ >= 29 */ - #ifdef __cplusplus } #endif diff --git a/apex/permission/testing/Android.bp b/packages/Connectivity/framework/Android.bp index 63bf0a08e956..8db8d7699a1e 100644 --- a/apex/permission/testing/Android.bp +++ b/packages/Connectivity/framework/Android.bp @@ -1,25 +1,29 @@ -// Copyright (C) 2019 The Android Open Source Project +// +// 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 +// 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. +// -apex_test { - name: "test_com.android.permission", +// TODO: use a java_library in the bootclasspath instead +filegroup { + name: "framework-connectivity-sources", + srcs: [ + "src/**/*.java", + "src/**/*.aidl", + ], + path: "src", visibility: [ - "//system/apex/tests", + "//frameworks/base", + "//packages/modules/Connectivity:__subpackages__", ], - defaults: ["com.android.permission-defaults"], - manifest: "test_manifest.json", - file_contexts: ":com.android.permission-file_contexts", - // Test APEX, should never be installed - installable: false, -} +}
\ No newline at end of file diff --git a/core/java/android/net/CaptivePortal.java b/packages/Connectivity/framework/src/android/net/CaptivePortal.java index 269bbf20c8b1..269bbf20c8b1 100644 --- a/core/java/android/net/CaptivePortal.java +++ b/packages/Connectivity/framework/src/android/net/CaptivePortal.java diff --git a/core/java/android/net/CaptivePortalData.aidl b/packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl index 1d57ee759136..1d57ee759136 100644 --- a/core/java/android/net/CaptivePortalData.aidl +++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.aidl diff --git a/core/java/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java index c443c7500f1d..18467fad8ec4 100644 --- a/core/java/android/net/CaptivePortalData.java +++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java @@ -39,9 +39,11 @@ public final class CaptivePortalData implements Parcelable { private final long mByteLimit; private final long mExpiryTimeMillis; private final boolean mCaptive; + private final String mVenueFriendlyName; private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl, - boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) { + boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive, + String venueFriendlyName) { mRefreshTimeMillis = refreshTimeMillis; mUserPortalUrl = userPortalUrl; mVenueInfoUrl = venueInfoUrl; @@ -49,11 +51,12 @@ public final class CaptivePortalData implements Parcelable { mByteLimit = byteLimit; mExpiryTimeMillis = expiryTimeMillis; mCaptive = captive; + mVenueFriendlyName = venueFriendlyName; } private CaptivePortalData(Parcel p) { this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(), - p.readLong(), p.readLong(), p.readBoolean()); + p.readLong(), p.readLong(), p.readBoolean(), p.readString()); } @Override @@ -70,6 +73,7 @@ public final class CaptivePortalData implements Parcelable { dest.writeLong(mByteLimit); dest.writeLong(mExpiryTimeMillis); dest.writeBoolean(mCaptive); + dest.writeString(mVenueFriendlyName); } /** @@ -83,6 +87,7 @@ public final class CaptivePortalData implements Parcelable { private long mBytesRemaining = -1; private long mExpiryTime = -1; private boolean mCaptive; + private String mVenueFriendlyName; /** * Create an empty builder. @@ -100,7 +105,8 @@ public final class CaptivePortalData implements Parcelable { .setSessionExtendable(data.mIsSessionExtendable) .setBytesRemaining(data.mByteLimit) .setExpiryTime(data.mExpiryTimeMillis) - .setCaptive(data.mCaptive); + .setCaptive(data.mCaptive) + .setVenueFriendlyName(data.mVenueFriendlyName); } /** @@ -167,12 +173,22 @@ public final class CaptivePortalData implements Parcelable { } /** + * Set the venue friendly name. + */ + @NonNull + public Builder setVenueFriendlyName(@Nullable String venueFriendlyName) { + mVenueFriendlyName = venueFriendlyName; + return this; + } + + /** * Create a new {@link CaptivePortalData}. */ @NonNull public CaptivePortalData build() { return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl, - mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive); + mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive, + mVenueFriendlyName); } } @@ -232,6 +248,14 @@ public final class CaptivePortalData implements Parcelable { return mCaptive; } + /** + * Get the venue friendly name + */ + @Nullable + public String getVenueFriendlyName() { + return mVenueFriendlyName; + } + @NonNull public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() { @Override @@ -248,7 +272,7 @@ public final class CaptivePortalData implements Parcelable { @Override public int hashCode() { return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl, - mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive); + mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName); } @Override @@ -261,7 +285,8 @@ public final class CaptivePortalData implements Parcelable { && mIsSessionExtendable == other.mIsSessionExtendable && mByteLimit == other.mByteLimit && mExpiryTimeMillis == other.mExpiryTimeMillis - && mCaptive == other.mCaptive; + && mCaptive == other.mCaptive + && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName); } @Override @@ -274,6 +299,7 @@ public final class CaptivePortalData implements Parcelable { + ", byteLimit: " + mByteLimit + ", expiryTime: " + mExpiryTimeMillis + ", captive: " + mCaptive + + ", venueFriendlyName: " + mVenueFriendlyName + "}"; } } diff --git a/core/java/android/net/ConnectionInfo.aidl b/packages/Connectivity/framework/src/android/net/ConnectionInfo.aidl index 07faf8bbbed8..07faf8bbbed8 100644 --- a/core/java/android/net/ConnectionInfo.aidl +++ b/packages/Connectivity/framework/src/android/net/ConnectionInfo.aidl diff --git a/core/java/android/net/ConnectionInfo.java b/packages/Connectivity/framework/src/android/net/ConnectionInfo.java index 4514a8484d96..4514a8484d96 100644 --- a/core/java/android/net/ConnectionInfo.java +++ b/packages/Connectivity/framework/src/android/net/ConnectionInfo.java diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.aidl b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl index 82ba0ca113c5..82ba0ca113c5 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.aidl +++ b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.aidl diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java index 523449497345..523449497345 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java new file mode 100644 index 000000000000..9afa5d1311c5 --- /dev/null +++ b/packages/Connectivity/framework/src/android/net/ConnectivityFrameworkInitializer.java @@ -0,0 +1,83 @@ +/* + * 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 android.net; + +import android.annotation.SystemApi; +import android.app.SystemServiceRegistry; +import android.content.Context; + +/** + * Class for performing registration for all core connectivity services. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class ConnectivityFrameworkInitializer { + private ConnectivityFrameworkInitializer() {} + + /** + * Called by {@link SystemServiceRegistry}'s static initializer and registers all core + * connectivity services to {@link Context}, so that {@link Context#getSystemService} can + * return them. + * + * @throws IllegalStateException if this is called anywhere besides + * {@link SystemServiceRegistry}. + */ + public static void registerServiceWrappers() { + // registerContextAwareService will throw if this is called outside of SystemServiceRegistry + // initialization. + SystemServiceRegistry.registerContextAwareService( + Context.CONNECTIVITY_SERVICE, + ConnectivityManager.class, + (context, serviceBinder) -> { + IConnectivityManager icm = IConnectivityManager.Stub.asInterface(serviceBinder); + return new ConnectivityManager(context, icm); + } + ); + + // TODO: move outside of the connectivity JAR + SystemServiceRegistry.registerContextAwareService( + Context.VPN_MANAGEMENT_SERVICE, + VpnManager.class, + (context) -> { + final ConnectivityManager cm = context.getSystemService( + ConnectivityManager.class); + return cm.createVpnManager(); + } + ); + + SystemServiceRegistry.registerContextAwareService( + Context.CONNECTIVITY_DIAGNOSTICS_SERVICE, + ConnectivityDiagnosticsManager.class, + (context) -> { + final ConnectivityManager cm = context.getSystemService( + ConnectivityManager.class); + return cm.createDiagnosticsManager(); + } + ); + + SystemServiceRegistry.registerContextAwareService( + Context.TEST_NETWORK_SERVICE, + TestNetworkManager.class, + context -> { + final ConnectivityManager cm = context.getSystemService( + ConnectivityManager.class); + return cm.startOrGetTestNetworkManager(); + } + ); + } +} diff --git a/core/java/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 06c159804a45..987dcc4898b5 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -15,7 +15,13 @@ */ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import static android.net.IpSecManager.INVALID_RESOURCE_ID; +import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; +import static android.net.NetworkRequest.Type.LISTEN; +import static android.net.NetworkRequest.Type.REQUEST; +import static android.net.NetworkRequest.Type.TRACK_DEFAULT; +import static android.net.QosCallback.QosCallbackRegistrationException; import android.annotation.CallbackExecutor; import android.annotation.IntDef; @@ -24,9 +30,9 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -69,7 +75,6 @@ import com.android.internal.util.Protocol; import libcore.net.event.NetworkEventDispatcher; -import java.io.FileDescriptor; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.annotation.Retention; @@ -1955,6 +1960,12 @@ public class ConnectivityManager { return k; } + // Construct an invalid fd. + private ParcelFileDescriptor createInvalidFd() { + final int invalidFd = -1; + return ParcelFileDescriptor.adoptFd(invalidFd); + } + /** * Request that keepalives be started on a IPsec NAT-T socket. * @@ -1985,7 +1996,7 @@ public class ConnectivityManager { } catch (IOException ignored) { // Construct an invalid fd, so that if the user later calls start(), it will fail with // ERROR_INVALID_SOCKET. - dup = new ParcelFileDescriptor(new FileDescriptor()); + dup = createInvalidFd(); } return new NattSocketKeepalive(mService, network, dup, socket.getResourceId(), source, destination, executor, callback); @@ -2027,7 +2038,7 @@ public class ConnectivityManager { } catch (IOException ignored) { // Construct an invalid fd, so that if the user later calls start(), it will fail with // ERROR_INVALID_SOCKET. - dup = new ParcelFileDescriptor(new FileDescriptor()); + dup = createInvalidFd(); } return new NattSocketKeepalive(mService, network, dup, INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback); @@ -2064,7 +2075,7 @@ public class ConnectivityManager { } catch (UncheckedIOException ignored) { // Construct an invalid fd, so that if the user later calls start(), it will fail with // ERROR_INVALID_SOCKET. - dup = new ParcelFileDescriptor(new FileDescriptor()); + dup = createInvalidFd(); } return new TcpSocketKeepalive(mService, network, dup, executor, callback); } @@ -3730,14 +3741,12 @@ public class ConnectivityManager { private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>(); private static CallbackHandler sCallbackHandler; - private static final int LISTEN = 1; - private static final int REQUEST = 2; - private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, - int timeoutMs, int action, int legacyType, CallbackHandler handler) { + int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) { printStackTrace(); checkCallbackNotNull(callback); - Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities"); + Preconditions.checkArgument( + reqType == TRACK_DEFAULT || need != null, "null NetworkCapabilities"); final NetworkRequest request; final String callingPackageName = mContext.getOpPackageName(); try { @@ -3750,13 +3759,13 @@ public class ConnectivityManager { } Messenger messenger = new Messenger(handler); Binder binder = new Binder(); - if (action == LISTEN) { + if (reqType == LISTEN) { request = mService.listenForNetwork( need, messenger, binder, callingPackageName); } else { request = mService.requestNetwork( - need, messenger, timeoutMs, binder, legacyType, callingPackageName, - getAttributionTag()); + need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType, + callingPackageName, getAttributionTag()); } if (request != null) { sCallbacks.put(request, callback); @@ -4260,7 +4269,7 @@ public class ConnectivityManager { // request, i.e., the system default network. CallbackHandler cbHandler = new CallbackHandler(handler); sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0, - REQUEST, TYPE_NONE, cbHandler); + TRACK_DEFAULT, TYPE_NONE, cbHandler); } /** @@ -4814,9 +4823,33 @@ public class ConnectivityManager { } } + /** @hide */ + public TestNetworkManager startOrGetTestNetworkManager() { + final IBinder tnBinder; + try { + tnBinder = mService.startOrGetTestNetworkService(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder)); + } + + /** @hide */ + public VpnManager createVpnManager() { + return new VpnManager(mContext, mService); + } + + /** @hide */ + public ConnectivityDiagnosticsManager createDiagnosticsManager() { + return new ConnectivityDiagnosticsManager(mContext, mService); + } + /** * Simulates a Data Stall for the specified Network. * + * <p>This method should only be used for tests. + * * <p>The caller must be the owner of the specified Network. * * @param detectionMethod The detection method used to identify the Data Stall. @@ -4826,7 +4859,7 @@ public class ConnectivityManager { * @throws SecurityException if the caller is not the owner of the given network. * @hide */ - @TestApi + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int detectionMethod, long timestampMillis, @@ -4842,4 +4875,206 @@ public class ConnectivityManager { Log.d(TAG, "setOemNetworkPreference called with preference: " + preference.toString()); } + + @NonNull + private final List<QosCallbackConnection> mQosCallbackConnections = new ArrayList<>(); + + /** + * Registers a {@link QosSocketInfo} with an associated {@link QosCallback}. The callback will + * receive available QoS events related to the {@link Network} and local ip + port + * specified within socketInfo. + * <p/> + * The same {@link QosCallback} must be unregistered before being registered a second time, + * otherwise {@link QosCallbackRegistrationException} is thrown. + * <p/> + * This API does not, in itself, require any permission if called with a network that is not + * restricted. However, the underlying implementation currently only supports the IMS network, + * which is always restricted. That means non-preinstalled callers can't possibly find this API + * useful, because they'd never be called back on networks that they would have access to. + * + * @throws SecurityException if {@link QosSocketInfo#getNetwork()} is restricted and the app is + * missing CONNECTIVITY_USE_RESTRICTED_NETWORKS permission. + * @throws QosCallback.QosCallbackRegistrationException if qosCallback is already registered. + * @throws RuntimeException if the app already has too many callbacks registered. + * + * Exceptions after the time of registration is passed through + * {@link QosCallback#onError(QosCallbackException)}. see: {@link QosCallbackException}. + * + * @param socketInfo the socket information used to match QoS events + * @param callback receives qos events that satisfy socketInfo + * @param executor The executor on which the callback will be invoked. The provided + * {@link Executor} must run callback sequentially, otherwise the order of + * callbacks cannot be guaranteed. + * + * @hide + */ + @SystemApi + public void registerQosCallback(@NonNull final QosSocketInfo socketInfo, + @NonNull final QosCallback callback, + @CallbackExecutor @NonNull final Executor executor) { + Objects.requireNonNull(socketInfo, "socketInfo must be non-null"); + Objects.requireNonNull(callback, "callback must be non-null"); + Objects.requireNonNull(executor, "executor must be non-null"); + + try { + synchronized (mQosCallbackConnections) { + if (getQosCallbackConnection(callback) == null) { + final QosCallbackConnection connection = + new QosCallbackConnection(this, callback, executor); + mQosCallbackConnections.add(connection); + mService.registerQosSocketCallback(socketInfo, connection); + } else { + Log.e(TAG, "registerQosCallback: Callback already registered"); + throw new QosCallbackRegistrationException(); + } + } + } catch (final RemoteException e) { + Log.e(TAG, "registerQosCallback: Error while registering ", e); + + // The same unregister method method is called for consistency even though nothing + // will be sent to the ConnectivityService since the callback was never successfully + // registered. + unregisterQosCallback(callback); + e.rethrowFromSystemServer(); + } catch (final ServiceSpecificException e) { + Log.e(TAG, "registerQosCallback: Error while registering ", e); + unregisterQosCallback(callback); + throw convertServiceException(e); + } + } + + /** + * Unregisters the given {@link QosCallback}. The {@link QosCallback} will no longer receive + * events once unregistered and can be registered a second time. + * <p/> + * If the {@link QosCallback} does not have an active registration, it is a no-op. + * + * @param callback the callback being unregistered + * + * @hide + */ + @SystemApi + public void unregisterQosCallback(@NonNull final QosCallback callback) { + Objects.requireNonNull(callback, "The callback must be non-null"); + try { + synchronized (mQosCallbackConnections) { + final QosCallbackConnection connection = getQosCallbackConnection(callback); + if (connection != null) { + connection.stopReceivingMessages(); + mService.unregisterQosCallback(connection); + mQosCallbackConnections.remove(connection); + } else { + Log.d(TAG, "unregisterQosCallback: Callback not registered"); + } + } + } catch (final RemoteException e) { + Log.e(TAG, "unregisterQosCallback: Error while unregistering ", e); + e.rethrowFromSystemServer(); + } + } + + /** + * Gets the connection related to the callback. + * + * @param callback the callback to look up + * @return the related connection + */ + @Nullable + private QosCallbackConnection getQosCallbackConnection(final QosCallback callback) { + for (final QosCallbackConnection connection : mQosCallbackConnections) { + // Checking by reference here is intentional + if (connection.getCallback() == callback) { + return connection; + } + } + return null; + } + + /** + * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but + * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can + * be used to request that the system provide a network without causing the network to be + * in the foreground. + * + * <p>This method will attempt to find the best network that matches the passed + * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the + * criteria. The platform will evaluate which network is the best at its own discretion. + * Throughput, latency, cost per byte, policy, user preference and other considerations + * may be factored in the decision of what is considered the best network. + * + * <p>As long as this request is outstanding, the platform will try to maintain the best network + * matching this request, while always attempting to match the request to a better network if + * possible. If a better match is found, the platform will switch this request to the now-best + * network and inform the app of the newly best network by invoking + * {@link NetworkCallback#onAvailable(Network)} on the provided callback. Note that the platform + * will not try to maintain any other network than the best one currently matching the request: + * a network not matching any network request may be disconnected at any time. + * + * <p>For example, an application could use this method to obtain a connected cellular network + * even if the device currently has a data connection over Ethernet. This may cause the cellular + * radio to consume additional power. Or, an application could inform the system that it wants + * a network supporting sending MMSes and have the system let it know about the currently best + * MMS-supporting network through the provided {@link NetworkCallback}. + * + * <p>The status of the request can be followed by listening to the various callbacks described + * in {@link NetworkCallback}. The {@link Network} object passed to the callback methods can be + * used to direct traffic to the network (although accessing some networks may be subject to + * holding specific permissions). Callers will learn about the specific characteristics of the + * network through + * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} and + * {@link NetworkCallback#onLinkPropertiesChanged(Network, LinkProperties)}. The methods of the + * provided {@link NetworkCallback} will only be invoked due to changes in the best network + * matching the request at any given time; therefore when a better network matching the request + * becomes available, the {@link NetworkCallback#onAvailable(Network)} method is called + * with the new network after which no further updates are given about the previously-best + * network, unless it becomes the best again at some later time. All callbacks are invoked + * in order on the same thread, which by default is a thread created by the framework running + * in the app. + * + * <p>This{@link NetworkRequest} will live until released via + * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits, at + * which point the system may let go of the network at any time. + * + * <p>It is presently unsupported to request a network with mutable + * {@link NetworkCapabilities} such as + * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or + * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL} + * as these {@code NetworkCapabilities} represent states that a particular + * network may never attain, and whether a network will attain these states + * is unknown prior to bringing up the network so the framework does not + * know how to go about satisfying a request with these capabilities. + * + * <p>To avoid performance issues due to apps leaking callbacks, the system will limit the + * number of outstanding requests to 100 per app (identified by their UID), shared with + * all variants of this method, of {@link #registerNetworkCallback} as well as + * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}. + * Requesting a network with this method will count toward this limit. If this limit is + * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources, + * make sure to unregister the callbacks with + * {@link #unregisterNetworkCallback(NetworkCallback)}. + * + * @param request {@link NetworkRequest} describing this request. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * If null, the callback is invoked on the default internal Handler. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * @throws IllegalArgumentException if {@code request} contains invalid network capabilities. + * @throws SecurityException if missing the appropriate permissions. + * @throws RuntimeException if the app already has too many callbacks registered. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @SuppressLint("ExecutorRegistration") + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK + }) + public void requestBackgroundNetwork(@NonNull NetworkRequest request, + @Nullable Handler handler, @NonNull NetworkCallback networkCallback) { + final NetworkCapabilities nc = request.networkCapabilities; + sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST, + TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler)); + } } diff --git a/core/java/android/net/ConnectivityMetricsEvent.aidl b/packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl index 1c541dc4c8cc..1c541dc4c8cc 100644 --- a/core/java/android/net/ConnectivityMetricsEvent.aidl +++ b/packages/Connectivity/framework/src/android/net/ConnectivityMetricsEvent.aidl diff --git a/core/java/android/net/ConnectivityThread.java b/packages/Connectivity/framework/src/android/net/ConnectivityThread.java index 0b218e738b77..0b218e738b77 100644 --- a/core/java/android/net/ConnectivityThread.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityThread.java diff --git a/core/java/android/net/DhcpInfo.aidl b/packages/Connectivity/framework/src/android/net/DhcpInfo.aidl index 29cd21fe7652..29cd21fe7652 100644 --- a/core/java/android/net/DhcpInfo.aidl +++ b/packages/Connectivity/framework/src/android/net/DhcpInfo.aidl diff --git a/core/java/android/net/DhcpInfo.java b/packages/Connectivity/framework/src/android/net/DhcpInfo.java index 912df67a0b45..912df67a0b45 100644 --- a/core/java/android/net/DhcpInfo.java +++ b/packages/Connectivity/framework/src/android/net/DhcpInfo.java diff --git a/core/java/android/net/DnsResolver.java b/packages/Connectivity/framework/src/android/net/DnsResolver.java index 3f7660f5709a..3f7660f5709a 100644 --- a/core/java/android/net/DnsResolver.java +++ b/packages/Connectivity/framework/src/android/net/DnsResolver.java diff --git a/core/java/android/net/ICaptivePortal.aidl b/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl index fe21905c7002..fe21905c7002 100644 --- a/core/java/android/net/ICaptivePortal.aidl +++ b/packages/Connectivity/framework/src/android/net/ICaptivePortal.aidl diff --git a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl index 82b64a928000..82b64a928000 100644 --- a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityDiagnosticsCallback.aidl diff --git a/core/java/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index b32c98b49cfc..1b4d2e413943 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -20,6 +20,8 @@ import android.app.PendingIntent; import android.net.ConnectionInfo; import android.net.ConnectivityDiagnosticsManager; import android.net.IConnectivityDiagnosticsCallback; +import android.net.IQosCallback; +import android.net.ISocketKeepaliveCallback; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkAgentConfig; @@ -27,9 +29,9 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkState; -import android.net.ISocketKeepaliveCallback; import android.net.ProxyInfo; import android.net.UidRange; +import android.net.QosSocketInfo; import android.os.Bundle; import android.os.IBinder; import android.os.INetworkActivityListener; @@ -41,7 +43,6 @@ import android.os.ResultReceiver; import com.android.connectivity.aidl.INetworkAgent; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; -import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnProfile; /** @@ -167,7 +168,7 @@ interface IConnectivityManager in NetworkCapabilities nc, int score, in NetworkAgentConfig config, in int factorySerialNumber); - NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, + NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType, in Messenger messenger, int timeoutSec, in IBinder binder, int legacy, String callingPackageName, String callingAttributionTag); @@ -206,11 +207,11 @@ interface IConnectivityManager void startNattKeepalive(in Network network, int intervalSeconds, in ISocketKeepaliveCallback cb, String srcAddr, int srcPort, String dstAddr); - void startNattKeepaliveWithFd(in Network network, in FileDescriptor fd, int resourceId, + void startNattKeepaliveWithFd(in Network network, in ParcelFileDescriptor pfd, int resourceId, int intervalSeconds, in ISocketKeepaliveCallback cb, String srcAddr, String dstAddr); - void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds, + void startTcpKeepalive(in Network network, in ParcelFileDescriptor pfd, int intervalSeconds, in ISocketKeepaliveCallback cb); void stopKeepalive(in Network network, int slot); @@ -239,4 +240,7 @@ interface IConnectivityManager void unregisterNetworkActivityListener(in INetworkActivityListener l); boolean isDefaultNetworkActive(); + + void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback); + void unregisterQosCallback(in IQosCallback callback); } diff --git a/core/java/android/net/ISocketKeepaliveCallback.aidl b/packages/Connectivity/framework/src/android/net/ISocketKeepaliveCallback.aidl index 020fbcacbfef..020fbcacbfef 100644 --- a/core/java/android/net/ISocketKeepaliveCallback.aidl +++ b/packages/Connectivity/framework/src/android/net/ISocketKeepaliveCallback.aidl diff --git a/core/java/android/net/ITestNetworkManager.aidl b/packages/Connectivity/framework/src/android/net/ITestNetworkManager.aidl index 2a863adde581..2a863adde581 100644 --- a/core/java/android/net/ITestNetworkManager.aidl +++ b/packages/Connectivity/framework/src/android/net/ITestNetworkManager.aidl diff --git a/core/java/android/net/InetAddresses.java b/packages/Connectivity/framework/src/android/net/InetAddresses.java index 01b795e456fa..01b795e456fa 100644 --- a/core/java/android/net/InetAddresses.java +++ b/packages/Connectivity/framework/src/android/net/InetAddresses.java diff --git a/core/java/android/net/InterfaceConfiguration.aidl b/packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl index 8aa5e3452853..8aa5e3452853 100644 --- a/core/java/android/net/InterfaceConfiguration.aidl +++ b/packages/Connectivity/framework/src/android/net/InterfaceConfiguration.aidl diff --git a/core/java/android/net/InvalidPacketException.java b/packages/Connectivity/framework/src/android/net/InvalidPacketException.java index 1873d778c0f2..1873d778c0f2 100644 --- a/core/java/android/net/InvalidPacketException.java +++ b/packages/Connectivity/framework/src/android/net/InvalidPacketException.java diff --git a/core/java/android/net/IpConfiguration.aidl b/packages/Connectivity/framework/src/android/net/IpConfiguration.aidl index 7a30f0e79cad..7a30f0e79cad 100644 --- a/core/java/android/net/IpConfiguration.aidl +++ b/packages/Connectivity/framework/src/android/net/IpConfiguration.aidl diff --git a/core/java/android/net/IpConfiguration.java b/packages/Connectivity/framework/src/android/net/IpConfiguration.java index 0b205642b321..0b205642b321 100644 --- a/core/java/android/net/IpConfiguration.java +++ b/packages/Connectivity/framework/src/android/net/IpConfiguration.java diff --git a/core/java/android/net/IpPrefix.aidl b/packages/Connectivity/framework/src/android/net/IpPrefix.aidl index 0d70f2a1ed2c..0d70f2a1ed2c 100644 --- a/core/java/android/net/IpPrefix.aidl +++ b/packages/Connectivity/framework/src/android/net/IpPrefix.aidl diff --git a/core/java/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java index e7c801475c4d..e7c801475c4d 100644 --- a/core/java/android/net/IpPrefix.java +++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java diff --git a/core/java/android/net/KeepalivePacketData.aidl b/packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl index d456b53fd188..d456b53fd188 100644 --- a/core/java/android/net/KeepalivePacketData.aidl +++ b/packages/Connectivity/framework/src/android/net/KeepalivePacketData.aidl diff --git a/core/java/android/net/KeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/KeepalivePacketData.java index 5877f1f4e269..5877f1f4e269 100644 --- a/core/java/android/net/KeepalivePacketData.java +++ b/packages/Connectivity/framework/src/android/net/KeepalivePacketData.java diff --git a/core/java/android/net/LinkAddress.aidl b/packages/Connectivity/framework/src/android/net/LinkAddress.aidl index 9c804db08d61..9c804db08d61 100644 --- a/core/java/android/net/LinkAddress.aidl +++ b/packages/Connectivity/framework/src/android/net/LinkAddress.aidl diff --git a/core/java/android/net/LinkAddress.java b/packages/Connectivity/framework/src/android/net/LinkAddress.java index 44d25a1ab0af..44d25a1ab0af 100644 --- a/core/java/android/net/LinkAddress.java +++ b/packages/Connectivity/framework/src/android/net/LinkAddress.java diff --git a/core/java/android/net/LinkProperties.aidl b/packages/Connectivity/framework/src/android/net/LinkProperties.aidl index a8b3c7b0392f..a8b3c7b0392f 100644 --- a/core/java/android/net/LinkProperties.aidl +++ b/packages/Connectivity/framework/src/android/net/LinkProperties.aidl diff --git a/core/java/android/net/LinkProperties.java b/packages/Connectivity/framework/src/android/net/LinkProperties.java index 486e2d74dd05..486e2d74dd05 100644 --- a/core/java/android/net/LinkProperties.java +++ b/packages/Connectivity/framework/src/android/net/LinkProperties.java diff --git a/core/java/android/net/MacAddress.aidl b/packages/Connectivity/framework/src/android/net/MacAddress.aidl index 48a18a7ac821..48a18a7ac821 100644 --- a/core/java/android/net/MacAddress.aidl +++ b/packages/Connectivity/framework/src/android/net/MacAddress.aidl diff --git a/core/java/android/net/MacAddress.java b/packages/Connectivity/framework/src/android/net/MacAddress.java index c7116b41e80a..c7116b41e80a 100644 --- a/core/java/android/net/MacAddress.java +++ b/packages/Connectivity/framework/src/android/net/MacAddress.java diff --git a/core/java/android/net/NattKeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/NattKeepalivePacketData.java index c4f8fc281f25..c4f8fc281f25 100644 --- a/core/java/android/net/NattKeepalivePacketData.java +++ b/packages/Connectivity/framework/src/android/net/NattKeepalivePacketData.java diff --git a/core/java/android/net/NattSocketKeepalive.java b/packages/Connectivity/framework/src/android/net/NattSocketKeepalive.java index b0ce0c71fbeb..a15d165e65e7 100644 --- a/core/java/android/net/NattSocketKeepalive.java +++ b/packages/Connectivity/framework/src/android/net/NattSocketKeepalive.java @@ -51,7 +51,7 @@ public final class NattSocketKeepalive extends SocketKeepalive { void startImpl(int intervalSec) { mExecutor.execute(() -> { try { - mService.startNattKeepaliveWithFd(mNetwork, mPfd.getFileDescriptor(), mResourceId, + mService.startNattKeepaliveWithFd(mNetwork, mPfd, mResourceId, intervalSec, mCallback, mSource.getHostAddress(), mDestination.getHostAddress()); } catch (RemoteException e) { diff --git a/core/java/android/net/Network.aidl b/packages/Connectivity/framework/src/android/net/Network.aidl index 05622025bf33..05622025bf33 100644 --- a/core/java/android/net/Network.aidl +++ b/packages/Connectivity/framework/src/android/net/Network.aidl diff --git a/core/java/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java index f98a1f8a220d..b07bd68a0f50 100644 --- a/core/java/android/net/Network.java +++ b/packages/Connectivity/framework/src/android/net/Network.java @@ -21,6 +21,7 @@ import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.system.ErrnoException; import android.system.Os; @@ -380,7 +381,13 @@ public class Network implements Parcelable { // Query a property of the underlying socket to ensure that the socket's file descriptor // exists, is available to bind to a network and is not closed. socket.getReuseAddress(); - bindSocket(socket.getFileDescriptor$()); + final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket); + bindSocket(pfd.getFileDescriptor()); + // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the + // dup share the underlying socket in the kernel. The socket is never truly closed until the + // last fd pointing to the socket being closed. So close the dup one after binding the + // socket to control the lifetime of the dup fd. + pfd.close(); } /** @@ -392,7 +399,13 @@ public class Network implements Parcelable { // Query a property of the underlying socket to ensure that the socket's file descriptor // exists, is available to bind to a network and is not closed. socket.getReuseAddress(); - bindSocket(socket.getFileDescriptor$()); + final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); + bindSocket(pfd.getFileDescriptor()); + // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the + // dup share the underlying socket in the kernel. The socket is never truly closed until the + // last fd pointing to the socket being closed. So close the dup one after binding the + // socket to control the lifetime of the dup fd. + pfd.close(); } /** @@ -420,7 +433,7 @@ public class Network implements Parcelable { throw new SocketException("Only AF_INET/AF_INET6 sockets supported"); } - final int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId); + final int err = NetworkUtils.bindSocketToNetwork(fd, netId); if (err != 0) { // bindSocketToNetwork returns negative errno. throw new ErrnoException("Binding socket to network " + netId, -err) diff --git a/core/java/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java index 4166c2c4f244..d22d82d1f4d0 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java @@ -30,6 +30,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.telephony.data.EpsBearerQosSessionAttributes; import android.util.Log; import com.android.connectivity.aidl.INetworkAgent; @@ -228,12 +229,6 @@ public abstract class NetworkAgent { public static final String REDIRECT_URL_KEY = "redirect URL"; /** - * Bundle key for the underlying networks in {@code EVENT_UNDERLYING_NETWORKS_CHANGED}. - * @hide - */ - public static final String UNDERLYING_NETWORKS_KEY = "underlyingNetworks"; - - /** * Sent by the NetworkAgent to ConnectivityService to indicate this network was * explicitly selected. This should be sent before the NetworkInfo is marked * CONNECTED so it can be given special treatment at that time. @@ -347,6 +342,24 @@ public abstract class NetworkAgent { */ private static final int EVENT_AGENT_DISCONNECTED = BASE + 19; + /** + * Sent by QosCallbackTracker to {@link NetworkAgent} to register a new filter with + * callback. + * + * arg1 = QoS agent callback ID + * obj = {@link QosFilter} + * @hide + */ + public static final int CMD_REGISTER_QOS_CALLBACK = BASE + 20; + + /** + * Sent by QosCallbackTracker to {@link NetworkAgent} to unregister a callback. + * + * arg1 = QoS agent callback ID + * @hide + */ + public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21; + private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) { // The subtype can be changed with (TODO) setLegacySubtype, but it starts // with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description. @@ -408,7 +421,8 @@ public abstract class NetworkAgent { throw new IllegalArgumentException(); } - mInitialConfiguration = new InitialConfiguration(context, new NetworkCapabilities(nc), + mInitialConfiguration = new InitialConfiguration(context, + new NetworkCapabilities(nc, /* parcelLocationSensitiveFields */ true), new LinkProperties(lp), score, config, ni); } @@ -525,6 +539,17 @@ public abstract class NetworkAgent { onRemoveKeepalivePacketFilter(msg.arg1 /* slot */); break; } + case CMD_REGISTER_QOS_CALLBACK: { + onQosCallbackRegistered( + msg.arg1 /* QoS callback id */, + (QosFilter) msg.obj /* QoS filter */); + break; + } + case CMD_UNREGISTER_QOS_CALLBACK: { + onQosCallbackUnregistered( + msg.arg1 /* QoS callback id */); + break; + } } } } @@ -558,6 +583,8 @@ public abstract class NetworkAgent { } private static class NetworkAgentBinder extends INetworkAgent.Stub { + private static final String LOG_TAG = NetworkAgentBinder.class.getSimpleName(); + private final Handler mHandler; private NetworkAgentBinder(Handler handler) { @@ -644,6 +671,25 @@ public abstract class NetworkAgent { mHandler.sendMessage(mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, slot, 0)); } + + @Override + public void onQosFilterCallbackRegistered(final int qosCallbackId, + final QosFilterParcelable qosFilterParcelable) { + if (qosFilterParcelable.getQosFilter() != null) { + mHandler.sendMessage( + mHandler.obtainMessage(CMD_REGISTER_QOS_CALLBACK, qosCallbackId, 0, + qosFilterParcelable.getQosFilter())); + return; + } + + Log.wtf(LOG_TAG, "onQosFilterCallbackRegistered: qos filter is null."); + } + + @Override + public void onQosCallbackUnregistered(final int qosCallbackId) { + mHandler.sendMessage(mHandler.obtainMessage( + CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null)); + } } /** @@ -818,7 +864,9 @@ public abstract class NetworkAgent { Objects.requireNonNull(networkCapabilities); mBandwidthUpdatePending.set(false); mLastBwRefreshTime = System.currentTimeMillis(); - final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); + final NetworkCapabilities nc = + new NetworkCapabilities(networkCapabilities, + /* parcelLocationSensitiveFields */ true); queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc)); } @@ -1070,8 +1118,68 @@ public abstract class NetworkAgent { protected void preventAutomaticReconnect() { } + /** + * Called when a qos callback is registered with a filter. + * @param qosCallbackId the id for the callback registered + * @param filter the filter being registered + */ + public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) { + } + + /** + * Called when a qos callback is registered with a filter. + * <p/> + * Any QoS events that are sent with the same callback id after this method is called + * are a no-op. + * + * @param qosCallbackId the id for the callback being unregistered + */ + public void onQosCallbackUnregistered(final int qosCallbackId) { + } + + + /** + * Sends the attributes of Eps Bearer Qos Session back to the Application + * + * @param qosCallbackId the callback id that the session belongs to + * @param sessionId the unique session id across all Eps Bearer Qos Sessions + * @param attributes the attributes of the Eps Qos Session + */ + public final void sendQosSessionAvailable(final int qosCallbackId, final int sessionId, + @NonNull final EpsBearerQosSessionAttributes attributes) { + Objects.requireNonNull(attributes, "The attributes must be non-null"); + queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId, + new QosSession(sessionId, QosSession.TYPE_EPS_BEARER), + attributes)); + } + + /** + * Sends event that the Eps Qos Session was lost. + * + * @param qosCallbackId the callback id that the session belongs to + * @param sessionId the unique session id across all Eps Bearer Qos Sessions + */ + public final void sendQosSessionLost(final int qosCallbackId, final int sessionId) { + queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId, + new QosSession(sessionId, QosSession.TYPE_EPS_BEARER))); + } + + /** + * Sends the exception type back to the application. + * + * The NetworkAgent should not send anymore messages with this id. + * + * @param qosCallbackId the callback id this exception belongs to + * @param exceptionType the type of exception + */ + public final void sendQosCallbackError(final int qosCallbackId, + @QosCallbackException.ExceptionType final int exceptionType) { + queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType)); + } + + /** @hide */ - protected void log(String s) { + protected void log(final String s) { Log.d(LOG_TAG, "NetworkAgent: " + s); } } diff --git a/core/java/android/net/NetworkAgentConfig.aidl b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl index cb70bdd31260..cb70bdd31260 100644 --- a/core/java/android/net/NetworkAgentConfig.aidl +++ b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.aidl diff --git a/core/java/android/net/NetworkAgentConfig.java b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java index fe1268d79b89..664c2650ff0c 100644 --- a/core/java/android/net/NetworkAgentConfig.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java @@ -16,6 +16,8 @@ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -125,6 +127,7 @@ public final class NetworkAgentConfig implements Parcelable { * @return the subscriber ID, or null if none. * @hide */ + @SystemApi(client = MODULE_LIBRARIES) @Nullable public String getSubscriberId() { return subscriberId; @@ -275,6 +278,7 @@ public final class NetworkAgentConfig implements Parcelable { * @hide */ @NonNull + @SystemApi(client = MODULE_LIBRARIES) public Builder setSubscriberId(@Nullable String subscriberId) { mConfig.subscriberId = subscriberId; return this; diff --git a/core/java/android/net/NetworkCapabilities.aidl b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl index 01d328605de4..01d328605de4 100644 --- a/core/java/android/net/NetworkCapabilities.aidl +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.aidl diff --git a/core/java/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index 1a37fb9fb690..8bfa77acd36d 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -23,7 +23,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.net.ConnectivityManager.NetworkCallback; import android.os.Build; @@ -35,9 +34,9 @@ import android.util.ArraySet; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.BitUtils; import com.android.internal.util.Preconditions; +import com.android.net.module.util.CollectionUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -76,12 +75,33 @@ public final class NetworkCapabilities implements Parcelable { */ private String mRequestorPackageName; + /** + * Indicates whether parceling should preserve fields that are set based on permissions of + * the process receiving the {@link NetworkCapabilities}. + */ + private final boolean mParcelLocationSensitiveFields; + public NetworkCapabilities() { + mParcelLocationSensitiveFields = false; clearAll(); mNetworkCapabilities = DEFAULT_CAPABILITIES; } public NetworkCapabilities(NetworkCapabilities nc) { + this(nc, false /* parcelLocationSensitiveFields */); + } + + /** + * Make a copy of NetworkCapabilities. + * + * @param nc Original NetworkCapabilities + * @param parcelLocationSensitiveFields Whether to parcel location sensitive data or not. + * @hide + */ + @SystemApi + public NetworkCapabilities( + @Nullable NetworkCapabilities nc, boolean parcelLocationSensitiveFields) { + mParcelLocationSensitiveFields = parcelLocationSensitiveFields; if (nc != null) { set(nc); } @@ -93,6 +113,12 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public void clearAll() { + // Ensures that the internal copies maintained by the connectivity stack does not set + // this bit. + if (mParcelLocationSensitiveFields) { + throw new UnsupportedOperationException( + "Cannot clear NetworkCapabilities when parcelLocationSensitiveFields is set"); + } mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0; mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; mNetworkSpecifier = null; @@ -109,6 +135,8 @@ public final class NetworkCapabilities implements Parcelable { /** * Set all contents of this object to the contents of a NetworkCapabilities. + * + * @param nc Original NetworkCapabilities * @hide */ public void set(@NonNull NetworkCapabilities nc) { @@ -117,7 +145,11 @@ public final class NetworkCapabilities implements Parcelable { mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps; mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; mNetworkSpecifier = nc.mNetworkSpecifier; - mTransportInfo = nc.mTransportInfo; + if (nc.getTransportInfo() != null) { + setTransportInfo(nc.getTransportInfo().makeCopy(mParcelLocationSensitiveFields)); + } else { + setTransportInfo(null); + } mSignalStrength = nc.mSignalStrength; setUids(nc.mUids); // Will make the defensive copy setAdministratorUids(nc.getAdministratorUids()); @@ -171,6 +203,8 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_PARTIAL_CONNECTIVITY, NET_CAPABILITY_TEMPORARILY_NOT_METERED, NET_CAPABILITY_OEM_PRIVATE, + NET_CAPABILITY_VEHICLE_INTERNAL, + NET_CAPABILITY_NOT_VCN_MANAGED, }) public @interface NetCapability { } @@ -357,8 +391,32 @@ public final class NetworkCapabilities implements Parcelable { @SystemApi public static final int NET_CAPABILITY_OEM_PRIVATE = 26; + /** + * Indicates this is an internal vehicle network, meant to communicate with other + * automotive systems. + * + * @hide + */ + @SystemApi + public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; + + /** + * Indicates that this network is not subsumed by a Virtual Carrier Network (VCN). + * <p> + * To provide an experience on a VCN similar to a single traditional carrier network, in + * some cases the system sets this bit is set by default in application's network requests, + * and may choose to remove it at its own discretion when matching the request to a network. + * <p> + * Applications that want to know about a Virtual Carrier Network's underlying networks, + * for example to use them for multipath purposes, should remove this bit from their network + * requests ; the system will not add it back once removed. + * @hide + */ + @SystemApi + public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PRIVATE; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -375,7 +433,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_NOT_CONGESTED) | (1 << NET_CAPABILITY_NOT_SUSPENDED) | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY) - | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED); + | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED) + | (1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Network capabilities that are not allowed in NetworkRequests. This exists because the @@ -384,16 +443,21 @@ public final class NetworkCapabilities implements Parcelable { * can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then * get immediately torn down because they do not have the requested capability. */ + // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities + // are mutable but requestable. Factories are responsible for not getting + // in an infinite loop about these. private static final long NON_REQUESTABLE_CAPABILITIES = - MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED); + MUTABLE_CAPABILITIES + & ~(1 << NET_CAPABILITY_TRUSTED) + & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Capabilities that are set by default when the object is constructed. */ private static final long DEFAULT_CAPABILITIES = - (1 << NET_CAPABILITY_NOT_RESTRICTED) | - (1 << NET_CAPABILITY_TRUSTED) | - (1 << NET_CAPABILITY_NOT_VPN); + (1 << NET_CAPABILITY_NOT_RESTRICTED) + | (1 << NET_CAPABILITY_TRUSTED) + | (1 << NET_CAPABILITY_NOT_VPN); /** * Capabilities that suggest that a network is restricted. @@ -401,15 +465,16 @@ public final class NetworkCapabilities implements Parcelable { */ @VisibleForTesting /* package */ static final long RESTRICTED_CAPABILITIES = - (1 << NET_CAPABILITY_CBS) | - (1 << NET_CAPABILITY_DUN) | - (1 << NET_CAPABILITY_EIMS) | - (1 << NET_CAPABILITY_FOTA) | - (1 << NET_CAPABILITY_IA) | - (1 << NET_CAPABILITY_IMS) | - (1 << NET_CAPABILITY_RCS) | - (1 << NET_CAPABILITY_XCAP) | - (1 << NET_CAPABILITY_MCX); + (1 << NET_CAPABILITY_CBS) + | (1 << NET_CAPABILITY_DUN) + | (1 << NET_CAPABILITY_EIMS) + | (1 << NET_CAPABILITY_FOTA) + | (1 << NET_CAPABILITY_IA) + | (1 << NET_CAPABILITY_IMS) + | (1 << NET_CAPABILITY_MCX) + | (1 << NET_CAPABILITY_RCS) + | (1 << NET_CAPABILITY_VEHICLE_INTERNAL) + | (1 << NET_CAPABILITY_XCAP); /** * Capabilities that force network to be restricted. @@ -425,10 +490,10 @@ public final class NetworkCapabilities implements Parcelable { */ @VisibleForTesting /* package */ static final long UNRESTRICTED_CAPABILITIES = - (1 << NET_CAPABILITY_INTERNET) | - (1 << NET_CAPABILITY_MMS) | - (1 << NET_CAPABILITY_SUPL) | - (1 << NET_CAPABILITY_WIFI_P2P); + (1 << NET_CAPABILITY_INTERNET) + | (1 << NET_CAPABILITY_MMS) + | (1 << NET_CAPABILITY_SUPL) + | (1 << NET_CAPABILITY_WIFI_P2P); /** * Capabilities that are managed by ConnectivityService. @@ -452,7 +517,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_NOT_VPN) | (1 << NET_CAPABILITY_NOT_ROAMING) | (1 << NET_CAPABILITY_NOT_CONGESTED) - | (1 << NET_CAPABILITY_NOT_SUSPENDED); + | (1 << NET_CAPABILITY_NOT_SUSPENDED) + | (1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Adds the given capability to this {@code NetworkCapability} instance. @@ -532,7 +598,6 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ @UnsupportedAppUsage - @TestApi public @NetCapability int[] getCapabilities() { return BitUtils.unpackBits(mNetworkCapabilities); } @@ -709,7 +774,7 @@ public final class NetworkCapabilities implements Parcelable { if (originalOwnerUid == creatorUid) { setOwnerUid(creatorUid); } - if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) { + if (CollectionUtils.contains(originalAdministratorUids, creatorUid)) { setAdministratorUids(new int[] {creatorUid}); } // There is no need to clear the UIDs, they have already been cleared by clearAll() above. @@ -777,7 +842,7 @@ public final class NetworkCapabilities implements Parcelable { * * @hide */ - @TestApi + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final int TRANSPORT_TEST = 7; /** @hide */ @@ -896,8 +961,8 @@ public final class NetworkCapabilities implements Parcelable { } private boolean satisfiedByTransportTypes(NetworkCapabilities nc) { - return ((this.mTransportTypes == 0) || - ((this.mTransportTypes & nc.mTransportTypes) != 0)); + return ((this.mTransportTypes == 0) + || ((this.mTransportTypes & nc.mTransportTypes) != 0)); } /** @hide */ @@ -1151,12 +1216,12 @@ public final class NetworkCapabilities implements Parcelable { Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps); } private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) { - return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps || - this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps); + return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps + || this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps); } private boolean equalsLinkBandwidths(NetworkCapabilities nc) { - return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps && - this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps); + return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps + && this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps); } /** @hide */ public static int minBandwidth(int a, int b) { @@ -1671,9 +1736,9 @@ public final class NetworkCapabilities implements Parcelable { */ public boolean equalRequestableCapabilities(@Nullable NetworkCapabilities nc) { if (nc == null) return false; - return (equalsNetCapabilitiesRequestable(nc) && - equalsTransportTypes(nc) && - equalsSpecifier(nc)); + return (equalsNetCapabilitiesRequestable(nc) + && equalsTransportTypes(nc) + && equalsSpecifier(nc)); } @Override @@ -1815,7 +1880,7 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" OwnerUid: ").append(mOwnerUid); } - if (!ArrayUtils.isEmpty(mAdministratorUids)) { + if (mAdministratorUids != null && mAdministratorUids.length != 0) { sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids)); } @@ -1939,6 +2004,8 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY"; case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED"; case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE"; + case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL"; + case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED"; default: return Integer.toString(capability); } } @@ -2446,7 +2513,7 @@ public final class NetworkCapabilities implements Parcelable { @NonNull public NetworkCapabilities build() { if (mCaps.getOwnerUid() != Process.INVALID_UID) { - if (!ArrayUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) { + if (!CollectionUtils.contains(mCaps.getAdministratorUids(), mCaps.getOwnerUid())) { throw new IllegalStateException("The owner UID must be included in " + " administrator UIDs."); } diff --git a/core/java/android/net/NetworkConfig.java b/packages/Connectivity/framework/src/android/net/NetworkConfig.java index 32a2cda00370..32a2cda00370 100644 --- a/core/java/android/net/NetworkConfig.java +++ b/packages/Connectivity/framework/src/android/net/NetworkConfig.java diff --git a/core/java/android/net/NetworkInfo.aidl b/packages/Connectivity/framework/src/android/net/NetworkInfo.aidl index f50187302966..f50187302966 100644 --- a/core/java/android/net/NetworkInfo.aidl +++ b/packages/Connectivity/framework/src/android/net/NetworkInfo.aidl diff --git a/core/java/android/net/NetworkInfo.java b/packages/Connectivity/framework/src/android/net/NetworkInfo.java index d752901e2eb0..d752901e2eb0 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/packages/Connectivity/framework/src/android/net/NetworkInfo.java diff --git a/core/java/android/net/NetworkProvider.java b/packages/Connectivity/framework/src/android/net/NetworkProvider.java index 14cb51c85d06..14cb51c85d06 100644 --- a/core/java/android/net/NetworkProvider.java +++ b/packages/Connectivity/framework/src/android/net/NetworkProvider.java diff --git a/core/java/android/net/NetworkRequest.aidl b/packages/Connectivity/framework/src/android/net/NetworkRequest.aidl index 508defc6b497..508defc6b497 100644 --- a/core/java/android/net/NetworkRequest.aidl +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.aidl diff --git a/core/java/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index dc16d7422038..04011fc6816e 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -40,6 +40,18 @@ import java.util.Set; */ public class NetworkRequest implements Parcelable { /** + * The first requestId value that will be allocated. + * @hide only used by ConnectivityService. + */ + public static final int FIRST_REQUEST_ID = 1; + + /** + * The requestId value that represents the absence of a request. + * @hide only used by ConnectivityService. + */ + public static final int REQUEST_ID_NONE = -1; + + /** * The {@link NetworkCapabilities} that define this request. * @hide */ @@ -341,7 +353,9 @@ public class NetworkRequest implements Parcelable { * NetworkSpecifier. */ public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) { - MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier); + if (networkSpecifier instanceof MatchAllNetworkSpecifier) { + throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted"); + } mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); return this; } diff --git a/core/java/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java index b5962c5bae14..8be4af7b1396 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java @@ -81,11 +81,11 @@ public class NetworkUtils { public native static boolean bindProcessToNetworkForHostResolution(int netId); /** - * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This + * Explicitly binds {@code fd} to the network designated by {@code netId}. This * overrides any binding via {@link #bindProcessToNetwork}. * @return 0 on success or negative errno on failure. */ - public native static int bindSocketToNetwork(int socketfd, int netId); + public static native int bindSocketToNetwork(FileDescriptor fd, int netId); /** * Protect {@code fd} from VPN connections. After protecting, data sent through @@ -93,9 +93,7 @@ public class NetworkUtils { * forwarded through the VPN. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static boolean protectFromVpn(FileDescriptor fd) { - return protectFromVpn(fd.getInt$()); - } + public static native boolean protectFromVpn(FileDescriptor fd); /** * Protect {@code socketfd} from VPN connections. After protecting, data sent through diff --git a/core/java/android/net/PacProxySelector.java b/packages/Connectivity/framework/src/android/net/PacProxySelector.java index 85bf79ab3d69..326943a27d4e 100644 --- a/core/java/android/net/PacProxySelector.java +++ b/packages/Connectivity/framework/src/android/net/PacProxySelector.java @@ -20,6 +20,7 @@ import android.os.ServiceManager; import android.util.Log; import com.android.net.IProxyService; + import com.google.android.collect.Lists; import java.io.IOException; @@ -50,7 +51,7 @@ public class PacProxySelector extends ProxySelector { ServiceManager.getService(PROXY_SERVICE)); if (mProxyService == null) { // Added because of b10267814 where mako is restarting. - Log.e(TAG, "PacManager: no proxy service"); + Log.e(TAG, "PacProxyInstaller: no proxy service"); } mDefaultList = Lists.newArrayList(java.net.Proxy.NO_PROXY); } diff --git a/core/java/android/net/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java index 03b07e080add..03b07e080add 100644 --- a/core/java/android/net/Proxy.java +++ b/packages/Connectivity/framework/src/android/net/Proxy.java diff --git a/core/java/android/net/ProxyInfo.aidl b/packages/Connectivity/framework/src/android/net/ProxyInfo.aidl index a5d0c120e747..a5d0c120e747 100644 --- a/core/java/android/net/ProxyInfo.aidl +++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.aidl diff --git a/core/java/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java index a32b41f6be4b..c9bca2876b0a 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java @@ -127,7 +127,7 @@ public class ProxyInfo implements Parcelable { } /** - * Only used in PacManager after Local Proxy is bound. + * Only used in PacProxyInstaller after Local Proxy is bound. * @hide */ public ProxyInfo(@NonNull Uri pacFileUrl, int localProxyPort) { @@ -355,7 +355,7 @@ public class ProxyInfo implements Parcelable { port = in.readInt(); } String exclList = in.readString(); - String[] parsedExclList = in.readStringArray(); + String[] parsedExclList = in.createStringArray(); ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList); return proxyProperties; } diff --git a/core/java/android/net/RouteInfo.aidl b/packages/Connectivity/framework/src/android/net/RouteInfo.aidl index 7af9fdaef342..7af9fdaef342 100644 --- a/core/java/android/net/RouteInfo.aidl +++ b/packages/Connectivity/framework/src/android/net/RouteInfo.aidl diff --git a/core/java/android/net/RouteInfo.java b/packages/Connectivity/framework/src/android/net/RouteInfo.java index 94f849f006f3..94f849f006f3 100644 --- a/core/java/android/net/RouteInfo.java +++ b/packages/Connectivity/framework/src/android/net/RouteInfo.java diff --git a/core/java/android/net/SocketKeepalive.java b/packages/Connectivity/framework/src/android/net/SocketKeepalive.java index d007a9520cb5..d007a9520cb5 100644 --- a/core/java/android/net/SocketKeepalive.java +++ b/packages/Connectivity/framework/src/android/net/SocketKeepalive.java diff --git a/core/java/android/net/StaticIpConfiguration.aidl b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl index 8aac701fe7e1..8aac701fe7e1 100644 --- a/core/java/android/net/StaticIpConfiguration.aidl +++ b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.aidl diff --git a/core/java/android/net/StaticIpConfiguration.java b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java index ce545974f5cb..ce545974f5cb 100644 --- a/core/java/android/net/StaticIpConfiguration.java +++ b/packages/Connectivity/framework/src/android/net/StaticIpConfiguration.java diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java index ddb3a6a72fb4..ddb3a6a72fb4 100644 --- a/core/java/android/net/TcpKeepalivePacketData.java +++ b/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java diff --git a/core/java/android/net/TcpRepairWindow.java b/packages/Connectivity/framework/src/android/net/TcpRepairWindow.java index f062fa9034ea..f062fa9034ea 100644 --- a/core/java/android/net/TcpRepairWindow.java +++ b/packages/Connectivity/framework/src/android/net/TcpRepairWindow.java diff --git a/core/java/android/net/TcpSocketKeepalive.java b/packages/Connectivity/framework/src/android/net/TcpSocketKeepalive.java index 436397ea7754..d89814d49bd0 100644 --- a/core/java/android/net/TcpSocketKeepalive.java +++ b/packages/Connectivity/framework/src/android/net/TcpSocketKeepalive.java @@ -21,7 +21,6 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; -import java.io.FileDescriptor; import java.util.concurrent.Executor; /** @hide */ @@ -54,8 +53,7 @@ final class TcpSocketKeepalive extends SocketKeepalive { void startImpl(int intervalSec) { mExecutor.execute(() -> { try { - final FileDescriptor fd = mPfd.getFileDescriptor(); - mService.startTcpKeepalive(mNetwork, fd, intervalSec, mCallback); + mService.startTcpKeepalive(mNetwork, mPfd, intervalSec, mCallback); } catch (RemoteException e) { Log.e(TAG, "Error starting packet keepalive: ", e); throw e.rethrowFromSystemServer(); diff --git a/core/java/android/net/TestNetworkInterface.aidl b/packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl index e1f4f9f794eb..e1f4f9f794eb 100644 --- a/core/java/android/net/TestNetworkInterface.aidl +++ b/packages/Connectivity/framework/src/android/net/TestNetworkInterface.aidl diff --git a/core/java/android/net/TestNetworkInterface.java b/packages/Connectivity/framework/src/android/net/TestNetworkInterface.java index 84550834be07..4449ff80180b 100644 --- a/core/java/android/net/TestNetworkInterface.java +++ b/packages/Connectivity/framework/src/android/net/TestNetworkInterface.java @@ -15,7 +15,8 @@ */ package android.net; -import android.annotation.TestApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -25,9 +26,11 @@ import android.os.Parcelable; * * @hide */ -@TestApi +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public final class TestNetworkInterface implements Parcelable { + @NonNull private final ParcelFileDescriptor mFileDescriptor; + @NonNull private final String mInterfaceName; @Override @@ -36,29 +39,32 @@ public final class TestNetworkInterface implements Parcelable { } @Override - public void writeToParcel(Parcel out, int flags) { + public void writeToParcel(@NonNull Parcel out, int flags) { out.writeParcelable(mFileDescriptor, PARCELABLE_WRITE_RETURN_VALUE); out.writeString(mInterfaceName); } - public TestNetworkInterface(ParcelFileDescriptor pfd, String intf) { + public TestNetworkInterface(@NonNull ParcelFileDescriptor pfd, @NonNull String intf) { mFileDescriptor = pfd; mInterfaceName = intf; } - private TestNetworkInterface(Parcel in) { + private TestNetworkInterface(@NonNull Parcel in) { mFileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); mInterfaceName = in.readString(); } + @NonNull public ParcelFileDescriptor getFileDescriptor() { return mFileDescriptor; } + @NonNull public String getInterfaceName() { return mInterfaceName; } + @NonNull public static final Parcelable.Creator<TestNetworkInterface> CREATOR = new Parcelable.Creator<TestNetworkInterface>() { public TestNetworkInterface createFromParcel(Parcel in) { diff --git a/core/java/android/net/TestNetworkManager.java b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java index a0a563b37025..4e894143bf91 100644 --- a/core/java/android/net/TestNetworkManager.java +++ b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java @@ -17,18 +17,21 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.TestApi; +import android.annotation.SystemApi; import android.os.IBinder; import android.os.RemoteException; import com.android.internal.util.Preconditions; +import java.util.Arrays; +import java.util.Collection; + /** * Class that allows creation and management of per-app, test-only networks * * @hide */ -@TestApi +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public class TestNetworkManager { /** * Prefix for tun interfaces created by this class. @@ -57,7 +60,7 @@ public class TestNetworkManager { * @param network The test network that should be torn down * @hide */ - @TestApi + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void teardownTestNetwork(@NonNull Network network) { try { mService.teardownTestNetwork(network.netId); @@ -102,7 +105,7 @@ public class TestNetworkManager { * @param binder A binder object guarding the lifecycle of this test network. * @hide */ - @TestApi + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) { setupTestNetwork(iface, null, true, new int[0], binder); } @@ -127,12 +130,29 @@ public class TestNetworkManager { * @param linkAddrs an array of LinkAddresses to assign to the TUN interface * @return A ParcelFileDescriptor of the underlying TUN interface. Close this to tear down the * TUN interface. + * @deprecated Use {@link #createTunInterface(Collection)} instead. * @hide */ - @TestApi + @Deprecated + @NonNull public TestNetworkInterface createTunInterface(@NonNull LinkAddress[] linkAddrs) { + return createTunInterface(Arrays.asList(linkAddrs)); + } + + /** + * Create a tun interface for testing purposes + * + * @param linkAddrs an array of LinkAddresses to assign to the TUN interface + * @return A ParcelFileDescriptor of the underlying TUN interface. Close this to tear down the + * TUN interface. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @NonNull + public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) { try { - return mService.createTunInterface(linkAddrs); + final LinkAddress[] arr = new LinkAddress[linkAddrs.size()]; + return mService.createTunInterface(linkAddrs.toArray(arr)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -145,7 +165,8 @@ public class TestNetworkManager { * TAP interface. * @hide */ - @TestApi + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @NonNull public TestNetworkInterface createTapInterface() { try { return mService.createTapInterface(); diff --git a/packages/Connectivity/framework/src/android/net/TransportInfo.java b/packages/Connectivity/framework/src/android/net/TransportInfo.java new file mode 100644 index 000000000000..aa4bbb051179 --- /dev/null +++ b/packages/Connectivity/framework/src/android/net/TransportInfo.java @@ -0,0 +1,63 @@ +/* + * 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.net; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +/** + * A container for transport-specific capabilities which is returned by + * {@link NetworkCapabilities#getTransportInfo()}. Specific networks + * may provide concrete implementations of this interface. + * @see android.net.wifi.aware.WifiAwareNetworkInfo + * @see android.net.wifi.WifiInfo + */ +public interface TransportInfo { + + /** + * Create a copy of a {@link TransportInfo} that will preserve location sensitive fields that + * were set based on the permissions of the process that originally received it. + * + * <p>By default {@link TransportInfo} does not preserve such fields during parceling, as + * they should not be shared outside of the process that receives them without appropriate + * checks. + * + * @param parcelLocationSensitiveFields Whether the location sensitive fields should be kept + * when parceling + * @return Copy of this instance. + * @hide + */ + @SystemApi + @NonNull + default TransportInfo makeCopy(boolean parcelLocationSensitiveFields) { + return this; + } + + /** + * Returns whether this TransportInfo type has location sensitive fields or not (helps + * to determine whether to perform a location permission check or not before sending to + * apps). + * + * @return {@code true} if this instance contains location sensitive info, {@code false} + * otherwise. + * @hide + */ + @SystemApi + default boolean hasLocationSensitiveFields() { + return false; + } +} diff --git a/core/java/android/net/UidRange.aidl b/packages/Connectivity/framework/src/android/net/UidRange.aidl index f70fc8e2fefd..f70fc8e2fefd 100644 --- a/core/java/android/net/UidRange.aidl +++ b/packages/Connectivity/framework/src/android/net/UidRange.aidl diff --git a/core/java/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java index c87b8279c4d6..c87b8279c4d6 100644 --- a/core/java/android/net/VpnManager.java +++ b/packages/Connectivity/framework/src/android/net/VpnManager.java diff --git a/core/java/android/net/VpnService.java b/packages/Connectivity/framework/src/android/net/VpnService.java index 8e90a119fe21..8e90a119fe21 100644 --- a/core/java/android/net/VpnService.java +++ b/packages/Connectivity/framework/src/android/net/VpnService.java diff --git a/core/java/android/net/apf/ApfCapabilities.aidl b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl index 7c4d4c2da4bc..7c4d4c2da4bc 100644 --- a/core/java/android/net/apf/ApfCapabilities.aidl +++ b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.aidl diff --git a/core/java/android/net/apf/ApfCapabilities.java b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java index bf5b26e278f9..bf5b26e278f9 100644 --- a/core/java/android/net/apf/ApfCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java diff --git a/core/java/android/net/util/DnsUtils.java b/packages/Connectivity/framework/src/android/net/util/DnsUtils.java index 7908353eeda2..7908353eeda2 100644 --- a/core/java/android/net/util/DnsUtils.java +++ b/packages/Connectivity/framework/src/android/net/util/DnsUtils.java diff --git a/core/java/android/net/util/KeepaliveUtils.java b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java index bfc4563fbf8f..bfc4563fbf8f 100644 --- a/core/java/android/net/util/KeepaliveUtils.java +++ b/packages/Connectivity/framework/src/android/net/util/KeepaliveUtils.java diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java index aa0f6220036c..85e3fa3048ed 100644 --- a/core/java/android/net/util/MultinetworkPolicyTracker.java +++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java @@ -29,12 +29,11 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; -import android.os.UserHandle; import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.util.Slog; +import android.util.Log; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -114,8 +113,8 @@ public class MultinetworkPolicyTracker { final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - mContext.registerReceiverAsUser( - mBroadcastReceiver, UserHandle.ALL, intentFilter, null, mHandler); + mContext.registerReceiverForAllUsers(mBroadcastReceiver, intentFilter, + null /* broadcastPermission */, mHandler); reevaluate(); } @@ -204,13 +203,13 @@ public class MultinetworkPolicyTracker { @Override public void onChange(boolean selfChange) { - Slog.wtf(TAG, "Should never be reached."); + Log.wtf(TAG, "Should never be reached."); } @Override public void onChange(boolean selfChange, Uri uri) { if (!mSettingsUris.contains(uri)) { - Slog.wtf(TAG, "Unexpected settings observation: " + uri); + Log.wtf(TAG, "Unexpected settings observation: " + uri); } reevaluate(); } diff --git a/core/java/android/net/util/SocketUtils.java b/packages/Connectivity/framework/src/android/net/util/SocketUtils.java index e64060f1b220..e64060f1b220 100644 --- a/core/java/android/net/util/SocketUtils.java +++ b/packages/Connectivity/framework/src/android/net/util/SocketUtils.java diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl new file mode 100644 index 000000000000..64b556720cd2 --- /dev/null +++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl @@ -0,0 +1,49 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +package com.android.connectivity.aidl; + +import android.net.NattKeepalivePacketData; +import android.net.QosFilterParcelable; +import android.net.TcpKeepalivePacketData; + +import com.android.connectivity.aidl.INetworkAgentRegistry; + +/** + * Interface to notify NetworkAgent of connectivity events. + * @hide + */ +oneway interface INetworkAgent { + void onRegistered(in INetworkAgentRegistry registry); + void onDisconnected(); + void onBandwidthUpdateRequested(); + void onValidationStatusChanged(int validationStatus, + in @nullable String captivePortalUrl); + void onSaveAcceptUnvalidated(boolean acceptUnvalidated); + void onStartNattSocketKeepalive(int slot, int intervalDurationMs, + in NattKeepalivePacketData packetData); + void onStartTcpSocketKeepalive(int slot, int intervalDurationMs, + in TcpKeepalivePacketData packetData); + void onStopSocketKeepalive(int slot); + void onSignalStrengthThresholdsUpdated(in int[] thresholds); + void onPreventAutomaticReconnect(); + void onAddNattKeepalivePacketFilter(int slot, + in NattKeepalivePacketData packetData); + void onAddTcpKeepalivePacketFilter(int slot, + in TcpKeepalivePacketData packetData); + void onRemoveKeepalivePacketFilter(int slot); + void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel); + void onQosCallbackUnregistered(int qosCallbackId); +} diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl new file mode 100644 index 000000000000..f0193db5c2e2 --- /dev/null +++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl @@ -0,0 +1,41 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +package com.android.connectivity.aidl; + +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.QosSession; +import android.telephony.data.EpsBearerQosSessionAttributes; + +/** + * Interface for NetworkAgents to send network network properties. + * @hide + */ +oneway interface INetworkAgentRegistry { + void sendNetworkCapabilities(in NetworkCapabilities nc); + void sendLinkProperties(in LinkProperties lp); + // TODO: consider replacing this by "markConnected()" and removing + void sendNetworkInfo(in NetworkInfo info); + void sendScore(int score); + void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial); + void sendSocketKeepaliveEvent(int slot, int reason); + void sendUnderlyingNetworks(in @nullable List<Network> networks); + void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes); + void sendQosSessionLost(int qosCallbackId, in QosSession session); + void sendQosCallbackError(int qosCallbackId, int exceptionType); +} diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp index a26f715280a1..8fc318180778 100644 --- a/packages/Connectivity/service/Android.bp +++ b/packages/Connectivity/service/Android.bp @@ -14,8 +14,8 @@ // limitations under the License. // -cc_defaults { - name: "libservice-connectivity-defaults", +cc_library_shared { + name: "libservice-connectivity", // TODO: build against the NDK (sdk_version: "30" for example) cflags: [ "-Wall", @@ -26,6 +26,7 @@ cc_defaults { srcs: [ "jni/com_android_server_TestNetworkService.cpp", "jni/com_android_server_connectivity_Vpn.cpp", + "jni/onload.cpp", ], shared_libs: [ "libbase", @@ -35,27 +36,11 @@ cc_defaults { // addresses, and remove dependency on libnetutils. "libnetutils", ], -} - -cc_library_shared { - name: "libservice-connectivity", - defaults: ["libservice-connectivity-defaults"], - srcs: [ - "jni/onload.cpp", - ], apex_available: [ - // TODO: move this library to the tethering APEX and remove libservice-connectivity-static - // "com.android.tethering", + "com.android.tethering", ], } -// Static library linked into libservices.core until libservice-connectivity can be loaded from -// the tethering APEX instead. -cc_library_static { - name: "libservice-connectivity-static", - defaults: ["libservice-connectivity-defaults"], -} - java_library { name: "service-connectivity", srcs: [ @@ -72,8 +57,10 @@ java_library { static_libs: [ "net-utils-device-common", "net-utils-framework-common", + "netd-client", ], apex_available: [ "//apex_available:platform", + "com.android.tethering", ], } diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java index 06c52942e671..fcee98d0bd0b 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java @@ -19,11 +19,8 @@ package com.android.dynsystem; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.image.DynamicSystemClient; -import android.os.image.DynamicSystemManager; -import android.util.FeatureFlagUtils; /** @@ -43,24 +40,10 @@ public class BootCompletedReceiver extends BroadcastReceiver { return; } - DynamicSystemManager dynSystem = - (DynamicSystemManager) context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE); - - boolean isInUse = (dynSystem != null) && dynSystem.isInUse(); - - if (!isInUse && !featureFlagEnabled()) { - return; - } - Intent startServiceIntent = new Intent( context, DynamicSystemInstallationService.class); startServiceIntent.setAction(DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE); context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM); } - - private boolean featureFlagEnabled() { - return SystemProperties.getBoolean( - FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false); - } } diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java index 82ea7449bf6d..64e42cc595ec 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java @@ -22,10 +22,8 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.image.DynamicSystemClient; -import android.util.FeatureFlagUtils; import android.util.Log; /** @@ -46,12 +44,6 @@ public class VerificationActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (!featureFlagEnabled()) { - Log.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; activity aborted."); - finish(); - return; - } - KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); if (km != null) { @@ -101,11 +93,6 @@ public class VerificationActivity extends Activity { startServiceAsUser(intent, UserHandle.SYSTEM); } - private boolean featureFlagEnabled() { - return SystemProperties.getBoolean( - FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false); - } - static boolean isVerified(String url) { if (url == null) return true; return sVerifiedUrl != null && sVerifiedUrl.equals(url); diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index c63cf06cf75c..2b5e9cdc017d 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -291,7 +291,7 @@ <item>256K</item> <item>1M</item> <item>4M</item> - <item>16M</item> + <item>8M</item> </string-array> <!-- Titles for logd limit size lowram selection preference. [CHAR LIMIT=14] --> @@ -309,7 +309,7 @@ <item>262144</item> <item>1048576</item> <item>4194304</item> - <item>16777216</item> + <item>8388608</item> </string-array> <!-- Summaries for logd limit size selection preference. [CHAR LIMIT=50]--> @@ -319,7 +319,7 @@ <item>256K per log buffer</item> <item>1M per log buffer</item> <item>4M per log buffer</item> - <item>16M per log buffer</item> + <item>8M per log buffer</item> </string-array> <!-- Values for logpersist state selection preference. --> diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index dd94d2eb8fe0..c559678d4005 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -149,5 +149,6 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_APP, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 266bfe0a22b5..6568bffddecc 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -846,8 +846,8 @@ class DatabaseHelper extends SQLiteOpenHelper { try { stmt = db.compileStatement("INSERT INTO system(name,value)" + " VALUES(?,?);"); - loadBooleanSetting(stmt, Settings.System.USER_ROTATION, - R.integer.def_user_rotation); // should be zero degrees + loadIntegerSetting(stmt, Settings.System.USER_ROTATION, + R.integer.def_user_rotation); db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -2265,6 +2265,8 @@ class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.System.ACCELEROMETER_ROTATION, R.bool.def_accelerometer_rotation); + loadIntegerSetting(stmt, Settings.System.USER_ROTATION, R.integer.def_user_rotation); + loadDefaultHapticSettings(stmt); loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE, diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 5ecb17102438..1345c3f071c7 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -420,6 +420,7 @@ public class SettingsBackupTest { Settings.Global.RADIO_WIMAX, Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS, Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT, + Settings.Global.RESTRICTED_NETWORKING_MODE, Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, Settings.Global.SAFE_BOOT_DISALLOWED, Settings.Global.SELINUX_STATUS, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 741a6803a1e3..135356c864ec 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -156,6 +156,7 @@ <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS" /> <uses-permission android:name="android.permission.MANAGE_APP_PREDICTIONS" /> <uses-permission android:name="android.permission.MANAGE_SEARCH_UI" /> + <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" /> <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.SET_TIME" /> @@ -291,8 +292,8 @@ <!-- Permission needed to test mainline permission module rollback --> <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" /> - <!-- Permission needed to read wifi network credentials for CtsNetTestCases --> - <uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" /> + <!-- Permission needed to restart WiFi Subsystem --> + <uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" /> <!-- Permission needed to read wifi network credentials for CtsNetTestCases --> <uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL" /> @@ -345,6 +346,16 @@ <uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" /> <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> + <!-- Permission required for CTS test - CtsSensorPrivacyTestCases --> + <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" /> + + <!-- Permission required for GTS test - GtsAssistIntentTestCases --> + <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" /> + <uses-permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD" /> + <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" /> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> + <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 6ecf303c6dc8..249b1946d435 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -45,7 +45,7 @@ android_library { "WindowManager-Shell", "SystemUIPluginLib", "SystemUISharedLib", - "SystemUI-statsd", + "SystemUI-statsd", "SettingsLib", "androidx.viewpager2_viewpager2", "androidx.legacy_legacy-support-v4", diff --git a/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml b/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml index 8dcddc28c3b7..7880cbdd112c 100644 --- a/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml +++ b/packages/SystemUI/res/layout/screen_pinning_request_text_area.xml @@ -16,65 +16,72 @@ * limitations under the License. */ --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/screen_pinning_text_area" +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="?android:attr/colorAccent" - android:gravity="center_vertical"> + android:fillViewport="true"> - <TextView - android:id="@+id/screen_pinning_title" - android:layout_width="match_parent" + <RelativeLayout + android:id="@+id/screen_pinning_text_area" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingEnd="48dp" - android:paddingStart="48dp" - android:paddingTop="43dp" - android:text="@string/screen_pinning_title" - android:textColor="@android:color/white" - android:textSize="24sp" /> + android:background="?android:attr/colorAccent" + android:gravity="center_vertical"> - <TextView - android:id="@+id/screen_pinning_description" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_below="@id/screen_pinning_title" - android:paddingEnd="48dp" - android:paddingStart="48dp" - android:paddingTop="12.6dp" - android:text="@string/screen_pinning_description" - android:textColor="@android:color/white" - android:textSize="16sp" /> + <TextView + android:id="@+id/screen_pinning_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingEnd="48dp" + android:paddingStart="48dp" + android:paddingTop="43dp" + android:text="@string/screen_pinning_title" + android:textColor="@android:color/white" + android:textSize="24sp" /> - <Button - android:id="@+id/screen_pinning_ok_button" - style="@android:style/Widget.Material.Button" - android:layout_width="wrap_content" - android:layout_height="36dp" - android:layout_alignParentEnd="true" - android:layout_below="@+id/screen_pinning_description" - android:layout_marginEnd="40dp" - android:layout_marginTop="18dp" - android:background="@null" - android:paddingEnd="8dp" - android:paddingStart="8dp" - android:text="@string/screen_pinning_positive" - android:textColor="@android:color/white" - android:textSize="14sp" /> + <TextView + android:id="@+id/screen_pinning_description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/screen_pinning_title" + android:paddingEnd="48dp" + android:paddingStart="48dp" + android:paddingTop="12.6dp" + android:text="@string/screen_pinning_description" + android:textColor="@android:color/white" + android:textSize="16sp" /> - <Button - android:id="@+id/screen_pinning_cancel_button" - style="@android:style/Widget.Material.Button" - android:layout_width="wrap_content" - android:layout_height="36dp" - android:layout_alignTop="@id/screen_pinning_ok_button" - android:layout_marginEnd="4dp" - android:layout_toStartOf="@id/screen_pinning_ok_button" - android:background="@null" - android:paddingEnd="8dp" - android:paddingStart="8dp" - android:text="@string/screen_pinning_negative" - android:textColor="@android:color/white" - android:textSize="14sp" /> + <Button + android:id="@+id/screen_pinning_ok_button" + style="@android:style/Widget.Material.Button" + android:layout_width="wrap_content" + android:layout_height="36dp" + android:layout_alignParentEnd="true" + android:layout_below="@+id/screen_pinning_description" + android:layout_marginEnd="40dp" + android:layout_marginTop="18dp" + android:background="@null" + android:paddingEnd="8dp" + android:paddingStart="8dp" + android:text="@string/screen_pinning_positive" + android:textColor="@android:color/white" + android:textSize="14sp" /> + + <Button + android:id="@+id/screen_pinning_cancel_button" + style="@android:style/Widget.Material.Button" + android:layout_width="wrap_content" + android:layout_height="36dp" + android:layout_alignTop="@id/screen_pinning_ok_button" + android:layout_marginEnd="4dp" + android:layout_toStartOf="@id/screen_pinning_ok_button" + android:background="@null" + android:paddingEnd="8dp" + android:paddingStart="8dp" + android:text="@string/screen_pinning_negative" + android:textColor="@android:color/white" + android:textSize="14sp" /> + + </RelativeLayout> -</RelativeLayout> +</ScrollView> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 5a7c5c9b5ebc..f65f97a3a450 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -271,7 +271,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { float contentStart = getPaddingStart(); int childCount = getChildCount(); // Underflow === don't show content until that index - if (DEBUG) android.util.Log.d(TAG, "calculateIconTranslations: start=" + translationX + if (DEBUG) Log.d(TAG, "calculateIconTranslations: start=" + translationX + " width=" + width + " underflow=" + mNeedsUnderflow); // Collect all of the states which want to be visible diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 309d4b04ebbf..c5a35eaf3e6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -29,7 +29,6 @@ import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.IConnectivityManager; import android.net.Network; -import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.os.Handler; import android.os.RemoteException; @@ -66,12 +65,8 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi private static final String TAG = "SecurityController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final NetworkRequest REQUEST = new NetworkRequest.Builder() - .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - .setUids(null) - .build(); + private static final NetworkRequest REQUEST = + new NetworkRequest.Builder().clearCapabilities().build(); private static final int NO_NETWORK = -1; private static final String VPN_BRANDED_META_DATA = "com.android.systemui.IS_BRANDED"; diff --git a/packages/services/CameraExtensionsProxy/OWNERS b/packages/services/CameraExtensionsProxy/OWNERS new file mode 100644 index 000000000000..f48a95c5b3a3 --- /dev/null +++ b/packages/services/CameraExtensionsProxy/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/av:/camera/OWNERS diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java index ac402228a180..f8b9309f9a7f 100644 --- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java +++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyServer.java @@ -19,8 +19,8 @@ import android.os.RemoteException; import android.util.Log; import com.android.net.IProxyPortListener; + import com.google.android.collect.Lists; -import com.google.android.collect.Sets; import java.io.IOException; import java.io.InputStream; @@ -34,7 +34,6 @@ import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.util.List; -import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -361,7 +360,7 @@ public class ProxyServer extends Thread { try { mCallback.setProxyPort(port); } catch (RemoteException e) { - Log.w(TAG, "Proxy failed to report port to PacManager", e); + Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e); } } mPort = port; @@ -372,7 +371,7 @@ public class ProxyServer extends Thread { try { callback.setProxyPort(mPort); } catch (RemoteException e) { - Log.w(TAG, "Proxy failed to report port to PacManager", e); + Log.w(TAG, "Proxy failed to report port to PacProxyInstaller", e); } } mCallback = callback; diff --git a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java index 970fdc727ded..bdf478d36c8c 100644 --- a/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java +++ b/packages/services/Proxy/src/com/android/proxyhandler/ProxyService.java @@ -30,7 +30,7 @@ public class ProxyService extends Service { private static ProxyServer server = null; - /** Keep these values up-to-date with PacManager.java */ + /** Keep these values up-to-date with PacProxyInstaller.java */ public static final String KEY_PROXY = "keyProxy"; public static final String HOST = "localhost"; public static final String EXCL_LIST = ""; diff --git a/services/Android.bp b/services/Android.bp index f40f7cfa7321..a13dbe612528 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -83,7 +83,6 @@ java_library { "services.voiceinteraction", "services.wifi", "service-blobstore", - "service-connectivity", "service-jobscheduler", "android.hidl.base-V1.0-java", ], @@ -140,7 +139,7 @@ droidstubs { last_released: { api_file: ":android.api.system-server.latest", removed_api_file: ":removed.api.system-server.latest", - baseline_file: ":system-server-api-incompatibilities-with-last-released" + baseline_file: ":android-incompatibilities.api.system-server.latest" }, api_lint: { enabled: true, @@ -148,11 +147,20 @@ droidstubs { baseline_file: "api/lint-baseline.txt", }, }, - dist: { - targets: ["sdk", "win_sdk"], - dir: "apistubs/android/system-server/api", - dest: "android.txt", - }, + dists: [ + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + dest: "android.txt", + tag: ".api.txt" + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + dest: "removed.txt", + tag: ".removed-api.txt", + }, + ] } java_library { diff --git a/services/OWNERS b/services/OWNERS index 88d0b61a2ab6..03e0807eea62 100644 --- a/services/OWNERS +++ b/services/OWNERS @@ -1 +1,6 @@ per-file Android.bp = file:platform/build/soong:/OWNERS + +# art-team@ manages the system server profile +per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com + +per-file java/com/android/server/* = toddke@google.com diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS index c6f42f719caa..a31cfae995b2 100644 --- a/services/accessibility/OWNERS +++ b/services/accessibility/OWNERS @@ -1,4 +1,4 @@ svetoslavganov@google.com pweaver@google.com rhedjao@google.com -qasid@google.com +ryanlwlin@google.com diff --git a/services/appwidget/java/com/android/server/appwidget/OWNERS b/services/appwidget/java/com/android/server/appwidget/OWNERS new file mode 100644 index 000000000000..d724cac4aa3e --- /dev/null +++ b/services/appwidget/java/com/android/server/appwidget/OWNERS @@ -0,0 +1 @@ +include /core/java/android/appwidget/OWNERS diff --git a/services/core/Android.bp b/services/core/Android.bp index 01060ca94066..4bebe399b8bc 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -84,6 +84,7 @@ java_library_static { ":storaged_aidl", ":vold_aidl", ":platform-compat-config", + ":platform-compat-overrides", ":display-device-config", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", @@ -112,6 +113,9 @@ java_library_static { "time_zone_distro", "time_zone_distro_installer", "android.hardware.authsecret-V1.0-java", + "android.hardware.boot-V1.0-java", + "android.hardware.boot-V1.1-java", + "android.hardware.boot-V1.2-java", "android.hardware.broadcastradio-V2.0-java", "android.hardware.health-V1.0-java", "android.hardware.health-V2.0-java", @@ -189,21 +193,19 @@ filegroup { "java/com/android/server/connectivity/AutodestructReference.java", "java/com/android/server/connectivity/ConnectivityConstants.java", "java/com/android/server/connectivity/DataConnectionStats.java", - "java/com/android/server/connectivity/DefaultNetworkMetrics.java", "java/com/android/server/connectivity/DnsManager.java", - "java/com/android/server/connectivity/IpConnectivityEventBuilder.java", - "java/com/android/server/connectivity/IpConnectivityMetrics.java", "java/com/android/server/connectivity/KeepaliveTracker.java", "java/com/android/server/connectivity/LingerMonitor.java", "java/com/android/server/connectivity/MockableSystemProperties.java", "java/com/android/server/connectivity/Nat464Xlat.java", - "java/com/android/server/connectivity/NetdEventListenerService.java", "java/com/android/server/connectivity/NetworkAgentInfo.java", "java/com/android/server/connectivity/NetworkDiagnostics.java", "java/com/android/server/connectivity/NetworkNotificationManager.java", "java/com/android/server/connectivity/NetworkRanker.java", "java/com/android/server/connectivity/PermissionMonitor.java", "java/com/android/server/connectivity/ProxyTracker.java", + "java/com/android/server/connectivity/QosCallbackAgentConnection.java", + "java/com/android/server/connectivity/QosCallbackTracker.java", "java/com/android/server/connectivity/TcpKeepaliveController.java", "java/com/android/server/connectivity/Vpn.java", "java/com/android/server/connectivity/VpnIkev2Utils.java", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 397eeb23396c..c091dfa384ca 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -28,7 +28,6 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS; import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; -import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_NONE; @@ -56,12 +55,14 @@ import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired; import static android.os.Process.INVALID_UID; +import static android.os.Process.VPN_UID; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; import static java.util.Map.Entry; import android.Manifest; +import android.annotation.BoolRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; @@ -87,13 +88,13 @@ import android.net.ICaptivePortal; import android.net.IConnectivityDiagnosticsCallback; import android.net.IConnectivityManager; import android.net.IDnsResolver; -import android.net.IIpConnectivityMetrics; import android.net.INetd; import android.net.INetworkManagementEventObserver; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; import android.net.INetworkStatsService; +import android.net.IQosCallback; import android.net.ISocketKeepaliveCallback; import android.net.InetAddresses; import android.net.IpMemoryStore; @@ -121,12 +122,17 @@ import android.net.NetworkUtils; import android.net.NetworkWatchlistManager; import android.net.PrivateDnsConfigParcel; import android.net.ProxyInfo; +import android.net.QosCallbackException; +import android.net.QosFilter; +import android.net.QosSocketFilter; +import android.net.QosSocketInfo; import android.net.RouteInfo; import android.net.RouteInfoParcel; import android.net.SocketKeepalive; import android.net.TetheringManager; import android.net.UidRange; import android.net.UidRangeParcel; +import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; import android.net.VpnService; @@ -154,7 +160,6 @@ import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.SystemProperties; @@ -180,7 +185,6 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.logging.MetricsLogger; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; -import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnProfile; import com.android.internal.util.ArrayUtils; import com.android.internal.util.AsyncChannel; @@ -195,7 +199,6 @@ import com.android.server.connectivity.AutodestructReference; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.DnsManager; import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate; -import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.KeepaliveTracker; import com.android.server.connectivity.LingerMonitor; import com.android.server.connectivity.MockableSystemProperties; @@ -206,14 +209,13 @@ import com.android.server.connectivity.NetworkNotificationManager.NotificationTy import com.android.server.connectivity.NetworkRanker; import com.android.server.connectivity.PermissionMonitor; import com.android.server.connectivity.ProxyTracker; +import com.android.server.connectivity.QosCallbackTracker; import com.android.server.connectivity.Vpn; import com.android.server.net.BaseNetworkObserver; import com.android.server.net.LockdownVpnTracker; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.utils.PriorityDump; -import com.google.android.collect.Lists; - import libcore.io.IoUtils; import java.io.FileDescriptor; @@ -281,6 +283,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // Default to 30s linger time-out. Modifiable only for testing. private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger"; private static final int DEFAULT_LINGER_DELAY_MS = 30_000; + + // The maximum number of network request allowed per uid before an exception is thrown. + private static final int MAX_NETWORK_REQUESTS_PER_UID = 100; + @VisibleForTesting protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it. @@ -293,6 +299,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @VisibleForTesting protected final PermissionMonitor mPermissionMonitor; + private final PerUidCounter mNetworkRequestCounter; + private KeyStore mKeyStore; @VisibleForTesting @@ -315,6 +323,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private boolean mRestrictBackground; private final Context mContext; + // The Context is created for UserHandle.ALL. + private final Context mUserAllContext; private final Dependencies mDeps; // 0 is full bad, 100 is full good private int mDefaultInetConditionPublished = 0; @@ -616,11 +626,12 @@ public class ConnectivityService extends IConnectivityManager.Stub private final LocationPermissionChecker mLocationPermissionChecker; private KeepaliveTracker mKeepaliveTracker; + private QosCallbackTracker mQosCallbackTracker; private NetworkNotificationManager mNotifier; private LingerMonitor mLingerMonitor; // sequence number of NetworkRequests - private int mNextNetworkRequestId = 1; + private int mNextNetworkRequestId = NetworkRequest.FIRST_REQUEST_ID; // Sequence number for NetworkProvider IDs. private final AtomicInteger mNextNetworkProviderId = new AtomicInteger( @@ -860,6 +871,66 @@ public class ConnectivityService extends IConnectivityManager.Stub }; /** + * Keeps track of the number of requests made under different uids. + */ + public static class PerUidCounter { + private final int mMaxCountPerUid; + + // Map from UID to number of NetworkRequests that UID has filed. + @GuardedBy("mUidToNetworkRequestCount") + private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray(); + + /** + * Constructor + * + * @param maxCountPerUid the maximum count per uid allowed + */ + public PerUidCounter(final int maxCountPerUid) { + mMaxCountPerUid = maxCountPerUid; + } + + /** + * Increments the request count of the given uid. Throws an exception if the number + * of open requests for the uid exceeds the value of maxCounterPerUid which is the value + * passed into the constructor. see: {@link #PerUidCounter(int)}. + * + * @throws ServiceSpecificException with + * {@link ConnectivityManager.Errors.TOO_MANY_REQUESTS} if the number of requests for + * the uid exceed the allowed number. + * + * @param uid the uid that the request was made under + */ + public void incrementCountOrThrow(final int uid) { + synchronized (mUidToNetworkRequestCount) { + final int networkRequests = mUidToNetworkRequestCount.get(uid, 0) + 1; + if (networkRequests >= mMaxCountPerUid) { + throw new ServiceSpecificException( + ConnectivityManager.Errors.TOO_MANY_REQUESTS); + } + mUidToNetworkRequestCount.put(uid, networkRequests); + } + } + + /** + * Decrements the request count of the given uid. + * + * @param uid the uid that the request was made under + */ + public void decrementCount(final int uid) { + synchronized (mUidToNetworkRequestCount) { + final int requests = mUidToNetworkRequestCount.get(uid, 0); + if (requests < 1) { + logwtf("BUG: too small request count " + requests + " for UID " + uid); + } else if (requests == 1) { + mUidToNetworkRequestCount.delete(uid); + } else { + mUidToNetworkRequestCount.put(uid, requests - 1); + } + } + } + } + + /** * Dependencies of ConnectivityService, for injection in tests. */ @VisibleForTesting @@ -890,6 +961,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * Get a reference to the system keystore. + */ + public KeyStore getKeyStore() { + return KeyStore.getInstance(); + } + + /** * @see ProxyTracker */ public ProxyTracker makeProxyTracker(@NonNull Context context, @@ -919,22 +997,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return new MultinetworkPolicyTracker(c, h, r); } - /** - * @see IpConnectivityMetrics.Logger - */ - public IpConnectivityMetrics.Logger getMetricsLogger() { - return Objects.requireNonNull(LocalServices.getService(IpConnectivityMetrics.Logger.class), - "no IpConnectivityMetrics service"); - } - - /** - * @see IpConnectivityMetrics - */ - public IIpConnectivityMetrics getIpConnectivityMetrics() { - return IIpConnectivityMetrics.Stub.asInterface( - ServiceManager.getService(IpConnectivityLog.SERVICE_NAME)); - } - public IBatteryStats getBatteryStatsService() { return BatteryStatsService.getService(); } @@ -956,6 +1018,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mSystemProperties = mDeps.getSystemProperties(); mNetIdManager = mDeps.makeNetIdManager(); mContext = Objects.requireNonNull(context, "missing Context"); + mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID); mMetricsLog = logger; mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST); @@ -973,6 +1036,10 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultWifiRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST); + mDefaultVehicleRequest = createAlwaysOnRequestForCapability( + NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL, + NetworkRequest.Type.BACKGROUND_REQUEST); + mHandlerThread = mDeps.makeHandlerThread(); mHandlerThread.start(); mHandler = new InternalHandler(mHandlerThread.getLooper()); @@ -995,7 +1062,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler); mNetd = netd; - mKeyStore = KeyStore.getInstance(); + mKeyStore = mDeps.getKeyStore(); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mLocationPermissionChecker = new LocationPermissionChecker(mContext); @@ -1093,8 +1160,8 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_USER_REMOVED); intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); - final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); - userAllContext.registerReceiver( + mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); + mUserAllContext.registerReceiver( mIntentReceiver, intentFilter, null /* broadcastPermission */, @@ -1110,7 +1177,7 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); - userAllContext.registerReceiver( + mUserAllContext.registerReceiver( mIntentReceiver, intentFilter, null /* broadcastPermission */, @@ -1119,14 +1186,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // Listen to lockdown VPN reset. intentFilter = new IntentFilter(); intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET); - userAllContext.registerReceiver( + mUserAllContext.registerReceiver( mIntentReceiver, intentFilter, NETWORK_STACK, mHandler); - try { - mNMS.registerObserver(mDataActivityObserver); - } catch (RemoteException e) { - loge("Error registering observer :" + e); - } + mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS); mSettingsObserver = new SettingsObserver(mContext, mHandler); registerSettingsCallbacks(); @@ -1136,6 +1199,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler); mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager); + mQosCallbackTracker = new QosCallbackTracker(mHandler, mNetworkRequestCounter); final int dailyLimit = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, @@ -1172,6 +1236,15 @@ public class ConnectivityService extends IConnectivityManager.Stub return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type); } + private NetworkRequest createAlwaysOnRequestForCapability(int capability, + NetworkRequest.Type type) { + final NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.clearAll(); + netCap.addCapability(capability); + netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); + return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type); + } + // Used only for testing. // TODO: Delete this and either: // 1. Give FakeSettingsProvider the ability to send settings change notifications (requires @@ -1189,10 +1262,19 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED); } + private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, @BoolRes int id) { + final boolean enable = mContext.getResources().getBoolean(id); + handleAlwaysOnNetworkRequest(networkRequest, enable); + } + private void handleAlwaysOnNetworkRequest( NetworkRequest networkRequest, String settingName, boolean defaultValue) { final boolean enable = toBool(Settings.Global.getInt( mContext.getContentResolver(), settingName, encodeBool(defaultValue))); + handleAlwaysOnNetworkRequest(networkRequest, enable); + } + + private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, boolean enable) { final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null); if (enable == isEnabled) { return; // Nothing to do. @@ -1209,9 +1291,11 @@ public class ConnectivityService extends IConnectivityManager.Stub private void handleConfigureAlwaysOnNetworks() { handleAlwaysOnNetworkRequest( - mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true); + mDefaultMobileDataRequest, Settings.Global.MOBILE_DATA_ALWAYS_ON, true); handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED, false); + handleAlwaysOnNetworkRequest(mDefaultVehicleRequest, + com.android.internal.R.bool.config_vehicleInternalNetworkAlwaysRequested); } private void registerSettingsCallbacks() { @@ -1238,34 +1322,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } private synchronized int nextNetworkRequestId() { + // TODO: Consider handle wrapping and exclude {@link NetworkRequest#REQUEST_ID_NONE} if + // doing that. return mNextNetworkRequestId++; } - private NetworkState getFilteredNetworkState(int networkType, int uid) { - if (mLegacyTypeTracker.isTypeSupported(networkType)) { - final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); - final NetworkState state; - if (nai != null) { - state = nai.getNetworkState(); - state.networkInfo.setType(networkType); - } else { - final NetworkInfo info = new NetworkInfo(networkType, 0, - getNetworkTypeName(networkType), ""); - info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); - info.setIsAvailable(true); - final NetworkCapabilities capabilities = new NetworkCapabilities(); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, - !info.isRoaming()); - state = new NetworkState(info, new LinkProperties(), capabilities, - null, null, null); - } - filterNetworkStateForUid(state, uid, false); - return state; - } else { - return NetworkState.EMPTY; - } - } - @VisibleForTesting protected NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) { if (network == null) { @@ -1329,15 +1390,20 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Check if UID should be blocked from using the specified network. */ - private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid, - boolean ignoreBlocked) { + private boolean isNetworkWithCapabilitiesBlocked(@Nullable final NetworkCapabilities nc, + final int uid, final boolean ignoreBlocked) { // Networks aren't blocked when ignoring blocked status if (ignoreBlocked) { return false; } if (isUidBlockedByVpn(uid, mVpnBlockedUidRanges)) return true; - final String iface = (lp == null ? "" : lp.getInterfaceName()); - return mPolicyManagerInternal.isUidNetworkingBlocked(uid, iface); + final long ident = Binder.clearCallingIdentity(); + try { + final boolean metered = nc == null ? true : nc.isMetered(); + return mPolicyManager.isUidNetworkingBlocked(uid, metered); + } finally { + Binder.restoreCallingIdentity(ident); + } } private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) { @@ -1365,29 +1431,32 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } final String action = blocked ? "BLOCKED" : "UNBLOCKED"; - final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest(); - final int requestId = satisfiedRequest != null - ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId; + final int requestId = nri.getActiveRequest() != null + ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId; mNetworkInfoBlockingLogs.log(String.format( "%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId())); } + private void filterNetworkInfo(@NonNull NetworkInfo networkInfo, + @NonNull NetworkCapabilities nc, int uid, boolean ignoreBlocked) { + if (isNetworkWithCapabilitiesBlocked(nc, uid, ignoreBlocked)) { + networkInfo.setDetailedState(DetailedState.BLOCKED, null, null); + } + synchronized (mVpns) { + if (mLockdownTracker != null) { + mLockdownTracker.augmentNetworkInfo(networkInfo); + } + } + } + /** * Apply any relevant filters to {@link NetworkState} for the given UID. For * example, this may mark the network as {@link DetailedState#BLOCKED} based - * on {@link #isNetworkWithLinkPropertiesBlocked}. + * on {@link #isNetworkWithCapabilitiesBlocked}. */ private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) { if (state == null || state.networkInfo == null || state.linkProperties == null) return; - - if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) { - state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null); - } - synchronized (mVpns) { - if (mLockdownTracker != null) { - mLockdownTracker.augmentNetworkInfo(state.networkInfo); - } - } + filterNetworkInfo(state.networkInfo, state.networkCapabilities, uid, ignoreBlocked); } /** @@ -1420,31 +1489,20 @@ public class ConnectivityService extends IConnectivityManager.Stub } private Network getActiveNetworkForUidInternal(final int uid, boolean ignoreBlocked) { - final int user = UserHandle.getUserId(uid); - int vpnNetId = NETID_UNSET; - synchronized (mVpns) { - final Vpn vpn = mVpns.get(user); - // TODO : now that capabilities contain the UID, the appliesToUid test should - // be removed as the satisfying test below should be enough. - if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId(); - } - NetworkAgentInfo nai; - if (vpnNetId != NETID_UNSET) { - nai = getNetworkAgentInfoForNetId(vpnNetId); - if (nai != null) { - final NetworkCapabilities requiredCaps = - createDefaultNetworkCapabilitiesForUid(uid); - if (requiredCaps.satisfiedByNetworkCapabilities(nai.networkCapabilities)) { - return nai.network; - } + final NetworkAgentInfo vpnNai = getVpnForUid(uid); + if (vpnNai != null) { + final NetworkCapabilities requiredCaps = createDefaultNetworkCapabilitiesForUid(uid); + if (requiredCaps.satisfiedByNetworkCapabilities(vpnNai.networkCapabilities)) { + return vpnNai.network; } } - nai = getDefaultNetwork(); - if (nai != null - && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, ignoreBlocked)) { - nai = null; + + NetworkAgentInfo nai = getDefaultNetwork(); + if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, + ignoreBlocked)) { + return null; } - return nai != null ? nai.network : null; + return nai.network; } // Public because it's used by mLockdownTracker. @@ -1463,6 +1521,27 @@ public class ConnectivityService extends IConnectivityManager.Stub return state.networkInfo; } + private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) { + if (!mLegacyTypeTracker.isTypeSupported(networkType)) { + return null; + } + final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + final NetworkInfo info; + final NetworkCapabilities nc; + if (nai != null) { + info = new NetworkInfo(nai.networkInfo); + info.setType(networkType); + nc = nai.networkCapabilities; + } else { + info = new NetworkInfo(networkType, 0, getNetworkTypeName(networkType), ""); + info.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); + info.setIsAvailable(true); + nc = new NetworkCapabilities(); + } + filterNetworkInfo(info, nc, uid, false); + return info; + } + @Override public NetworkInfo getNetworkInfo(int networkType) { enforceAccessPermission(); @@ -1477,8 +1556,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return state.networkInfo; } } - final NetworkState state = getFilteredNetworkState(networkType, uid); - return state.networkInfo; + return getFilteredNetworkInfo(networkType, uid); } @Override @@ -1497,7 +1575,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkInfo[] getAllNetworkInfo() { enforceAccessPermission(); - final ArrayList<NetworkInfo> result = Lists.newArrayList(); + final ArrayList<NetworkInfo> result = new ArrayList<>(); for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE; networkType++) { NetworkInfo info = getNetworkInfo(networkType); @@ -1511,10 +1589,16 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public Network getNetworkForType(int networkType) { enforceAccessPermission(); + if (!mLegacyTypeTracker.isTypeSupported(networkType)) { + return null; + } + final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + if (nai == null) { + return null; + } final int uid = mDeps.getCallingUid(); - NetworkState state = getFilteredNetworkState(networkType, uid); - if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) { - return state.network; + if (!isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid, false)) { + return nai.network; } return null; } @@ -1557,7 +1641,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nc != null) { result.put( nai.network, - maybeSanitizeLocationInfoForCaller( + createWithLocationInfoSanitizedIfNecessaryWhenParceled( nc, mDeps.getCallingUid(), callingPackageName)); } @@ -1567,7 +1651,9 @@ public class ConnectivityService extends IConnectivityManager.Stub for (Network network : networks) { nc = getNetworkCapabilitiesInternal(network); if (nc != null) { - result.put(network, maybeSanitizeLocationInfoForCaller( + result.put( + network, + createWithLocationInfoSanitizedIfNecessaryWhenParceled( nc, mDeps.getCallingUid(), callingPackageName)); } } @@ -1639,7 +1725,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) { if (nai == null) return null; synchronized (nai) { - if (nai.networkCapabilities == null) return null; return networkCapabilitiesRestrictedForCallerPermissions( nai.networkCapabilities, Binder.getCallingPid(), mDeps.getCallingUid()); } @@ -1649,7 +1734,7 @@ public class ConnectivityService extends IConnectivityManager.Stub public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) { mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName); enforceAccessPermission(); - return maybeSanitizeLocationInfoForCaller( + return createWithLocationInfoSanitizedIfNecessaryWhenParceled( getNetworkCapabilitiesInternal(network), mDeps.getCallingUid(), callingPackageName); } @@ -1670,37 +1755,51 @@ public class ConnectivityService extends IConnectivityManager.Stub return newNc; } + private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) { + final long token = Binder.clearCallingIdentity(); + try { + return mLocationPermissionChecker.checkLocationPermission( + callerPkgName, null /* featureId */, callerUid, null /* message */); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @VisibleForTesting @Nullable - NetworkCapabilities maybeSanitizeLocationInfoForCaller( + NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled( @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) { if (nc == null) { return null; } - final NetworkCapabilities newNc = new NetworkCapabilities(nc); - if (callerUid != newNc.getOwnerUid()) { + Boolean hasLocationPermission = null; + final NetworkCapabilities newNc; + // Avoid doing location permission check if the transport info has no location sensitive + // data. + if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) { + hasLocationPermission = hasLocationPermission(callerUid, callerPkgName); + newNc = new NetworkCapabilities(nc, hasLocationPermission); + } else { + newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */); + } + // Reset owner uid if not destined for the owner app. + if (callerUid != nc.getOwnerUid()) { newNc.setOwnerUid(INVALID_UID); return newNc; } - // Allow VPNs to see ownership of their own VPN networks - not location sensitive. if (nc.hasTransport(TRANSPORT_VPN)) { // Owner UIDs already checked above. No need to re-check. return newNc; } - - final long token = Binder.clearCallingIdentity(); - try { - if (!mLocationPermissionChecker.checkLocationPermission( - callerPkgName, null /* featureId */, callerUid, null /* message */)) { - // Caller does not have the requisite location permissions. Reset the - // owner's UID in the NetworkCapabilities. - newNc.setOwnerUid(INVALID_UID); - } - } finally { - Binder.restoreCallingIdentity(token); + if (hasLocationPermission == null) { + // Location permission not checked yet, check now for masking owner UID. + hasLocationPermission = hasLocationPermission(callerUid, callerPkgName); + } + // Reset owner uid if the app has no location permission. + if (!hasLocationPermission) { + newNc.setOwnerUid(INVALID_UID); } - return newNc; } @@ -1750,7 +1849,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // This contains IMSI details, so make sure the caller is privileged. NetworkStack.checkNetworkStackPermission(mContext); - final ArrayList<NetworkState> result = Lists.newArrayList(); + final ArrayList<NetworkState> result = new ArrayList<>(); for (Network network : getAllNetworks()) { final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); if (nai != null) { @@ -1777,14 +1876,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() { - @Override - public void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos, - int uid) { - sendDataActivityBroadcast(networkType, active, tsNanos); - } - }; - /** * Ensures that the system cannot call a particular method. */ @@ -2233,20 +2324,6 @@ public class ConnectivityService extends IConnectivityManager.Stub sendStickyBroadcast(makeGeneralIntent(info, bcastType)); } - private void sendDataActivityBroadcast(int deviceType, boolean active, long tsNanos) { - Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE); - intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType); - intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active); - intent.putExtra(ConnectivityManager.EXTRA_REALTIME_NS, tsNanos); - final long ident = Binder.clearCallingIdentity(); - try { - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, - RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - private void sendStickyBroadcast(Intent intent) { synchronized (this) { if (!mSystemReady @@ -2276,7 +2353,7 @@ public class ConnectivityService extends IConnectivityManager.Stub intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); } try { - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options); + mUserAllContext.sendStickyBroadcast(intent, options); } finally { Binder.restoreCallingIdentity(ident); } @@ -2352,74 +2429,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Setup data activity tracking for the given network. - * - * Every {@code setupDataActivityTracking} should be paired with a - * {@link #removeDataActivityTracking} for cleanup. - */ - private void setupDataActivityTracking(NetworkAgentInfo networkAgent) { - final String iface = networkAgent.linkProperties.getInterfaceName(); - - final int timeout; - final int type; - - if (networkAgent.networkCapabilities.hasTransport( - NetworkCapabilities.TRANSPORT_CELLULAR)) { - timeout = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE, - 10); - type = ConnectivityManager.TYPE_MOBILE; - } else if (networkAgent.networkCapabilities.hasTransport( - NetworkCapabilities.TRANSPORT_WIFI)) { - timeout = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI, - 15); - type = ConnectivityManager.TYPE_WIFI; - } else { - return; // do not track any other networks - } - - if (timeout > 0 && iface != null) { - try { - mNMS.addIdleTimer(iface, timeout, type); - } catch (Exception e) { - // You shall not crash! - loge("Exception in setupDataActivityTracking " + e); - } - } - } - - /** - * Remove data activity tracking when network disconnects. - */ - private void removeDataActivityTracking(NetworkAgentInfo networkAgent) { - final String iface = networkAgent.linkProperties.getInterfaceName(); - final NetworkCapabilities caps = networkAgent.networkCapabilities; - - if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || - caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) { - try { - // the call fails silently if no idle timer setup for this interface - mNMS.removeIdleTimer(iface); - } catch (Exception e) { - loge("Exception in removeDataActivityTracking " + e); - } - } - } - - /** - * Update data activity tracking when network state is updated. - */ - private void updateDataActivityTracking(NetworkAgentInfo newNetwork, - NetworkAgentInfo oldNetwork) { - if (newNetwork != null) { - setupDataActivityTracking(newNetwork); - } - if (oldNetwork != null) { - removeDataActivityTracking(oldNetwork); - } - } - /** * Reads the network specific MTU size from resources. * and set it on it's iface. */ @@ -2722,7 +2731,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @VisibleForTesting NetworkRequestInfo[] requestsSortedById() { NetworkRequestInfo[] requests = new NetworkRequestInfo[0]; - requests = mNetworkRequests.values().toArray(requests); + requests = getNrisFromGlobalRequests().toArray(requests); // Sort the array based off the NRI containing the min requestId in its requests. Arrays.sort(requests, Comparator.comparingInt(nri -> Collections.min(nri.mRequests, @@ -2733,7 +2742,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } private boolean isLiveNetworkAgent(NetworkAgentInfo nai, int what) { - if (nai.network == null) return false; final NetworkAgentInfo officialNai = getNetworkAgentInfoForNetwork(nai.network); if (officialNai != null && officialNai.equals(nai)) return true; if (officialNai != null || VDBG) { @@ -2834,13 +2842,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Log.wtf(TAG, "Non-virtual networks cannot have underlying networks"); break; } - final ArrayList<Network> underlying; - try { - underlying = ((Bundle) arg.second).getParcelableArrayList( - NetworkAgent.UNDERLYING_NETWORKS_KEY); - } catch (NullPointerException | ClassCastException e) { - break; - } + final List<Network> underlying = (List<Network>) arg.second; final Network[] oldUnderlying = nai.declaredUnderlyingNetworks; nai.declaredUnderlyingNetworks = (underlying != null) ? underlying.toArray(new Network[0]) : null; @@ -2853,6 +2855,7 @@ public class ConnectivityService extends IConnectivityManager.Stub updateCapabilitiesForNetwork(nai); notifyIfacesChangedForNetworkStats(); } + break; } } } @@ -2949,7 +2952,7 @@ public class ConnectivityService extends IConnectivityManager.Stub case EVENT_CAPPORT_DATA_CHANGED: { final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2); if (nai == null) break; - handleCaptivePortalDataUpdate(nai, (CaptivePortalData) msg.obj); + handleCapportApiDataUpdate(nai, (CaptivePortalData) msg.obj); break; } } @@ -2975,9 +2978,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (valid != nai.lastValidated) { if (wasDefault) { - mDeps.getMetricsLogger() - .defaultNetworkMetrics().logDefaultNetworkValidity( - SystemClock.elapsedRealtime(), valid); + mMetricsLog.logDefaultNetworkValidity(valid); } final int oldScore = nai.getCurrentScore(); nai.lastValidated = valid; @@ -3289,9 +3290,9 @@ public class ConnectivityService extends IConnectivityManager.Stub handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); } - private void handleCaptivePortalDataUpdate(@NonNull final NetworkAgentInfo nai, + private void handleCapportApiDataUpdate(@NonNull final NetworkAgentInfo nai, @Nullable final CaptivePortalData data) { - nai.captivePortalData = data; + nai.capportApiData = data; // CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); } @@ -3405,7 +3406,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence // whose timestamps tell how long it takes to recover a default network. long now = SystemClock.elapsedRealtime(); - mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai); + mMetricsLog.logDefaultNetworkEvent(null, 0, false, + null /* lp */, null /* nc */, nai.network, nai.getCurrentScore(), + nai.linkProperties, nai.networkCapabilities); } notifyIfacesChangedForNetworkStats(); // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied @@ -3414,6 +3417,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // of rematchAllNetworksAndRequests notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST); mKeepaliveTracker.handleStopAllKeepalives(nai, SocketKeepalive.ERROR_INVALID_NETWORK); + + mQosCallbackTracker.handleNetworkReleased(nai.network); for (String iface : nai.linkProperties.getAllInterfaceNames()) { // Disable wakeup packet monitoring for each interface. wakeupModifyInterface(iface, nai.networkCapabilities, false); @@ -3426,22 +3431,25 @@ public class ConnectivityService extends IConnectivityManager.Stub // available until we've told netd to delete it below. mNetworkForNetId.remove(nai.network.getNetId()); } + propagateUnderlyingNetworkCapabilities(nai.network); // Remove all previously satisfied requests. for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest request = nai.requestAt(i); final NetworkRequestInfo nri = mNetworkRequests.get(request); - final NetworkAgentInfo currentNetwork = nri.mSatisfier; + final NetworkAgentInfo currentNetwork = nri.getSatisfier(); if (currentNetwork != null && currentNetwork.network.getNetId() == nai.network.getNetId()) { - nri.mSatisfier = null; + nri.setSatisfier(null, null); sendUpdatedScoreToFactories(request, null); } } nai.clearLingerState(); - propagateUnderlyingNetworkCapabilities(nai.network); + // TODO: this loop, and the mLegacyTypeTracker.remove just below it, seem redundant given + // there's a full rematch right after. Currently, deleting it breaks tests that check for + // the default network disconnecting. Find out why, fix the rematch code, and delete this. if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) { mDefaultNetworkNai = null; - updateDataActivityTracking(null /* newNetwork */, nai); + mNetworkActivityTracker.updateDataActivityTracking(null /* newNetwork */, nai); notifyLockdownVpn(nai); ensureNetworkTransitionWakelock(nai.toShortString()); } @@ -3509,42 +3517,63 @@ public class ConnectivityService extends IConnectivityManager.Stub return null; } - private void handleRegisterNetworkRequestWithIntent(Message msg) { + private void handleRegisterNetworkRequestWithIntent(@NonNull final Message msg) { final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj); - - NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent); + // handleRegisterNetworkRequestWithIntent() doesn't apply to multilayer requests. + ensureNotMultilayerRequest(nri, "handleRegisterNetworkRequestWithIntent"); + final NetworkRequestInfo existingRequest = + findExistingNetworkRequestInfo(nri.mPendingIntent); if (existingRequest != null) { // remove the existing request. - if (DBG) log("Replacing " + existingRequest.request + " with " - + nri.request + " because their intents matched."); - handleReleaseNetworkRequest(existingRequest.request, getCallingUid(), + if (DBG) { + log("Replacing " + existingRequest.mRequests.get(0) + " with " + + nri.mRequests.get(0) + " because their intents matched."); + } + handleReleaseNetworkRequest(existingRequest.mRequests.get(0), getCallingUid(), /* callOnUnavailable */ false); } handleRegisterNetworkRequest(nri); } - private void handleRegisterNetworkRequest(NetworkRequestInfo nri) { + private void handleRegisterNetworkRequest(@NonNull final NetworkRequestInfo nri) { ensureRunningOnConnectivityServiceThread(); - mNetworkRequests.put(nri.request, nri); mNetworkRequestInfoLogs.log("REGISTER " + nri); - if (nri.request.isListen()) { - for (NetworkAgentInfo network : mNetworkAgentInfos) { - if (nri.request.networkCapabilities.hasSignalStrength() && - network.satisfiesImmutableCapabilitiesOf(nri.request)) { - updateSignalStrengthThresholds(network, "REGISTER", nri.request); + for (final NetworkRequest req : nri.mRequests) { + mNetworkRequests.put(req, nri); + if (req.isListen()) { + for (final NetworkAgentInfo network : mNetworkAgentInfos) { + if (req.networkCapabilities.hasSignalStrength() + && network.satisfiesImmutableCapabilitiesOf(req)) { + updateSignalStrengthThresholds(network, "REGISTER", req); + } } } } rematchAllNetworksAndRequests(); - if (nri.request.isRequest() && nri.mSatisfier == null) { - sendUpdatedScoreToFactories(nri.request, null); + // If an active request exists, return as its score has already been sent if needed. + if (null != nri.getActiveRequest()) { + return; + } + + // As this request was not satisfied on rematch and thus never had any scores sent to the + // factories, send null now for each request of type REQUEST. + for (final NetworkRequest req : nri.mRequests) { + if (!req.isRequest()) { + continue; + } + sendUpdatedScoreToFactories(req, null); } } - private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent, - int callingUid) { - NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent); + private void handleReleaseNetworkRequestWithIntent(@NonNull final PendingIntent pendingIntent, + final int callingUid) { + final NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent); if (nri != null) { - handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false); + // handleReleaseNetworkRequestWithIntent() paths don't apply to multilayer requests. + ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequestWithIntent"); + handleReleaseNetworkRequest( + nri.mRequests.get(0), + callingUid, + /* callOnUnavailable */ false); } } @@ -3598,6 +3627,11 @@ public class ConnectivityService extends IConnectivityManager.Stub return false; } for (final NetworkRequest req : nri.mRequests) { + // This multilayer listen request is satisfied therefore no further requests need to be + // evaluated deeming this network not a potential satisfier. + if (req.isListen() && nri.getActiveRequest() == req) { + return false; + } // As non-multilayer listen requests have already returned, the below would only happen // for a multilayer request therefore continue to the next request if available. if (req.isListen()) { @@ -3618,7 +3652,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 2. Unvalidated WiFi will not be reaped when validated cellular // is currently satisfying the request. This is desirable when // WiFi ends up validating and out scoring cellular. - || nri.mSatisfier.getCurrentScore() + || nri.getSatisfier().getCurrentScore() < candidate.getCurrentScoreAsValidated(); return isNetworkNeeded; } @@ -3643,30 +3677,45 @@ public class ConnectivityService extends IConnectivityManager.Stub return nri; } - private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) { + private void ensureNotMultilayerRequest(@NonNull final NetworkRequestInfo nri, + final String callingMethod) { + if (nri.isMultilayerRequest()) { + throw new IllegalStateException( + callingMethod + " does not support multilayer requests."); + } + } + + private void handleTimedOutNetworkRequest(@NonNull final NetworkRequestInfo nri) { ensureRunningOnConnectivityServiceThread(); - if (mNetworkRequests.get(nri.request) == null) { + // handleTimedOutNetworkRequest() is part of the requestNetwork() flow which works off of a + // single NetworkRequest and thus does not apply to multilayer requests. + ensureNotMultilayerRequest(nri, "handleTimedOutNetworkRequest"); + if (mNetworkRequests.get(nri.mRequests.get(0)) == null) { return; } - if (nri.mSatisfier != null) { + if (nri.getSatisfier() != null) { return; } - if (VDBG || (DBG && nri.request.isRequest())) { - log("releasing " + nri.request + " (timeout)"); + if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) { + log("releasing " + nri.mRequests.get(0) + " (timeout)"); } handleRemoveNetworkRequest(nri); - callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0); + callCallbackForRequest( + nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0); } - private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid, - boolean callOnUnavailable) { + private void handleReleaseNetworkRequest(@NonNull final NetworkRequest request, + final int callingUid, + final boolean callOnUnavailable) { final NetworkRequestInfo nri = getNriForAppRequest(request, callingUid, "release NetworkRequest"); if (nri == null) { return; } - if (VDBG || (DBG && nri.request.isRequest())) { - log("releasing " + nri.request + " (release request)"); + // handleReleaseNetworkRequest() paths don't apply to multilayer requests. + ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequest"); + if (VDBG || (DBG && request.isRequest())) { + log("releasing " + request + " (release request)"); } handleRemoveNetworkRequest(nri); if (callOnUnavailable) { @@ -3674,42 +3723,88 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) { + private void handleRemoveNetworkRequest(@NonNull final NetworkRequestInfo nri) { ensureRunningOnConnectivityServiceThread(); nri.unlinkDeathRecipient(); - mNetworkRequests.remove(nri.request); + for (final NetworkRequest req : nri.mRequests) { + mNetworkRequests.remove(req); + if (req.isListen()) { + removeListenRequestFromNetworks(req); + } + } + mNetworkRequestCounter.decrementCount(nri.mUid); + mNetworkRequestInfoLogs.log("RELEASE " + nri); - decrementNetworkRequestPerUidCount(nri); + if (null != nri.getActiveRequest()) { + if (nri.getActiveRequest().isRequest()) { + removeSatisfiedNetworkRequestFromNetwork(nri); + } else { + nri.setSatisfier(null, null); + } + } - mNetworkRequestInfoLogs.log("RELEASE " + nri); - if (nri.request.isRequest()) { - boolean wasKept = false; - final NetworkAgentInfo nai = nri.mSatisfier; - if (nai != null) { - boolean wasBackgroundNetwork = nai.isBackgroundNetwork(); - nai.removeRequest(nri.request.requestId); - if (VDBG || DDBG) { - log(" Removing from current network " + nai.toShortString() - + ", leaving " + nai.numNetworkRequests() + " requests."); - } - // If there are still lingered requests on this network, don't tear it down, - // but resume lingering instead. - final long now = SystemClock.elapsedRealtime(); - if (updateLingerState(nai, now)) { - notifyNetworkLosing(nai, now); - } - if (unneeded(nai, UnneededFor.TEARDOWN)) { - if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting"); - teardownUnneededNetwork(nai); - } else { - wasKept = true; - } - nri.mSatisfier = null; - if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) { - // Went from foreground to background. - updateCapabilitiesForNetwork(nai); - } + cancelNpiRequests(nri); + } + + private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) { + for (final NetworkRequest req : nri.mRequests) { + cancelNpiRequest(req); + } + } + + private void cancelNpiRequest(@NonNull final NetworkRequest req) { + if (req.isRequest()) { + for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) { + npi.cancelRequest(req); + } + } + } + + private void removeListenRequestFromNetworks(@NonNull final NetworkRequest req) { + // listens don't have a singular affected Network. Check all networks to see + // if this listen request applies and remove it. + for (final NetworkAgentInfo nai : mNetworkAgentInfos) { + nai.removeRequest(req.requestId); + if (req.networkCapabilities.hasSignalStrength() + && nai.satisfiesImmutableCapabilitiesOf(req)) { + updateSignalStrengthThresholds(nai, "RELEASE", req); + } + } + } + + /** + * Remove a NetworkRequestInfo's satisfied request from its 'satisfier' (NetworkAgentInfo) and + * manage the necessary upkeep (linger, teardown networks, etc.) when doing so. + * @param nri the NetworkRequestInfo to disassociate from its current NetworkAgentInfo + */ + private void removeSatisfiedNetworkRequestFromNetwork(@NonNull final NetworkRequestInfo nri) { + boolean wasKept = false; + final NetworkAgentInfo nai = nri.getSatisfier(); + if (nai != null) { + final int requestLegacyType = nri.getActiveRequest().legacyType; + final boolean wasBackgroundNetwork = nai.isBackgroundNetwork(); + nai.removeRequest(nri.getActiveRequest().requestId); + if (VDBG || DDBG) { + log(" Removing from current network " + nai.toShortString() + + ", leaving " + nai.numNetworkRequests() + " requests."); + } + // If there are still lingered requests on this network, don't tear it down, + // but resume lingering instead. + final long now = SystemClock.elapsedRealtime(); + if (updateLingerState(nai, now)) { + notifyNetworkLosing(nai, now); + } + if (unneeded(nai, UnneededFor.TEARDOWN)) { + if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting"); + teardownUnneededNetwork(nai); + } else { + wasKept = true; + } + nri.setSatisfier(null, null); + if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) { + // Went from foreground to background. + updateCapabilitiesForNetwork(nai); } // Maintain the illusion. When this request arrived, we might have pretended @@ -3717,15 +3812,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // connected. Now that this request has gone away, we might have to pretend // that the network disconnected. LegacyTypeTracker will generate that // phantom disconnect for this type. - if (nri.request.legacyType != TYPE_NONE && nai != null) { + if (requestLegacyType != TYPE_NONE) { boolean doRemove = true; if (wasKept) { // check if any of the remaining requests for this network are for the // same legacy type - if so, don't remove the nai for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest otherRequest = nai.requestAt(i); - if (otherRequest.legacyType == nri.request.legacyType && - otherRequest.isRequest()) { + if (otherRequest.legacyType == requestLegacyType + && otherRequest.isRequest()) { if (DBG) log(" still have other legacy request - leaving"); doRemove = false; } @@ -3733,39 +3828,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (doRemove) { - mLegacyTypeTracker.remove(nri.request.legacyType, nai, false); - } - } - - for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) { - npi.cancelRequest(nri.request); - } - } else { - // listens don't have a singular affectedNetwork. Check all networks to see - // if this listen request applies and remove it. - for (NetworkAgentInfo nai : mNetworkAgentInfos) { - nai.removeRequest(nri.request.requestId); - if (nri.request.networkCapabilities.hasSignalStrength() && - nai.satisfiesImmutableCapabilitiesOf(nri.request)) { - updateSignalStrengthThresholds(nai, "RELEASE", nri.request); + mLegacyTypeTracker.remove(requestLegacyType, nai, false); } } } } - private void decrementNetworkRequestPerUidCount(final NetworkRequestInfo nri) { - synchronized (mUidToNetworkRequestCount) { - final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0); - if (requests < 1) { - Log.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid); - } else if (requests == 1) { - mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid)); - } else { - mUidToNetworkRequestCount.put(nri.mUid, requests - 1); - } - } - } - @Override public void setAcceptUnvalidated(Network network, boolean accept, boolean always) { enforceNetworkStackSettingsOrSetup(); @@ -4471,7 +4539,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!nai.everConnected) { return; } - if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) { + final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai); + if (isNetworkWithCapabilitiesBlocked(nc, uid, false)) { return; } nai.networkMonitor().forceReevaluation(uid); @@ -4591,6 +4660,10 @@ public class ConnectivityService extends IConnectivityManager.Stub Log.w(TAG, s); } + private static void logwtf(String s) { + Log.wtf(TAG, s); + } + private static void loge(String s) { Log.e(TAG, s); } @@ -4783,28 +4856,28 @@ public class ConnectivityService extends IConnectivityManager.Stub * * <p>Must be called on the handler thread. */ - private VpnInfo[] getAllVpnInfo() { + private UnderlyingNetworkInfo[] getAllVpnInfo() { ensureRunningOnConnectivityServiceThread(); synchronized (mVpns) { if (mLockdownEnabled) { - return new VpnInfo[0]; + return new UnderlyingNetworkInfo[0]; } - List<VpnInfo> infoList = new ArrayList<>(); - for (NetworkAgentInfo nai : mNetworkAgentInfos) { - VpnInfo info = createVpnInfo(nai); - if (info != null) { - infoList.add(info); - } + } + List<UnderlyingNetworkInfo> infoList = new ArrayList<>(); + for (NetworkAgentInfo nai : mNetworkAgentInfos) { + UnderlyingNetworkInfo info = createVpnInfo(nai); + if (info != null) { + infoList.add(info); } - return infoList.toArray(new VpnInfo[infoList.size()]); } + return infoList.toArray(new UnderlyingNetworkInfo[infoList.size()]); } /** * @return VPN information for accounting, or null if we can't retrieve all required * information, e.g underlying ifaces. */ - private VpnInfo createVpnInfo(NetworkAgentInfo nai) { + private UnderlyingNetworkInfo createVpnInfo(NetworkAgentInfo nai) { if (!nai.isVPN()) return null; Network[] underlyingNetworks = nai.declaredUnderlyingNetworks; @@ -4833,16 +4906,14 @@ public class ConnectivityService extends IConnectivityManager.Stub if (interfaces.isEmpty()) return null; - VpnInfo info = new VpnInfo(); - info.ownerUid = nai.networkCapabilities.getOwnerUid(); - info.vpnIface = nai.linkProperties.getInterfaceName(); // Must be non-null or NetworkStatsService will crash. // Cannot happen in production code because Vpn only registers the NetworkAgent after the // tun or ipsec interface is created. - if (info.vpnIface == null) return null; - info.underlyingIfaces = interfaces.toArray(new String[0]); + // TODO: Remove this check. + if (nai.linkProperties.getInterfaceName() == null) return null; - return info; + return new UnderlyingNetworkInfo(nai.networkCapabilities.getOwnerUid(), + nai.linkProperties.getInterfaceName(), interfaces); } /** @@ -4948,16 +5019,23 @@ public class ConnectivityService extends IConnectivityManager.Stub mVpnBlockedUidRanges = newVpnBlockedUidRanges; } + private boolean isLockdownVpnEnabled() { + return mKeyStore.contains(Credentials.LOCKDOWN_VPN); + } + @Override public boolean updateLockdownVpn() { - if (mDeps.getCallingUid() != Process.SYSTEM_UID) { - logw("Lockdown VPN only available to AID_SYSTEM"); + // Allow the system UID for the system server and for Settings. + // Also, for unit tests, allow the process that ConnectivityService is running in. + if (mDeps.getCallingUid() != Process.SYSTEM_UID + && Binder.getCallingPid() != Process.myPid()) { + logw("Lockdown VPN only available to system process or AID_SYSTEM"); return false; } synchronized (mVpns) { // Tear down existing lockdown if profile was removed - mLockdownEnabled = LockdownVpnTracker.isEnabled(); + mLockdownEnabled = isLockdownVpnEnabled(); if (mLockdownEnabled) { byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN); if (profileTag == null) { @@ -4978,7 +5056,8 @@ public class ConnectivityService extends IConnectivityManager.Stub logw("VPN for user " + user + " not ready yet. Skipping lockdown"); return false; } - setLockdownTracker(new LockdownVpnTracker(mContext, this, mHandler, vpn, profile)); + setLockdownTracker( + new LockdownVpnTracker(mContext, this, mHandler, mKeyStore, vpn, profile)); } else { setLockdownTracker(null); } @@ -5066,7 +5145,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mVpns) { // Can't set always-on VPN if legacy VPN is already in lockdown mode. - if (LockdownVpnTracker.isEnabled()) { + if (isLockdownVpnEnabled()) { return false; } @@ -5172,7 +5251,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, mNetd, userId, mKeyStore); mVpns.put(userId, userVpn); - if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) { + if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) { updateLockdownVpn(); } } @@ -5256,7 +5335,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserUnlocked(int userId) { synchronized (mVpns) { // User present may be sent because of an unlock, which might mean an unlocked keystore. - if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) { + if (mUserManager.getUserInfo(userId).isPrimary() && isLockdownVpnEnabled()) { updateLockdownVpn(); } else { startAlwaysOnVpn(userId); @@ -5325,11 +5404,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>(); private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>(); - private static final int MAX_NETWORK_REQUESTS_PER_UID = 100; - // Map from UID to number of NetworkRequests that UID has filed. - @GuardedBy("mUidToNetworkRequestCount") - private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray(); - private static class NetworkProviderInfo { public final String name; public final Messenger messenger; @@ -5415,18 +5489,38 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Tracks info about the requester. - * Also used to notice when the calling process dies so we can self-expire + * Also used to notice when the calling process dies so as to self-expire */ @VisibleForTesting protected class NetworkRequestInfo implements IBinder.DeathRecipient { final List<NetworkRequest> mRequests; - final NetworkRequest request; + + // mSatisfier and mActiveRequest rely on one another therefore set them together. + void setSatisfier( + @Nullable final NetworkAgentInfo satisfier, + @Nullable final NetworkRequest activeRequest) { + mSatisfier = satisfier; + mActiveRequest = activeRequest; + } // The network currently satisfying this request, or null if none. Must only be touched // on the handler thread. This only makes sense for network requests and not for listens, // as defined by NetworkRequest#isRequest(). For listens, this is always null. @Nullable - NetworkAgentInfo mSatisfier; + private NetworkAgentInfo mSatisfier; + NetworkAgentInfo getSatisfier() { + return mSatisfier; + } + + // The request in mRequests assigned to a network agent. This is null if none of the + // requests in mRequests can be satisfied. This member has the constraint of only being + // accessible on the handler thread. + @Nullable + private NetworkRequest mActiveRequest; + NetworkRequest getActiveRequest() { + return mActiveRequest; + } + final PendingIntent mPendingIntent; boolean mPendingIntentSent; private final IBinder mBinder; @@ -5435,7 +5529,6 @@ public class ConnectivityService extends IConnectivityManager.Stub final Messenger messenger; NetworkRequestInfo(NetworkRequest r, PendingIntent pi) { - request = r; mRequests = initializeRequests(r); ensureAllNetworkRequestsHaveType(mRequests); mPendingIntent = pi; @@ -5443,20 +5536,19 @@ public class ConnectivityService extends IConnectivityManager.Stub mBinder = null; mPid = getCallingPid(); mUid = mDeps.getCallingUid(); - enforceRequestCountLimit(); + mNetworkRequestCounter.incrementCountOrThrow(mUid); } NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) { super(); messenger = m; - request = r; mRequests = initializeRequests(r); ensureAllNetworkRequestsHaveType(mRequests); mBinder = binder; mPid = getCallingPid(); mUid = mDeps.getCallingUid(); mPendingIntent = null; - enforceRequestCountLimit(); + mNetworkRequestCounter.incrementCountOrThrow(mUid); try { mBinder.linkToDeath(this, 0); @@ -5479,31 +5571,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return Collections.unmodifiableList(tempRequests); } - private NetworkRequest getSatisfiedRequest() { - if (mSatisfier == null) { - return null; - } - - for (NetworkRequest req : mRequests) { - if (mSatisfier.isSatisfyingRequest(req.requestId)) { - return req; - } - } - - return null; - } - - private void enforceRequestCountLimit() { - synchronized (mUidToNetworkRequestCount) { - int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1; - if (networkRequests >= MAX_NETWORK_REQUESTS_PER_UID) { - throw new ServiceSpecificException( - ConnectivityManager.Errors.TOO_MANY_REQUESTS); - } - mUidToNetworkRequestCount.put(mUid, networkRequests); - } - } - void unlinkDeathRecipient() { if (mBinder != null) { mBinder.unlinkToDeath(this, 0); @@ -5550,6 +5617,10 @@ public class ConnectivityService extends IConnectivityManager.Stub private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) { final SortedSet<Integer> thresholds = new TreeSet<>(); synchronized (nai) { + // mNetworkRequests may contain the same value multiple times in case of + // multilayer requests. It won't matter in this case because the thresholds + // will then be the same and be deduplicated as they enter the `thresholds` set. + // TODO : have mNetworkRequests be a Set<NetworkRequestInfo> or the like. for (final NetworkRequestInfo nri : mNetworkRequests.values()) { for (final NetworkRequest req : nri.mRequests) { if (req.networkCapabilities.hasSignalStrength() @@ -5589,7 +5660,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (ns == null) { return; } - MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns); + if (ns instanceof MatchAllNetworkSpecifier) { + throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted"); + } } private void ensureValid(NetworkCapabilities nc) { @@ -5613,31 +5686,43 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, int timeoutMs, IBinder binder, int legacyType, - @NonNull String callingPackageName, @Nullable String callingAttributionTag) { + int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder, + int legacyType, @NonNull String callingPackageName, + @Nullable String callingAttributionTag) { if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) { if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) { throw new SecurityException("Insufficient permissions to specify legacy type"); } } final int callingUid = mDeps.getCallingUid(); - final NetworkRequest.Type type = (networkCapabilities == null) - ? NetworkRequest.Type.TRACK_DEFAULT - : NetworkRequest.Type.REQUEST; - // If the requested networkCapabilities is null, take them instead from - // the default network request. This allows callers to keep track of - // the system default network. - if (type == NetworkRequest.Type.TRACK_DEFAULT) { - networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid); - enforceAccessPermission(); - } else { - networkCapabilities = new NetworkCapabilities(networkCapabilities); - enforceNetworkRequestPermissions(networkCapabilities, callingPackageName, - callingAttributionTag); - // TODO: this is incorrect. We mark the request as metered or not depending on the state - // of the app when the request is filed, but we never change the request if the app - // changes network state. http://b/29964605 - enforceMeteredApnPolicy(networkCapabilities); + final NetworkRequest.Type reqType; + try { + reqType = NetworkRequest.Type.values()[reqTypeInt]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException("Unsupported request type " + reqTypeInt); + } + switch (reqType) { + case TRACK_DEFAULT: + // If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities} + // is unused and will be replaced by the one from the default network request. + // This allows callers to keep track of the system default network. + networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid); + enforceAccessPermission(); + break; + case BACKGROUND_REQUEST: + enforceNetworkStackOrSettingsPermission(); + // Fall-through since other checks are the same with normal requests. + case REQUEST: + networkCapabilities = new NetworkCapabilities(networkCapabilities); + enforceNetworkRequestPermissions(networkCapabilities, callingPackageName, + callingAttributionTag); + // TODO: this is incorrect. We mark the request as metered or not depending on + // the state of the app when the request is filed, but we never change the + // request if the app changes network state. http://b/29964605 + enforceMeteredApnPolicy(networkCapabilities); + break; + default: + throw new IllegalArgumentException("Unsupported request type " + reqType); } ensureRequestableCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, @@ -5656,7 +5741,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureValid(networkCapabilities); NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, - nextNetworkRequestId(), type); + nextNetworkRequestId(), reqType); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder); if (DBG) log("requestNetwork for " + nri); @@ -5716,9 +5801,14 @@ public class ConnectivityService extends IConnectivityManager.Stub // Policy already enforced. return; } - if (mPolicyManagerInternal.isUidRestrictedOnMeteredNetworks(uid)) { - // If UID is restricted, don't allow them to bring up metered APNs. - networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); + final long ident = Binder.clearCallingIdentity(); + try { + if (mPolicyManager.isUidRestrictedOnMeteredNetworks(uid)) { + // If UID is restricted, don't allow them to bring up metered APNs. + networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); + } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -5911,13 +6001,19 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public void declareNetworkRequestUnfulfillable(NetworkRequest request) { + public void declareNetworkRequestUnfulfillable(@NonNull final NetworkRequest request) { if (request.hasTransport(TRANSPORT_TEST)) { enforceNetworkFactoryOrTestNetworksPermission(); } else { enforceNetworkFactoryPermission(); } - mHandler.post(() -> handleReleaseNetworkRequest(request, mDeps.getCallingUid(), true)); + final NetworkRequestInfo nri = mNetworkRequests.get(request); + if (nri != null) { + // declareNetworkRequestUnfulfillable() paths don't apply to multilayer requests. + ensureNotMultilayerRequest(nri, "declareNetworkRequestUnfulfillable"); + mHandler.post(() -> handleReleaseNetworkRequest( + nri.mRequests.get(0), mDeps.getCallingUid(), true)); + } } // NOTE: Accessed on multiple threads, must be synchronized on itself. @@ -5956,6 +6052,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // priority networks like ethernet are active. private final NetworkRequest mDefaultWifiRequest; + // Request used to optionally keep vehicle internal network always active + private final NetworkRequest mDefaultVehicleRequest; + private NetworkAgentInfo getDefaultNetwork() { return mDefaultNetworkNai; } @@ -6011,6 +6110,10 @@ public class ConnectivityService extends IConnectivityManager.Stub public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) { + Objects.requireNonNull(networkInfo, "networkInfo must not be null"); + Objects.requireNonNull(linkProperties, "linkProperties must not be null"); + Objects.requireNonNull(networkCapabilities, "networkCapabilities must not be null"); + Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null"); if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS); } else { @@ -6046,7 +6149,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkAgentInfo nai = new NetworkAgentInfo(na, new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), - this, mNetd, mDnsResolver, mNMS, providerId, uid); + this, mNetd, mDnsResolver, mNMS, providerId, uid, mQosCallbackTracker); // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says. processCapabilitiesFromAgent(nai, nc); @@ -6095,9 +6198,10 @@ public class ConnectivityService extends IConnectivityManager.Stub private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) { lp.ensureDirectlyConnectedRoutes(); nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix()); + nai.networkAgentPortalData = lp.getCaptivePortalData(); } - private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp, + private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp, @NonNull LinkProperties oldLp) { int netId = networkAgent.network.getNetId(); @@ -6106,8 +6210,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // the LinkProperties for the network are accurate. networkAgent.clatd.fixupLinkProperties(oldLp, newLp); - updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities, - networkAgent.networkInfo.getType()); + updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities); // update filtering rules, need to happen after the interface update so netd knows about the // new interface (the interface name -> index map becomes initialized) @@ -6138,9 +6241,11 @@ public class ConnectivityService extends IConnectivityManager.Stub updateWakeOnLan(newLp); - // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo, - // it is not contained in LinkProperties sent from NetworkAgents so needs to be merged here. - newLp.setCaptivePortalData(networkAgent.captivePortalData); + // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo. + // It is not always contained in the LinkProperties sent from NetworkAgents, and if it + // does, it needs to be merged here. + newLp.setCaptivePortalData(mergeCaptivePortalData(networkAgent.networkAgentPortalData, + networkAgent.capportApiData)); // TODO - move this check to cover the whole function if (!Objects.equals(newLp, oldLp)) { @@ -6160,6 +6265,57 @@ public class ConnectivityService extends IConnectivityManager.Stub mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent); } + /** + * @param naData captive portal data from NetworkAgent + * @param apiData captive portal data from capport API + */ + @Nullable + private CaptivePortalData mergeCaptivePortalData(CaptivePortalData naData, + CaptivePortalData apiData) { + if (naData == null || apiData == null) { + return naData == null ? apiData : naData; + } + final CaptivePortalData.Builder captivePortalBuilder = + new CaptivePortalData.Builder(naData); + + if (apiData.isCaptive()) { + captivePortalBuilder.setCaptive(true); + } + if (apiData.isSessionExtendable()) { + captivePortalBuilder.setSessionExtendable(true); + } + if (apiData.getExpiryTimeMillis() >= 0 || apiData.getByteLimit() >= 0) { + // Expiry time, bytes remaining, refresh time all need to come from the same source, + // otherwise data would be inconsistent. Prefer the capport API info if present, + // as it can generally be refreshed more often. + captivePortalBuilder.setExpiryTime(apiData.getExpiryTimeMillis()); + captivePortalBuilder.setBytesRemaining(apiData.getByteLimit()); + captivePortalBuilder.setRefreshTime(apiData.getRefreshTimeMillis()); + } else if (naData.getExpiryTimeMillis() < 0 && naData.getByteLimit() < 0) { + // No source has time / bytes remaining information: surface the newest refresh time + // for other fields + captivePortalBuilder.setRefreshTime( + Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis())); + } + + // Prioritize the user portal URL from the network agent. + if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null + || TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) { + captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl()); + } + // Prioritize the venue information URL from the network agent. + if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null + || TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) { + captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl()); + + // Note that venue friendly name can only come from the network agent because it is not + // in use in RFC8908. However, if using the Capport venue URL, make sure that the + // friendly name is not set from the network agent. + captivePortalBuilder.setVenueFriendlyName(null); + } + return captivePortalBuilder.build(); + } + private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) { // Marks are only available on WiFi interfaces. Checking for // marks on unsupported interfaces is harmless. @@ -6193,7 +6349,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void updateInterfaces(final @Nullable LinkProperties newLp, final @Nullable LinkProperties oldLp, final int netId, - final @Nullable NetworkCapabilities caps, final int legacyType) { + final @NonNull NetworkCapabilities caps) { final CompareResult<String> interfaceDiff = new CompareResult<>( oldLp != null ? oldLp.getAllInterfaceNames() : null, newLp != null ? newLp.getAllInterfaceNames() : null); @@ -6204,7 +6360,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("Adding iface " + iface + " to network " + netId); mNetd.networkAddInterface(netId, iface); wakeupModifyInterface(iface, caps, true); - bs.noteNetworkInterfaceType(iface, legacyType); + bs.noteNetworkInterfaceForTransports(iface, caps.getTransportTypes()); } catch (Exception e) { loge("Exception adding interface: " + e); } @@ -6476,6 +6632,7 @@ public class ConnectivityService extends IConnectivityManager.Stub * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal, * and foreground status). */ + @NonNull private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) { // Once a NetworkAgent is connected, complain if some immutable capabilities are removed. // Don't complain for VPNs since they're not driven by requests and there is no risk of @@ -6495,7 +6652,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Don't modify caller's NetworkCapabilities. - NetworkCapabilities newNc = new NetworkCapabilities(nc); + final NetworkCapabilities newNc = new NetworkCapabilities(nc); if (nai.lastValidated) { newNc.addCapability(NET_CAPABILITY_VALIDATED); } else { @@ -6532,6 +6689,25 @@ public class ConnectivityService extends IConnectivityManager.Stub return newNc; } + private void updateNetworkInfoForRoamingAndSuspended(NetworkAgentInfo nai, + NetworkCapabilities prevNc, NetworkCapabilities newNc) { + final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); + final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); + if (prevSuspended != suspended) { + // TODO (b/73132094) : remove this call once the few users of onSuspended and + // onResumed have been removed. + notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED + : ConnectivityManager.CALLBACK_RESUMED); + } + if (prevSuspended != suspended || prevRoaming != roaming) { + // updateNetworkInfo will mix in the suspended info from the capabilities and + // take appropriate action for the network having possibly changed state. + updateNetworkInfo(nai, nai.networkInfo); + } + } + /** * Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically: * @@ -6563,46 +6739,29 @@ public class ConnectivityService extends IConnectivityManager.Stub // on this network. We might have been called by rematchNetworkAndRequests when a // network changed foreground state. processListenRequests(nai); - final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); - final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); - final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); - final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); - if (prevSuspended != suspended || prevRoaming != roaming) { - // TODO (b/73132094) : remove this call once the few users of onSuspended and - // onResumed have been removed. - notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED - : ConnectivityManager.CALLBACK_RESUMED); - // updateNetworkInfo will mix in the suspended info from the capabilities and - // take appropriate action for the network having possibly changed state. - updateNetworkInfo(nai, nai.networkInfo); - } } else { // If the requestable capabilities have changed or the score changed, we can't have been // called by rematchNetworkAndRequests, so it's safe to start a rematch. rematchAllNetworksAndRequests(); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED); } + updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc); - // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps - // never returns null), so mark the relevant members and functions in nai as @NonNull and - // remove this test - if (prevNc != null) { - final boolean oldMetered = prevNc.isMetered(); - final boolean newMetered = newNc.isMetered(); - final boolean meteredChanged = oldMetered != newMetered; + final boolean oldMetered = prevNc.isMetered(); + final boolean newMetered = newNc.isMetered(); + final boolean meteredChanged = oldMetered != newMetered; - if (meteredChanged) { - maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground, - mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges); - } + if (meteredChanged) { + maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground, + mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges); + } - final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) != - newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); + final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) + != newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING); - // Report changes that are interesting for network statistics tracking. - if (meteredChanged || roamingChanged) { - notifyIfacesChangedForNetworkStats(); - } + // Report changes that are interesting for network statistics tracking. + if (meteredChanged || roamingChanged) { + notifyIfacesChangedForNetworkStats(); } // This network might have been underlying another network. Propagate its capabilities. @@ -6666,6 +6825,39 @@ public class ConnectivityService extends IConnectivityManager.Stub return stableRanges; } + private void maybeCloseSockets(NetworkAgentInfo nai, UidRangeParcel[] ranges, + int[] exemptUids) { + if (nai.isVPN() && !nai.networkAgentConfig.allowBypass) { + try { + mNetd.socketDestroy(ranges, exemptUids); + } catch (Exception e) { + loge("Exception in socket destroy: ", e); + } + } + } + + private void updateUidRanges(boolean add, NetworkAgentInfo nai, Set<UidRange> uidRanges) { + int[] exemptUids = new int[2]; + // TODO: Excluding VPN_UID is necessary in order to not to kill the TCP connection used + // by PPTP. Fix this by making Vpn set the owner UID to VPN_UID instead of system when + // starting a legacy VPN, and remove VPN_UID here. (b/176542831) + exemptUids[0] = VPN_UID; + exemptUids[1] = nai.networkCapabilities.getOwnerUid(); + UidRangeParcel[] ranges = toUidRangeStableParcels(uidRanges); + + maybeCloseSockets(nai, ranges, exemptUids); + try { + if (add) { + mNetd.networkAddUidRanges(nai.network.netId, ranges); + } else { + mNetd.networkRemoveUidRanges(nai.network.netId, ranges); + } + } catch (Exception e) { + loge("Exception while " + (add ? "adding" : "removing") + " uid ranges " + uidRanges + + " on netId " + nai.network.netId + ". " + e); + } + maybeCloseSockets(nai, ranges, exemptUids); + } private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc, NetworkCapabilities newNc) { @@ -6685,12 +6877,21 @@ public class ConnectivityService extends IConnectivityManager.Stub // in both ranges are not subject to any VPN routing rules. Adding new range before // removing old range works because, unlike the filtering rules below, it's possible to // add duplicate UID routing rules. + // TODO: calculate the intersection of add & remove. Imagining that we are trying to + // remove uid 3 from a set containing 1-5. Intersection of the prev and new sets is: + // [1-5] & [1-2],[4-5] == [3] + // Then we can do: + // maybeCloseSockets([3]) + // mNetd.networkAddUidRanges([1-2],[4-5]) + // mNetd.networkRemoveUidRanges([1-5]) + // maybeCloseSockets([3]) + // This can prevent the sockets of uid 1-2, 4-5 from being closed. It also reduce the + // number of binder calls from 6 to 4. if (!newRanges.isEmpty()) { - mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges)); + updateUidRanges(true, nai, newRanges); } if (!prevRanges.isEmpty()) { - mNetd.networkRemoveUidRanges( - nai.network.netId, toUidRangeStableParcels(prevRanges)); + updateUidRanges(false, nai, prevRanges); } final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties); final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties); @@ -6744,6 +6945,39 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void sendUpdatedScoreToFactories( + @NonNull final NetworkReassignment.RequestReassignment event) { + // If a request of type REQUEST is now being satisfied by a new network. + if (null != event.mNewNetworkRequest && event.mNewNetworkRequest.isRequest()) { + sendUpdatedScoreToFactories(event.mNewNetworkRequest, event.mNewNetwork); + } + + // If a previously satisfied request of type REQUEST is no longer being satisfied. + if (null != event.mOldNetworkRequest && event.mOldNetworkRequest.isRequest() + && event.mOldNetworkRequest != event.mNewNetworkRequest) { + sendUpdatedScoreToFactories(event.mOldNetworkRequest, null); + } + + cancelMultilayerLowerPriorityNpiRequests(event.mNetworkRequestInfo); + } + + /** + * Cancel with all NPIs the given NRI's multilayer requests that are a lower priority than + * its currently satisfied active request. + * @param nri the NRI to cancel lower priority requests for. + */ + private void cancelMultilayerLowerPriorityNpiRequests( + @NonNull final NetworkRequestInfo nri) { + if (!nri.isMultilayerRequest() || null == nri.mActiveRequest) { + return; + } + + final int indexOfNewRequest = nri.mRequests.indexOf(nri.mActiveRequest); + for (int i = indexOfNewRequest + 1; i < nri.mRequests.size(); i++) { + cancelNpiRequest(nri.mRequests.get(i)); + } + } + private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest, @Nullable NetworkAgentInfo nai) { final int score; @@ -6764,21 +6998,35 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** Sends all current NetworkRequests to the specified factory. */ - private void sendAllRequestsToProvider(NetworkProviderInfo npi) { + private void sendAllRequestsToProvider(@NonNull final NetworkProviderInfo npi) { ensureRunningOnConnectivityServiceThread(); - for (NetworkRequestInfo nri : mNetworkRequests.values()) { - if (nri.request.isListen()) continue; - NetworkAgentInfo nai = nri.mSatisfier; - final int score; - final int serial; - if (nai != null) { - score = nai.getCurrentScore(); - serial = nai.factorySerialNumber; - } else { - score = 0; - serial = NetworkProvider.ID_NONE; + for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) { + for (final NetworkRequest req : nri.mRequests) { + if (req.isListen() && nri.getActiveRequest() == req) { + break; + } + if (req.isListen()) { + continue; + } + // Only set the nai for the request it is satisfying. + final NetworkAgentInfo nai = + nri.getActiveRequest() == req ? nri.getSatisfier() : null; + final int score; + final int serial; + if (null != nai) { + score = nai.getCurrentScore(); + serial = nai.factorySerialNumber; + } else { + score = 0; + serial = NetworkProvider.ID_NONE; + } + npi.requestNetwork(req, score, serial); + // For multilayer requests, don't send lower priority requests if a higher priority + // request is already satisfied. + if (null != nai) { + break; + } } - npi.requestNetwork(nri.request, score, serial); } } @@ -6787,7 +7035,12 @@ public class ConnectivityService extends IConnectivityManager.Stub if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) { Intent intent = new Intent(); intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request); + // If apps could file multi-layer requests with PendingIntents, they'd need to know + // which of the layer is satisfied alongside with some ID for the request. Hence, if + // such an API is ever implemented, there is no doubt the right request to send in + // EXTRA_NETWORK_REQUEST is mActiveRequest, and whatever ID would be added would need to + // be sent as a separate extra. + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.getActiveRequest()); nri.mPendingIntentSent = true; sendIntent(nri.mPendingIntent, intent); } @@ -6817,8 +7070,9 @@ public class ConnectivityService extends IConnectivityManager.Stub releasePendingNetworkRequestWithDelay(pendingIntent); } - private void callCallbackForRequest(NetworkRequestInfo nri, - NetworkAgentInfo networkAgent, int notificationType, int arg1) { + private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri, + @NonNull final NetworkAgentInfo networkAgent, final int notificationType, + final int arg1) { if (nri.messenger == null) { // Default request has no msgr. Also prevents callbacks from being invoked for // NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks @@ -6826,8 +7080,14 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } Bundle bundle = new Bundle(); + // In the case of multi-layer NRIs, the first request is not necessarily the one that + // is satisfied. This is vexing, but the ConnectivityManager code that receives this + // callback is only using the request as a token to identify the callback, so it doesn't + // matter too much at this point as long as the callback can be found. + // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects. // TODO: check if defensive copies of data is needed. - putParcelable(bundle, new NetworkRequest(nri.request)); + final NetworkRequest nrForCallback = new NetworkRequest(nri.mRequests.get(0)); + putParcelable(bundle, nrForCallback); Message msg = Message.obtain(); if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) { putParcelable(bundle, networkAgent.network); @@ -6839,8 +7099,8 @@ public class ConnectivityService extends IConnectivityManager.Stub networkAgent.networkCapabilities, nri.mPid, nri.mUid); putParcelable( bundle, - maybeSanitizeLocationInfoForCaller( - nc, nri.mUid, nri.request.getRequestorPackageName())); + createWithLocationInfoSanitizedIfNecessaryWhenParceled( + nc, nri.mUid, nrForCallback.getRequestorPackageName())); putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( networkAgent.linkProperties, nri.mPid, nri.mUid)); // For this notification, arg1 contains the blocked status. @@ -6858,8 +7118,8 @@ public class ConnectivityService extends IConnectivityManager.Stub networkAgent.networkCapabilities, nri.mPid, nri.mUid); putParcelable( bundle, - maybeSanitizeLocationInfoForCaller( - netCap, nri.mUid, nri.request.getRequestorPackageName())); + createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, nri.mUid, nrForCallback.getRequestorPackageName())); break; } case ConnectivityManager.CALLBACK_IP_CHANGED: { @@ -6878,12 +7138,12 @@ public class ConnectivityService extends IConnectivityManager.Stub try { if (VDBG) { String notification = ConnectivityManager.getCallbackName(notificationType); - log("sending notification " + notification + " for " + nri.request); + log("sending notification " + notification + " for " + nrForCallback); } nri.messenger.send(msg); } catch (RemoteException e) { // may occur naturally in the race of binder death. - loge("RemoteException caught trying to send a callback msg for " + nri.request); + loge("RemoteException caught trying to send a callback msg for " + nrForCallback); } } @@ -6959,19 +7219,25 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) { - for (NetworkRequestInfo nri : mNetworkRequests.values()) { - NetworkRequest nr = nri.request; + for (final NetworkRequestInfo nri : mNetworkRequests.values()) { + if (nri.isMultilayerRequest()) { + continue; + } + final NetworkRequest nr = nri.mRequests.get(0); if (!nr.isListen()) continue; if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) { - nai.removeRequest(nri.request.requestId); + nai.removeRequest(nr.requestId); callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0); } } } private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) { - for (NetworkRequestInfo nri : mNetworkRequests.values()) { - NetworkRequest nr = nri.request; + for (final NetworkRequestInfo nri : mNetworkRequests.values()) { + if (nri.isMultilayerRequest()) { + continue; + } + final NetworkRequest nr = nri.mRequests.get(0); if (!nr.isListen()) continue; if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) { nai.addRequest(nr); @@ -6983,19 +7249,25 @@ public class ConnectivityService extends IConnectivityManager.Stub // An accumulator class to gather the list of changes that result from a rematch. private static class NetworkReassignment { static class RequestReassignment { - @NonNull public final NetworkRequestInfo mRequest; + @NonNull public final NetworkRequestInfo mNetworkRequestInfo; + @NonNull public final NetworkRequest mOldNetworkRequest; + @NonNull public final NetworkRequest mNewNetworkRequest; @Nullable public final NetworkAgentInfo mOldNetwork; @Nullable public final NetworkAgentInfo mNewNetwork; - RequestReassignment(@NonNull final NetworkRequestInfo request, + RequestReassignment(@NonNull final NetworkRequestInfo networkRequestInfo, + @NonNull final NetworkRequest oldNetworkRequest, + @NonNull final NetworkRequest newNetworkRequest, @Nullable final NetworkAgentInfo oldNetwork, @Nullable final NetworkAgentInfo newNetwork) { - mRequest = request; + mNetworkRequestInfo = networkRequestInfo; + mOldNetworkRequest = oldNetworkRequest; + mNewNetworkRequest = newNetworkRequest; mOldNetwork = oldNetwork; mNewNetwork = newNetwork; } public String toString() { - return mRequest.mRequests.get(0).requestId + " : " + return mNetworkRequestInfo.mRequests.get(0).requestId + " : " + (null != mOldNetwork ? mOldNetwork.network.getNetId() : "null") + " → " + (null != mNewNetwork ? mNewNetwork.network.getNetId() : "null"); } @@ -7013,7 +7285,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // sure this stays true, but without imposing this expensive check on all // reassignments on all user devices. for (final RequestReassignment existing : mReassignments) { - if (existing.mRequest.equals(reassignment.mRequest)) { + if (existing.mNetworkRequestInfo.equals(reassignment.mNetworkRequestInfo)) { throw new IllegalStateException("Trying to reassign [" + reassignment + "] but already have [" + existing + "]"); @@ -7028,7 +7300,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Nullable private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) { for (final RequestReassignment event : getRequestReassignments()) { - if (nri == event.mRequest) return event; + if (nri == event.mNetworkRequestInfo) return event; } return null; } @@ -7055,6 +7327,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri, + @NonNull final NetworkRequest previousRequest, + @NonNull final NetworkRequest newRequest, @Nullable final NetworkAgentInfo previousSatisfier, @Nullable final NetworkAgentInfo newSatisfier, final long now) { @@ -7064,58 +7338,98 @@ public class ConnectivityService extends IConnectivityManager.Stub if (VDBG || DDBG) { log(" accepting network in place of " + previousSatisfier.toShortString()); } - previousSatisfier.removeRequest(nri.request.requestId); - previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs); + previousSatisfier.removeRequest(previousRequest.requestId); + previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs); } else { if (VDBG || DDBG) log(" accepting network in place of null"); } - newSatisfier.unlingerRequest(nri.request); - if (!newSatisfier.addRequest(nri.request)) { + newSatisfier.unlingerRequest(newRequest.requestId); + if (!newSatisfier.addRequest(newRequest)) { Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has " - + nri.request); + + newRequest); } } else { if (DBG) { log("Network " + previousSatisfier.toShortString() + " stopped satisfying" - + " request " + nri.request.requestId); + + " request " + previousRequest.requestId); } - previousSatisfier.removeRequest(nri.request.requestId); + previousSatisfier.removeRequest(previousRequest.requestId); } - nri.mSatisfier = newSatisfier; + nri.setSatisfier(newSatisfier, newRequest); } + /** + * This function is triggered when something can affect what network should satisfy what + * request, and it computes the network reassignment from the passed collection of requests to + * network match to the one that the system should now have. That data is encoded in an + * object that is a list of changes, each of them having an NRI, and old satisfier, and a new + * satisfier. + * + * After the reassignment is computed, it is applied to the state objects. + * + * @param networkRequests the nri objects to evaluate for possible network reassignment + * @return NetworkReassignment listing of proposed network assignment changes + */ @NonNull - private NetworkReassignment computeNetworkReassignment() { - ensureRunningOnConnectivityServiceThread(); + private NetworkReassignment computeNetworkReassignment( + @NonNull final Collection<NetworkRequestInfo> networkRequests) { final NetworkReassignment changes = new NetworkReassignment(); // Gather the list of all relevant agents and sort them by score. final ArrayList<NetworkAgentInfo> nais = new ArrayList<>(); for (final NetworkAgentInfo nai : mNetworkAgentInfos) { - if (!nai.everConnected) continue; + if (!nai.everConnected) { + continue; + } nais.add(nai); } - for (final NetworkRequestInfo nri : mNetworkRequests.values()) { - if (nri.request.isListen()) continue; - final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais); + for (final NetworkRequestInfo nri : networkRequests) { + // Non-multilayer listen requests can be ignored. + if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) { + continue; + } + NetworkAgentInfo bestNetwork = null; + NetworkRequest bestRequest = null; + for (final NetworkRequest req : nri.mRequests) { + bestNetwork = mNetworkRanker.getBestNetwork(req, nais); + // Stop evaluating as the highest possible priority request is satisfied. + if (null != bestNetwork) { + bestRequest = req; + break; + } + } if (bestNetwork != nri.mSatisfier) { // bestNetwork may be null if no network can satisfy this request. changes.addRequestReassignment(new NetworkReassignment.RequestReassignment( - nri, nri.mSatisfier, bestNetwork)); + nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork)); } } return changes; } + private Set<NetworkRequestInfo> getNrisFromGlobalRequests() { + return new HashSet<>(mNetworkRequests.values()); + } + /** - * Attempt to rematch all Networks with NetworkRequests. This may result in Networks + * Attempt to rematch all Networks with all NetworkRequests. This may result in Networks * being disconnected. */ private void rematchAllNetworksAndRequests() { + rematchNetworksAndRequests(getNrisFromGlobalRequests()); + } + + /** + * Attempt to rematch all Networks with given NetworkRequests. This may result in Networks + * being disconnected. + */ + private void rematchNetworksAndRequests( + @NonNull final Set<NetworkRequestInfo> networkRequests) { + ensureRunningOnConnectivityServiceThread(); // TODO: This may be slow, and should be optimized. final long now = SystemClock.elapsedRealtime(); - final NetworkReassignment changes = computeNetworkReassignment(); + final NetworkReassignment changes = computeNetworkReassignment(networkRequests); if (VDBG || DDBG) { log(changes.debugString()); } else if (DBG) { @@ -7140,8 +7454,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // the linger status. for (final NetworkReassignment.RequestReassignment event : changes.getRequestReassignments()) { - updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork, - event.mNewNetwork, now); + updateSatisfiersForRematchRequest(event.mNetworkRequestInfo, + event.mOldNetworkRequest, event.mNewNetworkRequest, + event.mOldNetwork, event.mNewNetwork, + now); } final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork(); @@ -7155,12 +7471,32 @@ public class ConnectivityService extends IConnectivityManager.Stub if (oldDefaultNetwork != null) { mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork); } - updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork); + mNetworkActivityTracker.updateDataActivityTracking( + newDefaultNetwork, oldDefaultNetwork); // Notify system services of the new default. makeDefault(newDefaultNetwork); + // Log 0 -> X and Y -> X default network transitions, where X is the new default. - mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent( - now, newDefaultNetwork, oldDefaultNetwork); + final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null; + final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0; + final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated; + final LinkProperties lp = (newDefaultNetwork != null) + ? newDefaultNetwork.linkProperties : null; + final NetworkCapabilities nc = (newDefaultNetwork != null) + ? newDefaultNetwork.networkCapabilities : null; + + final Network prevNetwork = (oldDefaultNetwork != null) + ? oldDefaultNetwork.network : null; + final int prevScore = (oldDefaultNetwork != null) + ? oldDefaultNetwork.getCurrentScore() : 0; + final LinkProperties prevLp = (oldDefaultNetwork != null) + ? oldDefaultNetwork.linkProperties : null; + final NetworkCapabilities prevNc = (oldDefaultNetwork != null) + ? oldDefaultNetwork.networkCapabilities : null; + + mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc, + prevNetwork, prevScore, prevLp, prevNc); + // Have a new default network, release the transition wakelock in scheduleReleaseNetworkTransitionWakelock(); } @@ -7173,12 +7509,12 @@ public class ConnectivityService extends IConnectivityManager.Stub // trying to connect if they know they cannot match it. // TODO - this could get expensive if there are a lot of outstanding requests for this // network. Think of a way to reduce this. Push netid->request mapping to each factory? - sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork); + sendUpdatedScoreToFactories(event); if (null != event.mNewNetwork) { - notifyNetworkAvailable(event.mNewNetwork, event.mRequest); + notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo); } else { - callCallbackForRequest(event.mRequest, event.mOldNetwork, + callCallbackForRequest(event.mNetworkRequestInfo, event.mOldNetwork, ConnectivityManager.CALLBACK_LOST, 0); } } @@ -7416,10 +7752,6 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) { networkAgent.everConnected = true; - if (networkAgent.linkProperties == null) { - Log.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties"); - } - // NetworkCapabilities need to be set before sending the private DNS config to // NetworkMonitor, otherwise NetworkMonitor cannot determine if validation is required. networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities); @@ -7671,10 +8003,10 @@ public class ConnectivityService extends IConnectivityManager.Stub activeIface = activeLinkProperties.getInterfaceName(); } - final VpnInfo[] vpnInfos = getAllVpnInfo(); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = getAllVpnInfo(); try { - mStatsService.forceUpdateIfaces( - getDefaultNetworks(), getAllNetworkState(), activeIface, vpnInfos); + mStatsService.forceUpdateIfaces(getDefaultNetworks(), getAllNetworkState(), activeIface, + underlyingNetworkInfos); } catch (Exception ignored) { } } @@ -7738,10 +8070,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public void startNattKeepaliveWithFd(Network network, FileDescriptor fd, int resourceId, + public void startNattKeepaliveWithFd(Network network, ParcelFileDescriptor pfd, int resourceId, int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr, String dstAddr) { try { + final FileDescriptor fd = pfd.getFileDescriptor(); mKeepaliveTracker.startNattKeepalive( getNetworkAgentInfoForNetwork(network), fd, resourceId, intervalSeconds, cb, @@ -7749,24 +8082,25 @@ public class ConnectivityService extends IConnectivityManager.Stub } finally { // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks. // startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately. - if (fd != null && Binder.getCallingPid() != Process.myPid()) { - IoUtils.closeQuietly(fd); + if (pfd != null && Binder.getCallingPid() != Process.myPid()) { + IoUtils.closeQuietly(pfd); } } } @Override - public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds, + public void startTcpKeepalive(Network network, ParcelFileDescriptor pfd, int intervalSeconds, ISocketKeepaliveCallback cb) { try { enforceKeepalivePermission(); + final FileDescriptor fd = pfd.getFileDescriptor(); mKeepaliveTracker.startTcpKeepalive( getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb); } finally { // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks. // startTcpKeepalive calls Os.dup(fd) before returning, so we can close immediately. - if (fd != null && Binder.getCallingPid() != Process.myPid()) { - IoUtils.closeQuietly(fd); + if (pfd != null && Binder.getCallingPid() != Process.myPid()) { + IoUtils.closeQuietly(pfd); } } } @@ -7932,6 +8266,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return getVpnIfOwner(mDeps.getCallingUid()); } + // TODO: stop calling into Vpn.java and get this information from data in this class. @GuardedBy("mVpns") private Vpn getVpnIfOwner(int uid) { final int user = UserHandle.getUserId(uid); @@ -7940,7 +8275,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (vpn == null) { return null; } else { - final VpnInfo info = vpn.getVpnInfo(); + final UnderlyingNetworkInfo info = vpn.getUnderlyingNetworkInfo(); return (info == null || info.ownerUid != uid) ? null : vpn; } } @@ -8217,7 +8552,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Decrement the reference count for this NetworkRequestInfo. The reference count is // incremented when the NetworkRequestInfo is created as part of // enforceRequestCountLimit(). - decrementNetworkRequestPerUidCount(nri); + mNetworkRequestCounter.decrementCount(nri.mUid); return; } @@ -8283,7 +8618,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Decrement the reference count for this NetworkRequestInfo. The reference count is // incremented when the NetworkRequestInfo is created as part of // enforceRequestCountLimit(). - decrementNetworkRequestPerUidCount(nri); + mNetworkRequestCounter.decrementCount(nri.mUid); iCb.unlinkToDeath(cbInfo, 0); } @@ -8492,4 +8827,194 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyDataStallSuspected(p, network.getNetId()); } + + private final LegacyNetworkActivityTracker mNetworkActivityTracker; + + /** + * Class used for updating network activity tracking with netd and notify network activity + * changes. + */ + private static final class LegacyNetworkActivityTracker { + private final Context mContext; + private final INetworkManagementService mNMS; + + LegacyNetworkActivityTracker(@NonNull Context context, + @NonNull INetworkManagementService nms) { + mContext = context; + mNMS = nms; + try { + mNMS.registerObserver(mDataActivityObserver); + } catch (RemoteException e) { + loge("Error registering observer :" + e); + } + } + + // TODO: Migrate away the dependency with INetworkManagementEventObserver. + private final INetworkManagementEventObserver mDataActivityObserver = + new BaseNetworkObserver() { + @Override + public void interfaceClassDataActivityChanged(int transportType, boolean active, + long tsNanos, int uid) { + sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, + tsNanos); + } + }; + + // This is deprecated and only to support legacy use cases. + private int transportTypeToLegacyType(int type) { + switch (type) { + case NetworkCapabilities.TRANSPORT_CELLULAR: + return ConnectivityManager.TYPE_MOBILE; + case NetworkCapabilities.TRANSPORT_WIFI: + return ConnectivityManager.TYPE_WIFI; + case NetworkCapabilities.TRANSPORT_BLUETOOTH: + return ConnectivityManager.TYPE_BLUETOOTH; + case NetworkCapabilities.TRANSPORT_ETHERNET: + return ConnectivityManager.TYPE_ETHERNET; + default: + loge("Unexpected transport in transportTypeToLegacyType: " + type); + } + return ConnectivityManager.TYPE_NONE; + } + + public void sendDataActivityBroadcast(int deviceType, boolean active, long tsNanos) { + final Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE); + intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType); + intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active); + intent.putExtra(ConnectivityManager.EXTRA_REALTIME_NS, tsNanos); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, + RECEIVE_DATA_ACTIVITY_CHANGE, + null /* resultReceiver */, + null /* scheduler */, + 0 /* initialCode */, + null /* initialData */, + null /* initialExtra */); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** + * Setup data activity tracking for the given network. + * + * Every {@code setupDataActivityTracking} should be paired with a + * {@link #removeDataActivityTracking} for cleanup. + */ + private void setupDataActivityTracking(NetworkAgentInfo networkAgent) { + final String iface = networkAgent.linkProperties.getInterfaceName(); + + final int timeout; + final int type; + + if (networkAgent.networkCapabilities.hasTransport( + NetworkCapabilities.TRANSPORT_CELLULAR)) { + timeout = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE, + 10); + type = NetworkCapabilities.TRANSPORT_CELLULAR; + } else if (networkAgent.networkCapabilities.hasTransport( + NetworkCapabilities.TRANSPORT_WIFI)) { + timeout = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI, + 15); + type = NetworkCapabilities.TRANSPORT_WIFI; + } else { + return; // do not track any other networks + } + + if (timeout > 0 && iface != null) { + try { + // TODO: Access INetd directly instead of NMS + mNMS.addIdleTimer(iface, timeout, type); + } catch (Exception e) { + // You shall not crash! + loge("Exception in setupDataActivityTracking " + e); + } + } + } + + /** + * Remove data activity tracking when network disconnects. + */ + private void removeDataActivityTracking(NetworkAgentInfo networkAgent) { + final String iface = networkAgent.linkProperties.getInterfaceName(); + final NetworkCapabilities caps = networkAgent.networkCapabilities; + + if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + || caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) { + try { + // the call fails silently if no idle timer setup for this interface + // TODO: Access INetd directly instead of NMS + mNMS.removeIdleTimer(iface); + } catch (Exception e) { + // You shall not crash! + loge("Exception in removeDataActivityTracking " + e); + } + } + } + + /** + * Update data activity tracking when network state is updated. + */ + public void updateDataActivityTracking(NetworkAgentInfo newNetwork, + NetworkAgentInfo oldNetwork) { + if (newNetwork != null) { + setupDataActivityTracking(newNetwork); + } + if (oldNetwork != null) { + removeDataActivityTracking(oldNetwork); + } + } + } + /** + * Registers {@link QosSocketFilter} with {@link IQosCallback}. + * + * @param socketInfo the socket information + * @param callback the callback to register + */ + @Override + public void registerQosSocketCallback(@NonNull final QosSocketInfo socketInfo, + @NonNull final IQosCallback callback) { + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(socketInfo.getNetwork()); + if (nai == null || nai.networkCapabilities == null) { + try { + callback.onError(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED); + } catch (final RemoteException ex) { + loge("registerQosCallbackInternal: RemoteException", ex); + } + return; + } + registerQosCallbackInternal(new QosSocketFilter(socketInfo), callback, nai); + } + + /** + * Register a {@link IQosCallback} with base {@link QosFilter}. + * + * @param filter the filter to register + * @param callback the callback to register + * @param nai the agent information related to the filter's network + */ + @VisibleForTesting + public void registerQosCallbackInternal(@NonNull final QosFilter filter, + @NonNull final IQosCallback callback, @NonNull final NetworkAgentInfo nai) { + if (filter == null) throw new IllegalArgumentException("filter must be non-null"); + if (callback == null) throw new IllegalArgumentException("callback must be non-null"); + + if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) { + enforceConnectivityRestrictedNetworksPermission(); + } + mQosCallbackTracker.registerCallback(callback, filter, nai); + } + + /** + * Unregisters the given callback. + * + * @param callback the callback to unregister + */ + @Override + public void unregisterQosCallback(@NonNull final IQosCallback callback) { + mQosCallbackTracker.unregisterCallback(callback); + } } diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java index f701688b2b7e..0779f7117d82 100644 --- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java +++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java @@ -35,6 +35,8 @@ public final class ConnectivityServiceInitializer extends SystemService { public ConnectivityServiceInitializer(Context context) { super(context); + // Load JNI libraries used by ConnectivityService and its dependencies + System.loadLibrary("service-connectivity"); // TODO: Define formal APIs to get the needed services. mConnectivity = new ConnectivityService(context, getNetworkManagementService(), getNetworkStatsService()); diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index f2b63a642c29..88ce2208adcb 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -22,7 +22,6 @@ import android.gsi.AvbPublicKey; import android.gsi.GsiProgress; import android.gsi.IGsiService; import android.gsi.IGsiServiceCallback; -import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; @@ -30,7 +29,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.image.IDynamicSystemService; import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; +import android.os.storage.VolumeInfo; import android.util.Slog; import java.io.File; @@ -88,16 +87,17 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { String path = SystemProperties.get("os.aot.path"); if (path.isEmpty()) { final int userId = UserHandle.myUserId(); - final StorageVolume[] volumes = - StorageManager.getVolumeList(userId, StorageManager.FLAG_FOR_WRITE); - for (StorageVolume volume : volumes) { - if (volume.isEmulated()) continue; - if (!volume.isRemovable()) continue; - if (!Environment.MEDIA_MOUNTED.equals(volume.getState())) continue; - File sdCard = volume.getPathFile(); - if (sdCard.isDirectory()) { - path = new File(sdCard, dsuSlot).getPath(); - break; + final StorageManager sm = mContext.getSystemService(StorageManager.class); + for (VolumeInfo volume : sm.getVolumes()) { + if (volume.getType() != volume.TYPE_PUBLIC) { + continue; + } + if (!volume.isMountedWritable()) { + continue; + } + File sd_internal = volume.getInternalPathForUser(userId); + if (sd_internal != null) { + path = new File(sd_internal, dsuSlot).getPath(); } } if (path.isEmpty()) { diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 1ea4a89a761f..d30a6405e95d 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -405,6 +405,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { if (mLastPowerStateFromRadio != powerState) { mLastPowerStateFromRadio = powerState; try { + // TODO: The interface changes that comes from netd are handled by BSS itself. + // There are still events caused by setting or removing idle timer, so keep + // reporting from here until setting idler timer moved to CS. getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid); } catch (RemoteException e) { } @@ -415,6 +418,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { if (mLastPowerStateFromWifi != powerState) { mLastPowerStateFromWifi = powerState; try { + // TODO: The interface changes that comes from netd are handled by BSS itself. + // There are still events caused by setting or removing idle timer, so keep + // reporting from here until setting idler timer moved to CS. getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid); } catch (RemoteException e) { } diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index f6b72d6bfe2c..222c96f2496b 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -6,10 +6,10 @@ per-file VibratorService.java, DisplayThread.java = michaelwr@google.com per-file VibratorService.java, DisplayThread.java = ogunwale@google.com # Zram writeback -per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com +per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com # Userspace reboot -per-file UserspaceRebootLogger.java = ioffe@google.com, tomcherry@google.com +per-file UserspaceRebootLogger.java = ioffe@google.com, dvander@google.com # Sensor Privacy per-file SensorPrivacyService.java = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS @@ -31,6 +31,7 @@ per-file IpSecService.java = file:/services/core/java/com/android/server/net/OWN per-file MmsServiceBroker.java = file:/telephony/OWNERS per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS per-file PackageWatchdog.java = file:/services/core/java/com/android/server/rollback/OWNERS +per-file PinnerService.java = file:/apct-tests/perftests/OWNERS per-file TelephonyRegistry.java = file:/telephony/OWNERS per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index c8d457d370ff..4e2519b47a47 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1537,6 +1537,9 @@ class StorageManagerService extends IStorageManager.Stub mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); } else if (vol.type == VolumeInfo.TYPE_STUB) { + if (vol.disk.isStubVisible()) { + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + } vol.mountUserId = mCurrentUserId; mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); } else { @@ -1606,7 +1609,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private void onVolumeStateChangedAsync(VolumeInfo vol, int oldState, int newState) { synchronized (mLock) { // Remember that we saw this volume so we're ready to accept user @@ -3295,6 +3297,12 @@ class StorageManagerService extends IStorageManager.Stub enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); if (isFsEncrypted) { + // When a user has secure lock screen, require secret to actually unlock. + // This check is mostly in place for emulation mode. + if (StorageManager.isFileEncryptedEmulatedOnly() && + mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(secret)) { + throw new IllegalStateException("Secret required to unlock secure user " + userId); + } try { mVold.unlockUserKey(userId, serialNumber, encodeBytes(token), encodeBytes(secret)); @@ -3427,6 +3435,27 @@ class StorageManagerService extends IStorageManager.Stub } } + /* + * Disable storage's app data isolation for testing. + */ + @Override + public void disableAppDataIsolation(String pkgName, int pid, int userId) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) { + throw new SecurityException("no permission to enable app visibility"); + } + final String[] sharedPackages = + mPmInternal.getSharedUserPackagesForPackage(pkgName, userId); + final int uid = mPmInternal.getPackageUid(pkgName, 0, userId); + final String[] packages = + sharedPackages.length != 0 ? sharedPackages : new String[]{pkgName}; + try { + mVold.unmountAppStorageDirs(uid, pid, packages); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + /** Not thread safe */ class AppFuseMountScope extends AppFuseBridge.MountScope { private boolean mMounted = false; diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 5f6e8df30f8d..81d2b831dc3c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -52,7 +52,6 @@ import android.telephony.CallAttributes; import android.telephony.CallQuality; import android.telephony.CellIdentity; import android.telephony.CellInfo; -import android.telephony.CellLocation; import android.telephony.CellSignalStrength; import android.telephony.CellSignalStrengthCdma; import android.telephony.CellSignalStrengthGsm; @@ -64,6 +63,7 @@ import android.telephony.DisconnectCause; import android.telephony.LocationAccessPolicy; import android.telephony.PhoneCapability; import android.telephony.PhoneStateListener; +import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; import android.telephony.PreciseDisconnectCause; @@ -78,6 +78,7 @@ import android.telephony.data.ApnSetting; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.LocalLog; import android.util.Pair; @@ -99,10 +100,13 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; /** * Since phone process can be restarted, this class provides a centralized place @@ -142,14 +146,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int callerUid; int callerPid; - int events; + Set<Integer> eventList; int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - boolean matchPhoneStateListenerEvent(int events) { - return (callback != null) && ((events & this.events) != 0); + boolean matchPhoneStateListenerEvent(int event) { + return (callback != null) && (this.eventList.contains(event)); } boolean matchOnSubscriptionsChangedListener() { @@ -177,7 +181,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + onSubscriptionsChangedListenerCallback + " onOpportunisticSubscriptionsChangedListenererCallback=" + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId - + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + "}"; + + " phoneId=" + phoneId + " events=" + eventList + "}"; } } @@ -306,6 +310,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private final LocalLog mListenLog = new LocalLog(200); + private List<PhysicalChannelConfig> mPhysicalChannelConfigs; + + private boolean mIsDataEnabled = false; + + private int mDataEnabledReason; + /** * Per-phone map of precise data connection state. The key of the map is the pair of transport * type and APN setting. This is the cache to prevent redundant callbacks to the listeners. @@ -315,39 +325,62 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>> mPreciseDataConnectionStates; - // Starting in Q, almost all cellular location requires FINE location enforcement. - // Prior to Q, cellular was available with COARSE location enforcement. Bits in this - // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later. - static final int ENFORCE_LOCATION_PERMISSION_MASK = - PhoneStateListener.LISTEN_CELL_LOCATION - | PhoneStateListener.LISTEN_CELL_INFO - | PhoneStateListener.LISTEN_REGISTRATION_FAILURE - | PhoneStateListener.LISTEN_BARRING_INFO; - - static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = - PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR - | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR - | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST - | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED; - - static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK = - PhoneStateListener.LISTEN_PRECISE_CALL_STATE - | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE - | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES - | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED - | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES - | PhoneStateListener.LISTEN_REGISTRATION_FAILURE - | PhoneStateListener.LISTEN_BARRING_INFO; - - static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK = - PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL - | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS; - - static final int READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK = - PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT - | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED - | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED - | PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE; + private static final Set<Integer> REQUIRE_PRECISE_PHONE_STATE_PERMISSION; + static { + REQUIRE_PRECISE_PHONE_STATE_PERMISSION = new HashSet<Integer>(); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_DATA_CONNECTION_REAL_TIME_INFO_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_REGISTRATION_FAILURE); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED); + REQUIRE_PRECISE_PHONE_STATE_PERMISSION.add( + PhoneStateListener.EVENT_DATA_ENABLED_CHANGED); + } + + private boolean isLocationPermissionRequired(Set<Integer> events) { + return events.contains(PhoneStateListener.EVENT_CELL_LOCATION_CHANGED) + || events.contains(PhoneStateListener.EVENT_CELL_INFO_CHANGED) + || events.contains(PhoneStateListener.EVENT_REGISTRATION_FAILURE) + || events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED); + } + + private boolean isPhoneStatePermissionRequired(Set<Integer> events) { + return events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) + || events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) + || events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) + || events.contains(PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED); + } + + private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) { + for (Integer requireEvent : REQUIRE_PRECISE_PHONE_STATE_PERMISSION) { + if (events.contains(requireEvent)) { + return true; + } + } + return false; + } + + private boolean isActiveEmergencySessionPermissionRequired(Set<Integer> events) { + return events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL) + || events.contains(PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS); + } + + private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) { + return events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED) + || events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED) + || events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED); + } private static final int MSG_USER_SWITCHED = 1; private static final int MSG_UPDATE_DEFAULT_SUB = 2; @@ -495,6 +528,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { cutListToSize(mImsReasonInfo, mNumPhones); cutListToSize(mPreciseDataConnectionStates, mNumPhones); cutListToSize(mBarringInfo, mNumPhones); + cutListToSize(mPhysicalChannelConfigs, mNumPhones); return; } @@ -528,6 +562,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; + mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); } } @@ -548,8 +583,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public TelephonyRegistry(Context context, ConfigurationProvider configurationProvider) { - CellLocation location = CellLocation.getEmpty(); - mContext = context; mConfigurationProvider = configurationProvider; mBatteryStats = BatteryStatsService.getService(); @@ -588,6 +621,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones]; mBarringInfo = new ArrayList<>(); mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones]; + mPhysicalChannelConfigs = new ArrayList<>(); for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; @@ -617,6 +651,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; + mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -659,7 +694,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callingFeatureId = callingFeatureId; r.callerUid = Binder.getCallingUid(); r.callerPid = Binder.getCallingPid(); - r.events = 0; + r.eventList = new ArraySet<>(); if (DBG) { log("listen oscl: Register r=" + r); } @@ -713,7 +748,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callingFeatureId = callingFeatureId; r.callerUid = Binder.getCallingUid(); r.callerPid = Binder.getCallingPid(); - r.events = 0; + r.eventList = new ArraySet<>(); if (DBG) { log("listen ooscl: Register r=" + r); } @@ -786,323 +821,337 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - @Deprecated - @Override - public void listen(String callingPackage, IPhoneStateListener callback, int events, - boolean notifyNow) { - listenWithFeature(callingPackage, null, callback, events, notifyNow); - } - @Override - public void listenWithFeature(String callingPackage, String callingFeatureId, - IPhoneStateListener callback, int events, boolean notifyNow) { - listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callingPackage, - callingFeatureId, callback, events, notifyNow); - } - - @Override - public void listenForSubscriber(int subId, String callingPackage, String callingFeatureId, - IPhoneStateListener callback, int events, boolean notifyNow) { - listen(callingPackage, callingFeatureId, callback, events, notifyNow, subId); + public void listenWithEventList(int subId, String callingPackage, String callingFeatureId, + IPhoneStateListener callback, int[] events, boolean notifyNow) { + Set<Integer> eventList = Arrays.stream(events).boxed().collect(Collectors.toSet()); + listen(callingPackage, callingFeatureId, callback, eventList, notifyNow, subId); } private void listen(String callingPackage, @Nullable String callingFeatureId, - IPhoneStateListener callback, int events, boolean notifyNow, int subId) { + IPhoneStateListener callback, Set<Integer> events, boolean notifyNow, int subId) { int callerUserId = UserHandle.getCallingUserId(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid() - + " events=0x" + Integer.toHexString(events) + " notifyNow=" + notifyNow + " subId=" - + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId; + + " events=" + events + " notifyNow=" + notifyNow + + " subId=" + subId + " myUserId=" + UserHandle.myUserId() + + " callerUserId=" + callerUserId; mListenLog.log(str); if (VDBG) { log(str); } - if (events != PhoneStateListener.LISTEN_NONE) { - // Checks permission and throws SecurityException for disallowed operations. For pre-M - // apps whose runtime permission has been revoked, we return immediately to skip sending - // events to the app without crashing it. - if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, - "listen")) { - return; + if (events.isEmpty()) { + if (DBG) { + log("listen: Unregister"); } + events.clear(); + remove(callback.asBinder()); + return; + } - int phoneId = getPhoneIdFromSubId(subId); - synchronized (mRecords) { - // register - IBinder b = callback.asBinder(); - boolean doesLimitApply = - Binder.getCallingUid() != Process.SYSTEM_UID - && Binder.getCallingUid() != Process.PHONE_UID - && Binder.getCallingUid() != Process.myUid(); - Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); + // Checks permission and throws SecurityException for disallowed operations. For pre-M + // apps whose runtime permission has been revoked, we return immediately to skip sending + // events to the app without crashing it. + if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, + "listen")) { + return; + } - if (r == null) { - return; - } + int phoneId = getPhoneIdFromSubId(subId); + synchronized (mRecords) { + // register + IBinder b = callback.asBinder(); + boolean doesLimitApply = + Binder.getCallingUid() != Process.SYSTEM_UID + && Binder.getCallingUid() != Process.PHONE_UID + && Binder.getCallingUid() != Process.myUid(); + Record r = add(b, Binder.getCallingUid(), Binder.getCallingPid(), doesLimitApply); - r.context = mContext; - r.callback = callback; - r.callingPackage = callingPackage; - r.callingFeatureId = callingFeatureId; - r.callerUid = Binder.getCallingUid(); - r.callerPid = Binder.getCallingPid(); - // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID, - // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID - if (!SubscriptionManager.isValidSubscriptionId(subId)) { - r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - } else {//APP specify subID - r.subId = subId; - } - r.phoneId = phoneId; - r.events = events; - if (DBG) { - log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); - } - if (notifyNow && validatePhoneId(phoneId)) { - if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { - try { - if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); - ServiceState rawSs = new ServiceState(mServiceState[phoneId]); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onServiceStateChanged(rawSs); - } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onServiceStateChanged( - rawSs.createLocationInfoSanitizedCopy(false)); - } else { - r.callback.onServiceStateChanged( - rawSs.createLocationInfoSanitizedCopy(true)); - } - } catch (RemoteException ex) { - remove(r.binder); + if (r == null) { + return; + } + + r.context = mContext; + r.callback = callback; + r.callingPackage = callingPackage; + r.callingFeatureId = callingFeatureId; + r.callerUid = Binder.getCallingUid(); + r.callerPid = Binder.getCallingPid(); + // Legacy applications pass SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, + // force all illegal subId to SubscriptionManager.DEFAULT_SUBSCRIPTION_ID + if (!SubscriptionManager.isValidSubscriptionId(subId)) { + r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + } else {//APP specify subID + r.subId = subId; + } + r.phoneId = phoneId; + r.eventList = events; + if (DBG) { + log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); + } + if (notifyNow && validatePhoneId(phoneId)) { + if (events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED)) { + try { + if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); + ServiceState rawSs = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged( + rawSs.createLocationInfoSanitizedCopy(false)); + } else { + r.callback.onServiceStateChanged( + rawSs.createLocationInfoSanitizedCopy(true)); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { - try { - if (mSignalStrength[phoneId] != null) { - int gsmSignalStrength = mSignalStrength[phoneId] - .getGsmSignalStrength(); - r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 - : gsmSignalStrength)); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) { + try { + if (mSignalStrength[phoneId] != null) { + int gsmSignalStrength = mSignalStrength[phoneId] + .getGsmSignalStrength(); + r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 + : gsmSignalStrength)); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { - try { - r.callback.onMessageWaitingIndicatorChanged( - mMessageWaiting[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains( + PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { + try { + r.callback.onMessageWaitingIndicatorChanged( + mMessageWaiting[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { - try { - r.callback.onCallForwardingIndicatorChanged( - mCallForwarding[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains( + PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { + try { + r.callback.onCallForwardingIndicatorChanged( + mCallForwarding[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { - try { - if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); - if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) - && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - // null will be translated to empty CellLocation object in client. - r.callback.onCellLocationChanged(mCellIdentity[phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (validateEventAndUserLocked( + r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) { + try { + if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + // null will be translated to empty CellLocation object in client. + r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) { - try { - r.callback.onCallStateChanged(mCallState[phoneId], - getCallIncomingNumber(r, phoneId)); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_CALL_STATE_CHANGED)) { + try { + r.callback.onCallStateChanged(mCallState[phoneId], + getCallIncomingNumber(r, phoneId)); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { - try { - r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], + } + if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) { + try { + r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) { - try { - r.callback.onDataActivity(mDataActivity[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED)) { + try { + r.callback.onDataActivity(mDataActivity[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) { - try { - if (mSignalStrength[phoneId] != null) { - r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED)) { + try { + if (mSignalStrength[phoneId] != null) { + r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) - != 0) { - updateReportSignalStrengthDecision(r.subId); - try { - if (mSignalStrength[phoneId] != null) { - r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains( + PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + updateReportSignalStrengthDecision(r.subId); + try { + if (mSignalStrength[phoneId] != null) { + r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]); } + } catch (RemoteException ex) { + remove(r.binder); } - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { - try { - if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " - + mCellInfo.get(phoneId)); - if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) - && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { - r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (validateEventAndUserLocked( + r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) { + try { + if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + + mCellInfo.get(phoneId)); + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) { - try { - r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED)) { + try { + r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { - try { - r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId], - mCallPreciseDisconnectCause[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) { + try { + r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId], + mCallPreciseDisconnectCause[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { - try { - r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId)); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) { + try { + r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId)); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) { - try { - for (PreciseDataConnectionState pdcs - : mPreciseDataConnectionStates.get(phoneId).values()) { - r.callback.onPreciseDataConnectionStateChanged(pdcs); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains( + PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) { + try { + for (PreciseDataConnectionState pdcs + : mPreciseDataConnectionStates.get(phoneId).values()) { + r.callback.onPreciseDataConnectionStateChanged(pdcs); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) != 0) { - try { - r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED)) { + try { + r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) { - try { - r.callback.onVoiceActivationStateChanged( - mVoiceActivationState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) { + try { + r.callback.onVoiceActivationStateChanged( + mVoiceActivationState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) !=0) { - try { - r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED)) { + try { + r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { - try { - r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { + try { + r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { - try { - if (mTelephonyDisplayInfos[phoneId] != null) { - r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); + } + if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) { + try { + if (mTelephonyDisplayInfos[phoneId] != null) { + r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]); } + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { - try { - r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) { + try { + r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) { - try { - r.callback.onPhoneCapabilityChanged(mPhoneCapability); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) { + try { + r.callback.onPhoneCapabilityChanged(mPhoneCapability); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener - .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) { - try { - r.callback.onActiveDataSubIdChanged(mActiveDataSubId); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains( + PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { + try { + r.callback.onActiveDataSubIdChanged(mActiveDataSubId); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) { - try { - r.callback.onRadioPowerStateChanged(mRadioPowerState); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED)) { + try { + r.callback.onRadioPowerStateChanged(mRadioPowerState); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) != 0) { - try { - r.callback.onSrvccStateChanged(mSrvccState[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_SRVCC_STATE_CHANGED)) { + try { + r.callback.onSrvccStateChanged(mSrvccState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) { - try { - r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED)) { + try { + r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); } - if ((events & PhoneStateListener.LISTEN_BARRING_INFO) != 0) { - BarringInfo barringInfo = mBarringInfo.get(phoneId); - BarringInfo biNoLocation = barringInfo != null - ? barringInfo.createLocationInfoSanitizedCopy() : null; - if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); - try { - r.callback.onBarringInfoChanged( - checkFineLocationAccess(r, Build.VERSION_CODES.BASE) - ? barringInfo : biNoLocation); - } catch (RemoteException ex) { - remove(r.binder); - } + } + if (events.contains(PhoneStateListener.EVENT_BARRING_INFO_CHANGED)) { + BarringInfo barringInfo = mBarringInfo.get(phoneId); + BarringInfo biNoLocation = barringInfo != null + ? barringInfo.createLocationInfoSanitizedCopy() : null; + if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); + try { + r.callback.onBarringInfoChanged( + checkFineLocationAccess(r, Build.VERSION_CODES.BASE) + ? barringInfo : biNoLocation); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if (events.contains( + PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) { + try { + r.callback.onPhysicalChannelConfigChanged( + mPhysicalChannelConfigs); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if (events.contains( + PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { + try { + r.callback.onDataEnabledChanged(mIsDataEnabled, mDataEnabledReason); + } catch (RemoteException ex) { + remove(r.binder); } } } - } else { - if(DBG) log("listen: Unregister"); - remove(callback.asBinder()); } } @@ -1114,7 +1163,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // If any of the system clients wants to always listen to signal strength, // we need to set it on. if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) { + PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { telephonyManager.createForSubscriptionId(subscriptionId) .setAlwaysReportSignalStrength(true); return; @@ -1215,7 +1264,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // strength is removed from registry records, we need to check if // the signal strength decision needs to update on its slot. if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) { + PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { updateReportSignalStrengthDecision(r.subId); } return; @@ -1235,8 +1284,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) && - (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED) + && (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { // Ensure the listener has read call log permission; if they do not return // an empty phone number. @@ -1270,9 +1319,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallState[phoneId] = state; mCallIncomingNumber[phoneId] = incomingNumber; for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) && - (r.subId == subId) && - (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { + if (r.matchPhoneStateListenerEvent(PhoneStateListener.EVENT_CALL_STATE_CHANGED) + && (r.subId == subId) + && (r.subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) { try { String incomingNumberOrEmpty = getCallIncomingNumber(r, phoneId); r.callback.onCallStateChanged(state, incomingNumberOrEmpty); @@ -1312,8 +1361,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) && - idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_SERVICE_STATE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { ServiceState stateToSend; @@ -1374,7 +1424,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if ((activationType == SIM_ACTIVATION_TYPE_VOICE) && r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) + PhoneStateListener.EVENT_VOICE_ACTIVATION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r @@ -1385,7 +1435,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if ((activationType == SIM_ACTIVATION_TYPE_DATA) && r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) + PhoneStateListener.EVENT_DATA_ACTIVATION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { if (DBG) { log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r @@ -1424,9 +1474,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " ss=" + signalStrength); } - if ((r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) + if ((r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED) || r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) + PhoneStateListener. + EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { @@ -1439,8 +1491,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mRemoveList.add(r.binder); } } - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTH) && - idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { int gsmSignalStrength = signalStrength.getGsmSignalStrength(); int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength); @@ -1486,8 +1539,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_CARRIER_NETWORK_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCarrierNetworkChange(active); } catch (RemoteException ex) { @@ -1517,9 +1570,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mCellInfo.set(phoneId, cellInfo); for (Record r : mRecords) { - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && - idMatch(r.subId, subId, phoneId) && - (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + if (validateEventAndUserLocked( + r, PhoneStateListener.EVENT_CELL_INFO_CHANGED) + && idMatch(r.subId, subId, phoneId) + && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { @@ -1551,8 +1605,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mMessageWaiting[phoneId] = mwi; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onMessageWaitingIndicatorChanged(mwi); } catch (RemoteException ex) { @@ -1578,8 +1632,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mUserMobileDataState[phoneId] = state; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onUserMobileDataStateChanged(state); } catch (RemoteException ex) { @@ -1617,7 +1671,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) + PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED) && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) { try { r.callback.onDisplayInfoChanged(telephonyDisplayInfo); @@ -1649,8 +1703,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallForwarding[phoneId] = cfi; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallForwardingIndicatorChanged(cfi); } catch (RemoteException ex) { @@ -1677,8 +1731,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataActivity[phoneId] = state; for (Record r : mRecords) { // Notify by correct subId. - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY) && - idMatch(r.subId, subId, phoneId)) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_DATA_ACTIVITY_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { r.callback.onDataActivity(state); } catch (RemoteException ex) { @@ -1725,7 +1780,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mLocalLog.log(str); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) + PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG) { @@ -1747,12 +1802,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { preciseState.getApnSetting()); PreciseDataConnectionState oldState = mPreciseDataConnectionStates.get(phoneId) .remove(key); - log("Jack: oldState=" + oldState); - log("Jack: newState=" + preciseState); if (!Objects.equals(oldState, preciseState)) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) + PhoneStateListener.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onPreciseDataConnectionStateChanged(preciseState); @@ -1797,9 +1850,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId) && !Objects.equals(cellIdentity, mCellIdentity[phoneId])) { mCellIdentity[phoneId] = cellIdentity; for (Record r : mRecords) { - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && - idMatch(r.subId, subId, phoneId) && - (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + if (validateEventAndUserLocked( + r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED) + && idMatch(r.subId, subId, phoneId) + && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { @@ -1850,7 +1904,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE) + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_PRECISE_CALL_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]); @@ -1859,7 +1914,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } if (notifyCallAttributes && r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) + PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); @@ -1908,7 +1963,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mImsReasonInfo.set(phoneId, imsReasonInfo); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) + PhoneStateListener.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -1940,8 +1995,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSrvccState[phoneId] = state; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED) && - idMatch(r.subId, subId, phoneId)) { + PhoneStateListener.EVENT_SRVCC_STATE_CHANGED) + && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { log("notifySrvccStateChanged: mSrvccState=" + state + " r=" + r); @@ -1969,7 +2024,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId); } if ((r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) + PhoneStateListener.EVENT_OEM_HOOK_RAW)) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onOemHookRawEvent(rawData); @@ -1997,7 +2052,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE)) { + PhoneStateListener.EVENT_PHONE_CAPABILITY_CHANGED)) { try { r.callback.onPhoneCapabilityChanged(capability); } catch (RemoteException ex) { @@ -2022,7 +2077,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) { + PhoneStateListener.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)) { try { r.callback.onActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { @@ -2049,7 +2104,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) + PhoneStateListener.EVENT_RADIO_POWER_STATE_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onRadioPowerStateChanged(state); @@ -2078,7 +2133,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) + PhoneStateListener.EVENT_EMERGENCY_NUMBER_LIST_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); @@ -2110,7 +2165,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { // Send to all listeners regardless of subscription if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)) { + PhoneStateListener.EVENT_OUTGOING_EMERGENCY_CALL)) { try { r.callback.onOutgoingEmergencyCall(emergencyNumber, subId); } catch (RemoteException ex) { @@ -2134,7 +2189,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { // Send to all listeners regardless of subscription if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS)) { + PhoneStateListener.EVENT_OUTGOING_EMERGENCY_SMS)) { try { r.callback.onOutgoingEmergencySms(emergencyNumber, subId); } catch (RemoteException ex) { @@ -2164,7 +2219,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) + PhoneStateListener.EVENT_CALL_ATTRIBUTES_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onCallAttributesChanged(mCallAttributes[phoneId]); @@ -2195,7 +2250,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_REGISTRATION_FAILURE) + PhoneStateListener.EVENT_REGISTRATION_FAILURE) && idMatch(r.subId, subId, phoneId)) { try { r.callback.onRegistrationFailed( @@ -2238,7 +2293,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_BARRING_INFO) + PhoneStateListener.EVENT_BARRING_INFO_CHANGED) && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { @@ -2258,6 +2313,81 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Send a notification to registrants that the configs of physical channel has changed for + * a particular subscription. + * + * @param subId the subId + * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel. + */ + public void notifyPhysicalChannelConfigForSubscriber( + int subId, List<PhysicalChannelConfig> configs) { + if (!checkNotifyPermission("notifyPhysicalChannelConfig()")) { + return; + } + + if (VDBG) { + log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs); + } + + synchronized (mRecords) { + int phoneId = SubscriptionManager.getPhoneId(subId); + if (validatePhoneId(phoneId)) { + mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId)); + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED) + && idMatch(r.subId, subId, phoneId)) { + try { + if (DBG_LOC) { + log("notifyPhysicalChannelConfig: " + + "mPhysicalChannelConfigs=" + + configs + " r=" + r); + } + r.callback.onPhysicalChannelConfigChanged(configs); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + } + handleRemoveListLocked(); + } + } + + /** + * Notify that the data enabled has changed. + * + * @param enabled True if data is enabled, otherwise disabled. + * @param reason Reason for data enabled/disabled. See {@code DATA_*} in + * {@link TelephonyManager}. + */ + public void notifyDataEnabled(boolean enabled, + @TelephonyManager.DataEnabledReason int reason) { + if (!checkNotifyPermission("notifyDataEnabled()")) { + return; + } + + if (VDBG) { + log("notifyDataEnabled: enabled=" + enabled + " reason=" + reason); + } + + mIsDataEnabled = enabled; + mDataEnabledReason = reason; + synchronized (mRecords) { + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { + try { + r.callback.onDataEnabledChanged(enabled, reason); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @@ -2310,6 +2440,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mEmergencyNumberList=" + mEmergencyNumberList); pw.println("mDefaultPhoneId=" + mDefaultPhoneId); pw.println("mDefaultSubId=" + mDefaultSubId); + pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs); + pw.println("mIsDataEnabled=" + mIsDataEnabled); + pw.println("mDataEnabledReason=" + mDataEnabledReason); pw.decreaseIndent(); @@ -2538,22 +2671,29 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { == PackageManager.PERMISSION_GRANTED; } - private boolean checkListenerPermission(int events, int subId, String callingPackage, + private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage, @Nullable String callingFeatureId, String message) { LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder = new LocationAccessPolicy.LocationPermissionQuery.Builder() - .setCallingPackage(callingPackage) - .setMethod(message + " events: " + events) - .setCallingPid(Binder.getCallingPid()) - .setCallingUid(Binder.getCallingUid()); + .setCallingPackage(callingPackage) + .setMethod(message + " events: " + events) + .setCallingPid(Binder.getCallingPid()) + .setCallingUid(Binder.getCallingUid()); + + boolean shouldCheckLocationPermissions = false; - if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) { + if (isLocationPermissionRequired(events)) { // Everything that requires fine location started in Q. So far... locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); // If we're enforcing fine starting in Q, we also want to enforce coarse even for // older SDK versions. locationQueryBuilder.setMinSdkVersionForCoarse(0); + shouldCheckLocationPermissions = true; + } + + boolean isPermissionCheckSuccessful = true; + if (shouldCheckLocationPermissions) { LocationAccessPolicy.LocationPermissionResult result = LocationAccessPolicy.checkLocationPermission( mContext, locationQueryBuilder.build()); @@ -2562,18 +2702,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { throw new SecurityException("Unable to listen for events " + events + " due to " + "insufficient location permissions."); case DENIED_SOFT: - return false; + isPermissionCheckSuccessful = false; } } - if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) { + if (isPhoneStatePermissionRequired(events)) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mContext, subId, callingPackage, callingFeatureId, message)) { - return false; + isPermissionCheckSuccessful = false; } } - if ((events & ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) { + if (isPrecisePhoneStatePermissionRequired(events)) { // check if calling app has either permission READ_PRECISE_PHONE_STATE // or with carrier privileges try { @@ -2584,47 +2724,46 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) { + if (isActiveEmergencySessionPermissionRequired(events)) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null); } - if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { + if ((events.contains(PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null); } - if ((events & READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK) != 0) { + if (isPrivilegedPhoneStatePermissionRequired(events)) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } - - return true; + return isPermissionCheckSuccessful; } private void handleRemoveListLocked() { int size = mRemoveList.size(); if (VDBG) log("handleRemoveListLocked: mRemoveList.size()=" + size); if (size > 0) { - for (IBinder b: mRemoveList) { + for (IBinder b : mRemoveList) { remove(b); } mRemoveList.clear(); } } - private boolean validateEventsAndUserLocked(Record r, int events) { + private boolean validateEventAndUserLocked(Record r, int event) { int foregroundUser; long callingIdentity = Binder.clearCallingIdentity(); boolean valid = false; try { foregroundUser = ActivityManager.getCurrentUser(); valid = UserHandle.getUserId(r.callerUid) == foregroundUser - && r.matchPhoneStateListenerEvent(events); + && r.matchPhoneStateListenerEvent(event); if (DBG | DBG_LOC) { - log("validateEventsAndUserLocked: valid=" + valid + log("validateEventAndUserLocked: valid=" + valid + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser - + " r.events=" + r.events + " events=" + events); + + " r.eventList=" + r.eventList + " event=" + event); } } finally { Binder.restoreCallingIdentity(callingIdentity); @@ -2677,6 +2816,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Note -- this method should only be used at the site of a permission check if you need to + * explicitly allow apps below a certain SDK level access regardless of location permissions. + * If you don't need app compat logic, use {@link #checkFineLocationAccess(Record)}. + */ private boolean checkFineLocationAccess(Record r, int minSdk) { LocationAccessPolicy.LocationPermissionQuery query = new LocationAccessPolicy.LocationPermissionQuery.Builder() @@ -2695,6 +2839,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { }); } + /** + * Note -- this method should only be used at the site of a permission check if you need to + * explicitly allow apps below a certain SDK level access regardless of location permissions. + * If you don't need app compat logic, use {@link #checkCoarseLocationAccess(Record)}. + */ private boolean checkCoarseLocationAccess(Record r, int minSdk) { LocationAccessPolicy.LocationPermissionQuery query = new LocationAccessPolicy.LocationPermissionQuery.Builder() @@ -2714,9 +2863,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } private void checkPossibleMissNotify(Record r, int phoneId) { - int events = r.events; + Set<Integer> events = r.eventList; + + if (events == null || events.isEmpty()) { + log("checkPossibleMissNotify: events = null."); + return; + } - if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { + if ((events.contains(PhoneStateListener.EVENT_SERVICE_STATE_CHANGED))) { try { if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" + mServiceState[phoneId]); @@ -2735,8 +2889,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0 - || (events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { + if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTHS_CHANGED) + || events.contains( + PhoneStateListener.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { SignalStrength signalStrength = mSignalStrength[phoneId]; @@ -2751,7 +2906,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) { + if (events.contains(PhoneStateListener.EVENT_SIGNAL_STRENGTH_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { int gsmSignalStrength = mSignalStrength[phoneId] @@ -2768,7 +2923,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { + if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_INFO_CHANGED)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " @@ -2783,7 +2938,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) != 0) { + if (events.contains(PhoneStateListener.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onUserMobileDataStateChanged phoneId=" @@ -2795,7 +2950,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { + if (events.contains(PhoneStateListener.EVENT_DISPLAY_INFO_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onDisplayInfoChanged phoneId=" @@ -2809,7 +2964,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { + if (events.contains(PhoneStateListener.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onMessageWaitingIndicatorChanged phoneId=" @@ -2822,7 +2977,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) { + if (events.contains(PhoneStateListener.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) { try { if (VDBG) { log("checkPossibleMissNotify: onCallForwardingIndicatorChanged phoneId=" @@ -2835,7 +2990,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { + if (validateEventAndUserLocked(r, PhoneStateListener.EVENT_CELL_LOCATION_CHANGED)) { try { if (DBG_LOC) { log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = " @@ -2851,7 +3006,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if ((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) { + if (events.contains(PhoneStateListener.EVENT_DATA_CONNECTION_STATE_CHANGED)) { try { if (DBG) { log("checkPossibleMissNotify: onDataConnectionStateChanged(mDataConnectionState" diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index e8687e57a07b..e96fd390f15a 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -32,6 +32,7 @@ import android.net.NetworkAgent; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkProvider; +import android.net.NetworkStack; import android.net.RouteInfo; import android.net.StringNetworkSpecifier; import android.net.TestNetworkInterface; @@ -48,6 +49,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.net.module.util.NetdUtils; import java.io.UncheckedIOException; import java.net.Inet4Address; @@ -242,6 +244,7 @@ class TestNetworkService extends ITestNetworkManager.Stub { nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); nc.setNetworkSpecifier(new StringNetworkSpecifier(iface)); nc.setAdministratorUids(administratorUids); if (!isMetered) { @@ -316,10 +319,10 @@ class TestNetworkService extends ITestNetworkManager.Stub { } try { - // This requires NETWORK_STACK privileges. final long token = Binder.clearCallingIdentity(); try { - mNMS.setInterfaceUp(iface); + NetworkStack.checkNetworkStackPermission(mContext); + NetdUtils.setInterfaceUp(mNetd, iface); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index c191a78aad0e..8562b0d9cb82 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -25,21 +25,28 @@ import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.net.vcn.IVcnManagementService; +import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; +import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.Looper; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.os.Process; +import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.ArrayMap; +import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -154,6 +161,11 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper; + @GuardedBy("mLock") + @NonNull + private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners = + new ArrayMap<>(); + @VisibleForTesting(visibility = Visibility.PRIVATE) VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { mContext = requireNonNull(context, "Missing context"); @@ -495,4 +507,84 @@ public class VcnManagementService extends IVcnManagementService.Stub { return Collections.unmodifiableMap(mVcns); } } + + /** Binder death recipient used to remove a registered policy listener. */ + private class PolicyListenerBinderDeath implements Binder.DeathRecipient { + @NonNull private final IVcnUnderlyingNetworkPolicyListener mListener; + + PolicyListenerBinderDeath(@NonNull IVcnUnderlyingNetworkPolicyListener listener) { + mListener = listener; + } + + @Override + public void binderDied() { + Log.e(TAG, "app died without removing VcnUnderlyingNetworkPolicyListener"); + removeVcnUnderlyingNetworkPolicyListener(mListener); + } + } + + /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */ + @GuardedBy("mLock") + @Override + public void addVcnUnderlyingNetworkPolicyListener( + @NonNull IVcnUnderlyingNetworkPolicyListener listener) { + requireNonNull(listener, "listener was null"); + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_FACTORY, + "Must have permission NETWORK_FACTORY to register a policy listener"); + + PolicyListenerBinderDeath listenerBinderDeath = new PolicyListenerBinderDeath(listener); + + synchronized (mLock) { + mRegisteredPolicyListeners.put(listener.asBinder(), listenerBinderDeath); + + try { + listener.asBinder().linkToDeath(listenerBinderDeath, 0 /* flags */); + } catch (RemoteException e) { + // Remote binder already died - cleanup registered Listener + listenerBinderDeath.binderDied(); + } + } + } + + /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */ + @GuardedBy("mLock") + @Override + public void removeVcnUnderlyingNetworkPolicyListener( + @NonNull IVcnUnderlyingNetworkPolicyListener listener) { + requireNonNull(listener, "listener was null"); + + synchronized (mLock) { + PolicyListenerBinderDeath listenerBinderDeath = + mRegisteredPolicyListeners.remove(listener.asBinder()); + + if (listenerBinderDeath != null) { + listener.asBinder().unlinkToDeath(listenerBinderDeath, 0 /* flags */); + } + } + } + + /** + * Gets the UnderlyingNetworkPolicy as determined by the provided NetworkCapabilities and + * LinkProperties. + */ + @NonNull + @Override + public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties) { + requireNonNull(networkCapabilities, "networkCapabilities was null"); + requireNonNull(linkProperties, "linkProperties was null"); + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_FACTORY, + "Must have permission NETWORK_FACTORY or be the SystemServer to get underlying" + + " Network policies"); + + // TODO(b/175914059): implement policy generation once VcnManagementService is able to + // determine policies + + return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); + } } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 630548df4b0b..a8478476b7e6 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -31,6 +31,7 @@ import android.os.IPowerManager; import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceDebugInfo; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; @@ -109,7 +110,6 @@ public class Watchdog extends Thread { }; public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList( - "android.hardware.audio@2.0::IDevicesFactory", "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", @@ -135,6 +135,11 @@ public class Watchdog extends Thread { "android.system.suspend@1.0::ISystemSuspend" ); + public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] { + "android.hardware.light.ILights/", + "android.hardware.power.stats.IPowerStats/", + }; + private static Watchdog sWatchdog; /* This handler will be used to post message back onto the main thread */ @@ -515,12 +520,11 @@ public class Watchdog extends Thread { return builder.toString(); } - private static ArrayList<Integer> getInterestingHalPids() { + private static void addInterestingHidlPids(HashSet<Integer> pids) { try { IServiceManager serviceManager = IServiceManager.getService(); ArrayList<IServiceManager.InstanceDebugInfo> dump = serviceManager.debugDump(); - HashSet<Integer> pids = new HashSet<>(); for (IServiceManager.InstanceDebugInfo info : dump) { if (info.pid == IServiceManager.PidConstant.NO_PID) { continue; @@ -532,24 +536,37 @@ public class Watchdog extends Thread { pids.add(info.pid); } - return new ArrayList<Integer>(pids); } catch (RemoteException e) { - return new ArrayList<Integer>(); + Log.w(TAG, e); + } + } + + private static void addInterestingAidlPids(HashSet<Integer> pids) { + ServiceDebugInfo[] infos = ServiceManager.getServiceDebugInfo(); + if (infos == null) return; + + for (ServiceDebugInfo info : infos) { + for (String prefix : AIDL_INTERFACE_PREFIXES_OF_INTEREST) { + if (info.name.startsWith(prefix)) { + pids.add(info.debugPid); + } + } } } static ArrayList<Integer> getInterestingNativePids() { - ArrayList<Integer> pids = getInterestingHalPids(); + HashSet<Integer> pids = new HashSet<>(); + addInterestingAidlPids(pids); + addInterestingHidlPids(pids); int[] nativePids = Process.getPidsForCommands(NATIVE_STACKS_OF_INTEREST); if (nativePids != null) { - pids.ensureCapacity(pids.size() + nativePids.length); for (int i : nativePids) { pids.add(i); } } - return pids; + return new ArrayList<Integer>(pids); } @Override @@ -704,7 +721,7 @@ public class Watchdog extends Thread { WatchdogDiagnostics.diagnoseCheckers(blockedCheckers); Slog.w(TAG, "*** GOODBYE!"); if (!Build.IS_USER && isCrashLoopFound() - && !WatchdogProperties.is_fatal_ignore().orElse(false)) { + && !WatchdogProperties.should_ignore_fatal_count().orElse(false)) { breakCrashLoop(); } Process.killProcess(Process.myPid()); @@ -783,7 +800,7 @@ public class Watchdog extends Thread { private boolean isCrashLoopFound() { int fatalCount = WatchdogProperties.fatal_count().orElse(0); long fatalWindowMs = TimeUnit.SECONDS.toMillis( - WatchdogProperties.fatal_window_second().orElse(0)); + WatchdogProperties.fatal_window_seconds().orElse(0)); if (fatalCount == 0 || fatalWindowMs == 0) { if (fatalCount != fatalWindowMs) { Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together", diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 928ddab9dca7..686adbb7b793 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -190,6 +190,7 @@ import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; +import android.compat.Compatibility; import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; @@ -318,6 +319,7 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.ProcessMap; import com.android.internal.app.SystemUserHomeActivity; import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.content.PackageHelper; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -16944,6 +16946,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (disableHiddenApiChecks || disableTestApiChecks) { enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS, "disable hidden API checks"); + + enableTestApiAccess(ii.packageName); } // TODO(b/158750470): remove @@ -17083,6 +17087,25 @@ public class ActivityManagerService extends IActivityManager.Stub forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false, app.userId, "finished inst"); + + disableTestApiAccess(app.info.packageName); + } + + private void enableTestApiAccess(String packageName) { + if (mPlatformCompat != null) { + Compatibility.ChangeConfig config = new Compatibility.ChangeConfig( + Collections.singleton(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */), + Collections.emptySet()); + CompatibilityChangeConfig override = new CompatibilityChangeConfig(config); + mPlatformCompat.setOverridesForTest(override, packageName); + } + } + + private void disableTestApiAccess(String packageName) { + if (mPlatformCompat != null) { + mPlatformCompat.clearOverrideForTest(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */, + packageName); + } } public void finishInstrumentation(IApplicationThread target, diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 1ade8e7e9311..9986085224b1 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -21,11 +21,14 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.net.INetworkManagementEventObserver; +import android.net.NetworkCapabilities; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.INetworkManagementService; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.ParcelFormatException; @@ -33,6 +36,7 @@ import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; @@ -52,6 +56,7 @@ import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; @@ -62,6 +67,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ParseUtils; import com.android.server.LocalServices; +import com.android.server.net.BaseNetworkObserver; import java.io.File; import java.io.FileDescriptor; @@ -108,6 +114,39 @@ public final class BatteryStatsService extends IBatteryStats.Stub private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE); private static final int MAX_LOW_POWER_STATS_SIZE = 4096; + @GuardedBy("mStats") + private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; + @GuardedBy("mStats") + private int mLastPowerStateFromWifi = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; + private final INetworkManagementEventObserver mActivityChangeObserver = + new BaseNetworkObserver() { + @Override + public void interfaceClassDataActivityChanged(int transportType, boolean active, + long tsNanos, int uid) { + final int powerState = active + ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH + : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; + final long timestampNanos; + if (tsNanos <= 0) { + timestampNanos = SystemClock.elapsedRealtimeNanos(); + } else { + timestampNanos = tsNanos; + } + + switch (transportType) { + case NetworkCapabilities.TRANSPORT_CELLULAR: + noteMobileRadioPowerState(powerState, timestampNanos, uid); + break; + case NetworkCapabilities.TRANSPORT_WIFI: + noteWifiRadioPowerState(powerState, timestampNanos, uid); + break; + default: + Slog.d(TAG, "Received unexpected transport in " + + "interfaceClassDataActivityChanged unexpected type: " + + transportType); + } + } + }; /** * Replaces the information in the given rpmStats with up-to-date information. */ @@ -203,6 +242,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub } public void systemServicesReady() { + final INetworkManagementService nms = INetworkManagementService.Stub.asInterface( + ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); + try { + nms.registerObserver(mActivityChangeObserver); + } catch (RemoteException e) { + Slog.e(TAG, "Could not register INetworkManagement event observer " + e); + } mStats.systemServicesReady(mContext); } @@ -680,8 +726,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteMobileRadioPowerState(int powerState, long timestampNs, int uid) { enforceCallingPermission(); + final boolean update; synchronized (mStats) { + // Ignore if no power state change. + if (mLastPowerStateFromRadio == powerState) return; + + mLastPowerStateFromRadio = powerState; update = mStats.noteMobileRadioPowerStateLocked(powerState, timestampNs, uid); } @@ -863,6 +914,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub // There was a change in WiFi power state. // Collect data now for the past activity. synchronized (mStats) { + // Ignore if no power state change. + if (mLastPowerStateFromWifi == powerState) return; + + mLastPowerStateFromWifi = powerState; if (mStats.isOnBattery()) { final String type = (powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM) ? "active" @@ -1011,9 +1066,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override - public void noteNetworkInterfaceType(String iface, int networkType) { + public void noteNetworkInterfaceForTransports(final String iface, int[] transportTypes) { enforceCallingPermission(); - mStats.noteNetworkInterfaceType(iface, networkType); + mStats.noteNetworkInterfaceForTransports(iface, transportTypes); } @Override diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 88b0c3be5464..b6e632d42d8e 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -110,7 +110,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ProcessMap; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.os.RuntimeInit; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -349,12 +348,23 @@ public final class ProcessList { private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. /** - * Enable memory tag checks in non-system apps. This flag will only have an effect on - * hardware supporting the ARM Memory Tagging Extension (MTE). + * Enable asynchronous (ASYNC) memory tag checking in this process. This + * flag will only have an effect on hardware supporting the ARM Memory + * Tagging Extension (MTE). */ @ChangeId @Disabled - private static final long NATIVE_MEMORY_TAGGING = 135772972; // This is a bug id. + private static final long NATIVE_MEMTAG_ASYNC = 135772972; // This is a bug id. + + /** + * Enable synchronous (SYNC) memory tag checking in this process. This flag + * will only have an effect on hardware supporting the ARM Memory Tagging + * Extension (MTE). If both NATIVE_MEMTAG_ASYNC and this option is selected, + * this option takes preference and MTE is enabled in SYNC mode. + */ + @ChangeId + @Disabled + private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id. /** * Enable sampled memory bug detection in the app. @@ -1677,23 +1687,23 @@ public final class ProcessList { return gidArray; } - private boolean shouldEnableMemoryTagging(ProcessRecord app) { + // Returns the memory tagging level to be enabled. If memory tagging isn't + // requested, returns zero. + private int getMemtagLevel(ProcessRecord app) { // Ensure the hardware + kernel actually supports MTE. if (!Zygote.nativeSupportsMemoryTagging()) { - return false; + return 0; } - // Enable MTE for system apps if supported. - if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - return true; + if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_SYNC, app.info)) { + return Zygote.MEMORY_TAG_LEVEL_SYNC; } - // Enable MTE if the compat feature is enabled. - if (mPlatformCompat.isChangeEnabled(NATIVE_MEMORY_TAGGING, app.info)) { - return true; + if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_ASYNC, app.info)) { + return Zygote.MEMORY_TAG_LEVEL_ASYNC; } - return false; + return 0; } private boolean shouldEnableTaggedPointers(ProcessRecord app) { @@ -1717,8 +1727,9 @@ public final class ProcessList { private int decideTaggingLevel(ProcessRecord app) { // Check MTE support first, as it should take precedence over TBI. - if (shouldEnableMemoryTagging(app)) { - return Zygote.MEMORY_TAG_LEVEL_ASYNC; + int memtagLevel = getMemtagLevel(app); + if (memtagLevel != 0) { + return memtagLevel; } if (shouldEnableTaggedPointers(app)) { diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java b/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java new file mode 100644 index 000000000000..9d43a39072bf --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/AppHibernationConstants.java @@ -0,0 +1,28 @@ +/* + * 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.apphibernation; + +/** + * Flags and constants that modify app hibernation behavior. + */ +final class AppHibernationConstants { + + private AppHibernationConstants() {} + + // Device config feature flag for app hibernation + static final String KEY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled"; +} diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java new file mode 100644 index 000000000000..fded85cd9126 --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -0,0 +1,452 @@ +/* + * 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.apphibernation; + +import static android.content.Intent.ACTION_PACKAGE_ADDED; +import static android.content.Intent.ACTION_PACKAGE_REMOVED; +import static android.content.Intent.ACTION_USER_ADDED; +import static android.content.Intent.ACTION_USER_REMOVED; +import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS; +import static android.content.Intent.EXTRA_REPLACING; +import static android.content.pm.PackageManager.MATCH_ALL; +import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.IActivityManager; +import android.apphibernation.IAppHibernationService; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.UserInfo; +import android.os.Binder; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ServiceManager; +import android.os.ShellCallback; +import android.os.Trace; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.DeviceConfig; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.SystemService; + +import java.io.FileDescriptor; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * System service that manages app hibernation state, a state apps can enter that means they are + * not being actively used and can be optimized for storage. The actual policy for determining + * if an app should hibernate is managed by PermissionController code. + */ +public final class AppHibernationService extends SystemService { + private static final String TAG = "AppHibernationService"; + + /** + * Lock for accessing any in-memory hibernation state + */ + private final Object mLock = new Object(); + private final Context mContext; + private final IPackageManager mIPackageManager; + private final IActivityManager mIActivityManager; + private final UserManager mUserManager; + @GuardedBy("mLock") + private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>(); + @GuardedBy("mLock") + private final Set<String> mGloballyHibernatedPackages = new ArraySet<>(); + + /** + * Initializes the system service. + * <p> + * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + * </p> + * + * @param context The system server context. + */ + public AppHibernationService(@NonNull Context context) { + this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")), + ActivityManager.getService(), + context.getSystemService(UserManager.class)); + } + + @VisibleForTesting + AppHibernationService(@NonNull Context context, IPackageManager packageManager, + IActivityManager activityManager, UserManager userManager) { + super(context); + mContext = context; + mIPackageManager = packageManager; + mIActivityManager = activityManager; + mUserManager = userManager; + + final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_USER_ADDED); + intentFilter.addAction(ACTION_USER_REMOVED); + userAllContext.registerReceiver(mBroadcastReceiver, intentFilter); + + intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_PACKAGE_ADDED); + intentFilter.addAction(ACTION_PACKAGE_REMOVED); + intentFilter.addDataScheme("package"); + userAllContext.registerReceiver(mBroadcastReceiver, intentFilter); + } + + @Override + public void onStart() { + publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub); + } + + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_BOOT_COMPLETED) { + synchronized (mLock) { + final List<UserInfo> users = mUserManager.getUsers(); + // TODO: Pull from persistent disk storage. For now, just make from scratch. + for (UserInfo user : users) { + addUserPackageStatesL(user.id); + } + } + } + } + + /** + * Whether a package is hibernating for a given user. + * + * @param packageName the package to check + * @param userId the user to check + * @return true if package is hibernating for the user + */ + boolean isHibernatingForUser(String packageName, int userId) { + userId = handleIncomingUser(userId, "isHibernating"); + synchronized (mLock) { + final Map<String, UserPackageState> packageStates = mUserStates.get(userId); + if (packageStates == null) { + throw new IllegalArgumentException("No user associated with user id " + userId); + } + final UserPackageState pkgState = packageStates.get(packageName); + if (pkgState == null) { + throw new IllegalArgumentException( + String.format("Package %s is not installed for user %s", + packageName, userId)); + } + return pkgState.hibernated; + } + } + + /** + * Whether a package is hibernated globally. This only occurs when a package is hibernating for + * all users and allows us to make optimizations at the package or APK level. + * + * @param packageName package to check + */ + boolean isHibernatingGlobally(String packageName) { + synchronized (mLock) { + return mGloballyHibernatedPackages.contains(packageName); + } + } + + /** + * Set whether the package is hibernating for the given user. + * + * @param packageName package to modify state + * @param userId user + * @param isHibernating new hibernation state + */ + void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { + userId = handleIncomingUser(userId, "setHibernating"); + synchronized (mLock) { + if (!mUserStates.contains(userId)) { + throw new IllegalArgumentException("No user associated with user id " + userId); + } + Map<String, UserPackageState> packageStates = mUserStates.get(userId); + UserPackageState pkgState = packageStates.get(packageName); + if (pkgState == null) { + throw new IllegalArgumentException( + String.format("Package %s is not installed for user %s", + packageName, userId)); + } + + if (pkgState.hibernated == isHibernating) { + return; + } + + if (isHibernating) { + hibernatePackageForUserL(packageName, userId, pkgState); + } else { + unhibernatePackageForUserL(packageName, userId, pkgState); + } + } + } + + /** + * Set whether the package should be hibernated globally at a package level, allowing the + * the system to make optimizations at the package or APK level. + * + * @param packageName package to hibernate globally + * @param isHibernating new hibernation state + */ + void setHibernatingGlobally(String packageName, boolean isHibernating) { + if (isHibernating != mGloballyHibernatedPackages.contains(packageName)) { + synchronized (mLock) { + if (isHibernating) { + hibernatePackageGloballyL(packageName); + } else { + unhibernatePackageGloballyL(packageName); + } + } + } + } + + /** + * Put an app into hibernation for a given user, allowing user-level optimizations to occur. + * The caller should hold {@link #mLock} + * + * @param pkgState package hibernation state + */ + private void hibernatePackageForUserL(@NonNull String packageName, int userId, + @NonNull UserPackageState pkgState) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage"); + final long caller = Binder.clearCallingIdentity(); + try { + mIActivityManager.forceStopPackage(packageName, userId); + mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId, + null /* observer */); + pkgState.hibernated = true; + } catch (RemoteException e) { + throw new IllegalStateException( + "Failed to hibernate due to manager not being available", e); + } finally { + Binder.restoreCallingIdentity(caller); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } + + /** + * Remove a package from hibernation for a given user. The caller should hold {@link #mLock}. + * + * @param pkgState package hibernation state + */ + private void unhibernatePackageForUserL(@NonNull String packageName, int userId, + UserPackageState pkgState) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage"); + final long caller = Binder.clearCallingIdentity(); + try { + mIPackageManager.setPackageStoppedState(packageName, false, userId); + pkgState.hibernated = false; + } catch (RemoteException e) { + throw new IllegalStateException( + "Failed to unhibernate due to manager not being available", e); + } finally { + Binder.restoreCallingIdentity(caller); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } + + /** + * Put a package into global hibernation, optimizing its storage at a package / APK level. + * The caller should hold {@link #mLock}. + */ + private void hibernatePackageGloballyL(@NonNull String packageName) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally"); + // TODO(175830194): Delete vdex/odex when DexManager API is built out + mGloballyHibernatedPackages.add(packageName); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + /** + * Unhibernate a package from global hibernation. The caller should hold {@link #mLock}. + */ + private void unhibernatePackageGloballyL(@NonNull String packageName) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally"); + mGloballyHibernatedPackages.remove(packageName); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + /** + * Populates {@link #mUserStates} with the users installed packages. The caller should hold + * {@link #mLock}. + * + * @param userId user id to add installed packages for + */ + private void addUserPackageStatesL(int userId) { + Map<String, UserPackageState> packages = new ArrayMap<>(); + List<PackageInfo> packageList; + try { + packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList(); + } catch (RemoteException e) { + throw new IllegalStateException("Package manager not available.", e); + } + + for (int i = 0, size = packageList.size(); i < size; i++) { + packages.put(packageList.get(i).packageName, new UserPackageState()); + } + mUserStates.put(userId, packages); + } + + private void onUserAdded(int userId) { + synchronized (mLock) { + addUserPackageStatesL(userId); + } + } + + private void onUserRemoved(int userId) { + synchronized (mLock) { + mUserStates.remove(userId); + } + } + + private void onPackageAdded(@NonNull String packageName, int userId) { + synchronized (mLock) { + mUserStates.get(userId).put(packageName, new UserPackageState()); + } + } + + private void onPackageRemoved(@NonNull String packageName, int userId) { + synchronized (mLock) { + mUserStates.get(userId).remove(packageName); + } + } + + private void onPackageRemovedForAllUsers(@NonNull String packageName) { + synchronized (mLock) { + mGloballyHibernatedPackages.remove(packageName); + } + } + + /** + * Private helper method to get the real user id and enforce permission checks. + * + * @param userId user id to handle + * @param name name to use for exceptions + * @return real user id + */ + private int handleIncomingUser(int userId, @NonNull String name) { + int callingUid = Binder.getCallingUid(); + try { + return mIActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, + false /* allowAll */, true /* requireFull */, name, null); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this); + + static final class AppHibernationServiceStub extends IAppHibernationService.Stub { + final AppHibernationService mService; + + AppHibernationServiceStub(AppHibernationService service) { + mService = service; + } + + @Override + public boolean isHibernatingForUser(String packageName, int userId) { + return mService.isHibernatingForUser(packageName, userId); + } + + @Override + public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { + mService.setHibernatingForUser(packageName, userId, isHibernating); + } + + @Override + public void setHibernatingGlobally(String packageName, boolean isHibernating) { + mService.setHibernatingGlobally(packageName, isHibernating); + } + + @Override + public boolean isHibernatingGlobally(String packageName) { + return mService.isHibernatingGlobally(packageName); + } + + @Override + public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, + @Nullable FileDescriptor err, @NonNull String[] args, + @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) { + new AppHibernationShellCommand(mService).exec(this, in, out, err, args, callback, + resultReceiver); + } + } + + // Broadcast receiver for user and package add/removal events + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId == UserHandle.USER_NULL) { + return; + } + + final String action = intent.getAction(); + if (ACTION_USER_ADDED.equals(action)) { + onUserAdded(userId); + } + if (ACTION_USER_REMOVED.equals(action)) { + onUserRemoved(userId); + } + if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) { + final String packageName = intent.getData().getSchemeSpecificPart(); + if (intent.getBooleanExtra(EXTRA_REPLACING, false)) { + // Package removal/add is part of an update, so no need to modify package state. + return; + } + + if (ACTION_PACKAGE_ADDED.equals(action)) { + onPackageAdded(packageName, userId); + } else if (ACTION_PACKAGE_REMOVED.equals(action)) { + onPackageRemoved(packageName, userId); + if (intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false)) { + onPackageRemovedForAllUsers(packageName); + } + } + } + } + }; + + /** + * Whether app hibernation is enabled on this device. + * + * @return true if enabled, false otherwise + */ + public static boolean isAppHibernationEnabled() { + return DeviceConfig.getBoolean( + NAMESPACE_APP_HIBERNATION, + AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED, + false /* defaultValue */); + } + + /** + * Data class that contains hibernation state info of a package for a user. + */ + private static final class UserPackageState { + public boolean hibernated; + // TODO: Track whether hibernation is exempted by the user + } +} diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java new file mode 100644 index 000000000000..7d6eea25541a --- /dev/null +++ b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java @@ -0,0 +1,136 @@ +/* + * 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.apphibernation; + +import android.os.ShellCommand; +import android.os.UserHandle; + +import java.io.PrintWriter; + +/** + * Shell command implementation for {@link AppHibernationService}. + */ +final class AppHibernationShellCommand extends ShellCommand { + private static final String USER_OPT = "--user"; + private static final String GLOBAL_OPT = "--global"; + private static final int SUCCESS = 0; + private static final int ERROR = -1; + private final AppHibernationService mService; + + AppHibernationShellCommand(AppHibernationService service) { + mService = service; + } + + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + switch (cmd) { + case "set-state": + return runSetState(); + case "get-state": + return runGetState(); + default: + return handleDefaultCommands(cmd); + } + } + + private int runSetState() { + String opt; + boolean setsGlobal = false; + int userId = UserHandle.USER_CURRENT; + while ((opt = getNextOption()) != null) { + switch (opt) { + case USER_OPT: + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case GLOBAL_OPT: + setsGlobal = true; + break; + default: + getErrPrintWriter().println("Error: Unknown option: " + opt); + } + } + + String pkg = getNextArgRequired(); + if (pkg == null) { + getErrPrintWriter().println("Error: no package specified"); + return ERROR; + } + + String newStateRaw = getNextArgRequired(); + if (newStateRaw == null) { + getErrPrintWriter().println("Error: No state to set specified"); + return ERROR; + } + boolean newState = Boolean.parseBoolean(newStateRaw); + + if (setsGlobal) { + mService.setHibernatingGlobally(pkg, newState); + } else { + mService.setHibernatingForUser(pkg, userId, newState); + } + return SUCCESS; + } + + private int runGetState() { + String opt; + boolean requestsGlobal = false; + int userId = UserHandle.USER_CURRENT; + while ((opt = getNextOption()) != null) { + switch (opt) { + case USER_OPT: + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + case GLOBAL_OPT: + requestsGlobal = true; + break; + default: + getErrPrintWriter().println("Error: Unknown option: " + opt); + } + } + + String pkg = getNextArgRequired(); + if (pkg == null) { + getErrPrintWriter().println("Error: No package specified"); + return ERROR; + } + boolean isHibernating = requestsGlobal + ? mService.isHibernatingGlobally(pkg) : mService.isHibernatingForUser(pkg, userId); + final PrintWriter pw = getOutPrintWriter(); + pw.println(isHibernating); + return SUCCESS; + } + + @Override + public void onHelp() { + final PrintWriter pw = getOutPrintWriter(); + pw.println("App hibernation (app_hibernation) commands: "); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(""); + pw.println(" set-state [--user USER_ID] [--global] PACKAGE true|false"); + pw.println(" Sets the hibernation state of the package to value specified. Optionally"); + pw.println(" may specify a user id or set global hibernation state."); + pw.println(""); + pw.println(" get-state [--user USER_ID] [--global] PACKAGE"); + pw.println(" Gets the hibernation state of the package. Optionally may specify a user"); + pw.println(" id or request global hibernation state."); + pw.println(""); + } +} diff --git a/services/core/java/com/android/server/apphibernation/OWNERS b/services/core/java/com/android/server/apphibernation/OWNERS index 4804fa3eb915..c2e27e084c8c 100644 --- a/services/core/java/com/android/server/apphibernation/OWNERS +++ b/services/core/java/com/android/server/apphibernation/OWNERS @@ -1,3 +1 @@ -# TODO: Include /core/java/android/apphibernation/OWNERS. See b/177005153 -kevhan@google.com -rajekumar@google.com +include /core/java/android/apphibernation/OWNERS diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 024dca7e23c6..4c69704df0c9 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -32,6 +32,7 @@ import static com.android.server.audio.AudioEventLogger.Event.ALOGW; import android.Manifest; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -166,6 +167,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -173,6 +175,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; import java.util.stream.Collectors; /** @@ -563,6 +566,117 @@ public class AudioService extends IAudioService.Stub private boolean mDockAudioMediaEnabled = true; + /** + * RestorableParameters is a thread-safe class used to store a + * first-in first-out history of parameters for replay / restoration. + * + * The idealized implementation of restoration would have a list of setting methods and + * values to be called for restoration. Explicitly managing such setters and + * values would be tedious - a simpler method is to store the values and the + * method implicitly by lambda capture (the values must be immutable or synchronization + * needs to be taken). + * + * We provide queueRestoreWithRemovalIfTrue() to allow + * the caller to provide a BooleanSupplier lambda, which conveniently packages + * the setter and its parameters needed for restoration. If during restoration, + * the BooleanSupplier returns true, it is removed from the mMap. + * + * We provide a setParameters() method as an example helper method. + */ + private static class RestorableParameters { + /** + * Sets a parameter and queues for restoration if successful. + * + * @param id a string handle associated with this parameter. + * @param parameter the actual parameter string. + * @return the result of AudioSystem.setParameters + */ + public int setParameters(@NonNull String id, @NonNull String parameter) { + Objects.requireNonNull(id, "id must not be null"); + Objects.requireNonNull(parameter, "parameter must not be null"); + synchronized (mMap) { + final int status = AudioSystem.setParameters(parameter); + if (status == AudioSystem.AUDIO_STATUS_OK) { // Java uses recursive mutexes. + queueRestoreWithRemovalIfTrue(id, () -> { // remove me if set fails. + return AudioSystem.setParameters(parameter) != AudioSystem.AUDIO_STATUS_OK; + }); + } + // Implementation detail: We do not mMap.remove(id); on failure. + return status; + } + } + + /** + * Queues a restore method which is executed on restoreAll(). + * + * If the supplier null, the id is removed from the restore map. + * + * Note: When the BooleanSupplier restore method is executed + * during restoreAll, if it returns true, it is removed from the + * restore map. + * + * @param id a unique tag associated with the restore method. + * @param supplier is a BooleanSupplier lambda. + */ + public void queueRestoreWithRemovalIfTrue( + @NonNull String id, @Nullable BooleanSupplier supplier) { + Objects.requireNonNull(id, "id must not be null"); + synchronized (mMap) { + if (supplier != null) { + mMap.put(id, supplier); + } else { + mMap.remove(id); + } + } + } + + /** + * Restore all parameters + * + * During restoration after audioserver death, any BooleanSupplier that returns + * true will be removed from mMap. + */ + public void restoreAll() { + synchronized (mMap) { + // Note: removing from values() also removes from the backing map. + // TODO: Consider catching exceptions? + mMap.values().removeIf(v -> { + return v.getAsBoolean(); + }); + } + } + + /** + * mMap is a LinkedHashMap<Key, Value> of parameters restored by restore(). + * The Key is a unique id tag for identification. + * The Value is a lambda expression which returns true if the entry is to + * be removed. + * + * 1) For memory limitation purposes, mMap keeps the latest MAX_ENTRIES + * accessed in the map. + * 2) Parameters are restored in order of queuing, first in first out, + * from earliest to latest. + */ + @GuardedBy("mMap") + private Map</* @NonNull */ String, /* @NonNull */ BooleanSupplier> mMap = + new LinkedHashMap<>() { + // TODO: do we need this memory limitation? + private static final int MAX_ENTRIES = 1000; // limit our memory for now. + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + if (size() <= MAX_ENTRIES) return false; + Log.w(TAG, "Parameter map exceeds " + + MAX_ENTRIES + " removing " + eldest.getKey()); // don't silently remove. + return true; + } + }; + } + + // We currently have one instance for mRestorableParameters used for + // setAdditionalOutputDeviceDelay(). Other methods requiring restoration could share this + // or use their own instance. + private RestorableParameters mRestorableParameters = new RestorableParameters(); + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; // Used when safe volume warning message display is requested by setStreamVolume(). In this @@ -1095,6 +1209,9 @@ public class AudioService extends IAudioService.Stub RotationHelper.updateOrientation(); } + // Restore setParameters and other queued setters. + mRestorableParameters.restoreAll(); + synchronized (mSettingsLock) { final int forDock = mDockAudioMediaEnabled ? AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE; @@ -9303,6 +9420,95 @@ public class AudioService extends IAudioService.Stub } } + /** + * @hide + * Sets an additional audio output device delay in milliseconds. + * + * The additional output delay is a request to the output device to + * delay audio presentation (generally with respect to video presentation for better + * synchronization). + * It may not be supported by all output devices, + * and typically increases the audio latency by the amount of additional + * audio delay requested. + * + * If additional audio delay is supported by an audio output device, + * it is expected to be supported for all output streams (and configurations) + * opened on that device. + * + * @param deviceType + * @param address + * @param delayMillis delay in milliseconds desired. This should be in range of {@code 0} + * to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}. + * @return true if successful, false if the device does not support output device delay + * or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}. + */ + @Override + //@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public boolean setAdditionalOutputDeviceDelay( + @NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) { + Objects.requireNonNull(device, "device must not be null"); + enforceModifyAudioRoutingPermission(); + final String getterKey = "additional_output_device_delay=" + + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()) + + "," + device.getAddress(); // "getter" key as an id. + final String setterKey = getterKey + "," + delayMillis; // append the delay for setter + return mRestorableParameters.setParameters(getterKey, setterKey) + == AudioSystem.AUDIO_STATUS_OK; + } + + /** + * @hide + * Returns the current additional audio output device delay in milliseconds. + * + * @param deviceType + * @param address + * @return the additional output device delay. This is a non-negative number. + * {@code 0} is returned if unsupported. + */ + @Override + @IntRange(from = 0) + public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) { + Objects.requireNonNull(device, "device must not be null"); + final String key = "additional_output_device_delay"; + final String reply = AudioSystem.getParameters( + key + "=" + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()) + + "," + device.getAddress()); + long delayMillis; + try { + delayMillis = Long.parseLong(reply.substring(key.length() + 1)); + } catch (NullPointerException e) { + delayMillis = 0; + } + return delayMillis; + } + + /** + * @hide + * Returns the maximum additional audio output device delay in milliseconds. + * + * @param deviceType + * @param address + * @return the maximum output device delay in milliseconds that can be set. + * This is a non-negative number + * representing the additional audio delay supported for the device. + * {@code 0} is returned if unsupported. + */ + @Override + @IntRange(from = 0) + public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) { + Objects.requireNonNull(device, "device must not be null"); + final String key = "max_additional_output_device_delay"; + final String reply = AudioSystem.getParameters( + key + "=" + AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()) + + "," + device.getAddress()); + long delayMillis; + try { + delayMillis = Long.parseLong(reply.substring(key.length() + 1)); + } catch (NullPointerException e) { + delayMillis = 0; + } + return delayMillis; + } //====================== // misc diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index 18907a19f96d..e3757dfc6a59 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -23,8 +23,11 @@ import android.content.pm.ApplicationInfo; import com.android.internal.compat.CompatibilityChangeInfo; import com.android.server.compat.config.Change; +import com.android.server.compat.overrides.ChangeOverrides; +import com.android.server.compat.overrides.OverrideValue; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -64,7 +67,7 @@ public final class CompatChange extends CompatibilityChangeInfo { private Map<String, Boolean> mDeferredOverrides; public CompatChange(long changeId) { - this(changeId, null, -1, -1, false, false, null); + this(changeId, null, -1, -1, false, false, null, false); } /** @@ -77,9 +80,10 @@ public final class CompatChange extends CompatibilityChangeInfo { * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set. */ public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk, - int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description) { + int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description, + boolean overridable) { super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly, - description); + description, overridable); } /** @@ -88,7 +92,7 @@ public final class CompatChange extends CompatibilityChangeInfo { public CompatChange(Change change) { super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(), change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(), - change.getDescription()); + change.getDescription(), change.getOverridable()); } void registerListener(ChangeListener listener) { @@ -252,6 +256,71 @@ public final class CompatChange extends CompatibilityChangeInfo { return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName); } + /** + * Checks whether a change has any package overrides. + * @return true if the change has at least one deferred override + */ + boolean hasAnyPackageOverride() { + return mDeferredOverrides != null && !mDeferredOverrides.isEmpty(); + } + + /** + * Checks whether a change has any deferred overrides. + * @return true if the change has at least one deferred override + */ + boolean hasAnyDeferredOverride() { + return mPackageOverrides != null && !mPackageOverrides.isEmpty(); + } + + void loadOverrides(ChangeOverrides changeOverrides) { + if (mDeferredOverrides == null) { + mDeferredOverrides = new HashMap<>(); + } + mDeferredOverrides.clear(); + for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) { + mDeferredOverrides.put(override.getPackageName(), override.getEnabled()); + } + + if (mPackageOverrides == null) { + mPackageOverrides = new HashMap<>(); + } + mPackageOverrides.clear(); + for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) { + mPackageOverrides.put(override.getPackageName(), override.getEnabled()); + } + } + + ChangeOverrides saveOverrides() { + if (!hasAnyDeferredOverride() && !hasAnyPackageOverride()) { + return null; + } + ChangeOverrides changeOverrides = new ChangeOverrides(); + changeOverrides.setChangeId(getId()); + ChangeOverrides.Deferred deferredOverrides = new ChangeOverrides.Deferred(); + List<OverrideValue> deferredList = deferredOverrides.getOverrideValue(); + if (mDeferredOverrides != null) { + for (Map.Entry<String, Boolean> entry : mDeferredOverrides.entrySet()) { + OverrideValue override = new OverrideValue(); + override.setPackageName(entry.getKey()); + override.setEnabled(entry.getValue()); + deferredList.add(override); + } + } + changeOverrides.setDeferred(deferredOverrides); + ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated(); + List<OverrideValue> validatedList = validatedOverrides.getOverrideValue(); + if (mPackageOverrides != null) { + for (Map.Entry<String, Boolean> entry : mPackageOverrides.entrySet()) { + OverrideValue override = new OverrideValue(); + override.setPackageName(entry.getKey()); + override.setEnabled(entry.getValue()); + validatedList.add(override); + } + } + changeOverrides.setValidated(validatedOverrides); + return changeOverrides; + } + @Override public String toString() { StringBuilder sb = new StringBuilder("ChangeId(") @@ -274,6 +343,9 @@ public final class CompatChange extends CompatibilityChangeInfo { if (mDeferredOverrides != null && mDeferredOverrides.size() > 0) { sb.append("; deferredOverrides=").append(mDeferredOverrides); } + if (getOverridable()) { + sb.append("; overridable"); + } return sb.append(")").toString(); } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 9376e8dc16ea..6b77b9d4ce39 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -21,7 +21,6 @@ import android.compat.Compatibility.ChangeConfig; import android.content.Context; import android.content.pm.ApplicationInfo; import android.os.Environment; -import android.os.RemoteException; import android.text.TextUtils; import android.util.LongArray; import android.util.LongSparseArray; @@ -35,7 +34,10 @@ import com.android.internal.compat.CompatibilityChangeInfo; import com.android.internal.compat.IOverrideValidator; import com.android.internal.compat.OverrideAllowedState; import com.android.server.compat.config.Change; -import com.android.server.compat.config.XmlParser; +import com.android.server.compat.config.Config; +import com.android.server.compat.overrides.ChangeOverrides; +import com.android.server.compat.overrides.Overrides; +import com.android.server.compat.overrides.XmlWriter; import com.android.server.pm.ApexManager; import org.xmlpull.v1.XmlPullParserException; @@ -53,7 +55,7 @@ import java.util.Set; import javax.xml.datatype.DatatypeConfigurationException; /** - * This class maintains state relating to platform compatibility changes. + * CompatConfig maintains state related to the platform compatibility changes. * * <p>It stores the default configuration for each change, and any per-package overrides that have * been configured. @@ -61,22 +63,47 @@ import javax.xml.datatype.DatatypeConfigurationException; final class CompatConfig { private static final String TAG = "CompatConfig"; + private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat"; + private static final String OVERRIDES_FILE = "compat_framework_overrides.xml"; @GuardedBy("mChanges") private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); - private OverrideValidatorImpl mOverrideValidator; + private final OverrideValidatorImpl mOverrideValidator; + private File mOverridesFile; @VisibleForTesting CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) { mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this); } + static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) { + CompatConfig config = new CompatConfig(androidBuildClassifier, context); + config.initConfigFromLib(Environment.buildPath( + Environment.getRootDirectory(), "etc", "compatconfig")); + config.initConfigFromLib(Environment.buildPath( + Environment.getRootDirectory(), "system_ext", "etc", "compatconfig")); + + List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos(); + for (ApexManager.ActiveApexInfo apex : apexes) { + config.initConfigFromLib(Environment.buildPath( + apex.apexDirectory, "etc", "compatconfig")); + } + File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE); + config.initOverrides(overridesFile); + config.invalidateCache(); + return config; + } + /** - * Add a change. This is intended to be used by code that reads change config from the - * filesystem. This should be done at system startup time. + * Adds a change. + * + * <p>This is intended to be used by code that reads change config from the filesystem. This + * should be done at system startup time. + * + * <p>Any change with the same ID will be overwritten. * - * @param change The change to add. Any change with the same ID will be overwritten. + * @param change the change to add */ void addChange(CompatChange change) { synchronized (mChanges) { @@ -86,13 +113,15 @@ final class CompatConfig { } /** - * Retrieves the set of disabled changes for a given app. Any change ID not in the returned - * array is by default enabled for the app. + * Retrieves the set of disabled changes for a given app. * - * @param app The app in question - * @return A sorted long array of change IDs. We use a primitive array to minimize memory - * footprint: Every app process will store this array statically so we aim to reduce - * overhead as much as possible. + * <p>Any change ID not in the returned array is by default enabled for the app. + * + * <p>We use a primitive array to minimize memory footprint: every app process will store this + * array statically so we aim to reduce overhead as much as possible. + * + * @param app the app in question + * @return a sorted long array of change IDs */ long[] getDisabledChanges(ApplicationInfo app) { LongArray disabled = new LongArray(); @@ -110,10 +139,10 @@ final class CompatConfig { } /** - * Look up a change ID by name. + * Looks up a change ID by name. * - * @param name Name of the change to look up - * @return The change ID, or {@code -1} if no change with that name exists. + * @param name name of the change to look up + * @return the change ID, or {@code -1} if no change with that name exists */ long lookupChangeId(String name) { synchronized (mChanges) { @@ -127,10 +156,10 @@ final class CompatConfig { } /** - * Find if a given change is enabled for a given application. + * Checks if a given change is enabled for a given application. * - * @param changeId The ID of the change in question - * @param app App to check for + * @param changeId the ID of the change in question + * @param app app to check for * @return {@code true} if the change is enabled for this app. Also returns {@code true} if the * change ID is not known, as unknown changes are enabled by default. */ @@ -146,10 +175,10 @@ final class CompatConfig { } /** - * Find if a given change will be enabled for a given package name, prior to installation. + * Checks if a given change will be enabled for a given package name after the installation. * - * @param changeId The ID of the change in question - * @param packageName Package name to check for + * @param changeId the ID of the change in question + * @param packageName package name to check for * @return {@code true} if the change would be enabled for this package name. Also returns * {@code true} if the change ID is not known, as unknown changes are enabled by default. */ @@ -165,22 +194,33 @@ final class CompatConfig { } /** - * Overrides the enabled state for a given change and app. This method is intended to be used - * *only* for debugging purposes, ultimately invoked either by an adb command, or from some - * developer settings UI. + * Overrides the enabled state for a given change and app. * - * <p>Note, package overrides are not persistent and will be lost on system or runtime restart. + * <p>This method is intended to be used *only* for debugging purposes, ultimately invoked + * either by an adb command, or from some developer settings UI. * - * @param changeId The ID of the change to be overridden. Note, this call will succeed even - * if - * this change is not known; it will only have any effect if any code in the - * platform is gated on the ID given. - * @param packageName The app package name to override the change for. - * @param enabled If the change should be enabled or disabled. - * @return {@code true} if the change existed before adding the override. + * <p>Note: package overrides are not persistent and will be lost on system or runtime restart. + * + * @param changeId the ID of the change to be overridden. Note, this call will succeed even + * if this change is not known; it will only have any effect if any code in + * the platform is gated on the ID given. + * @param packageName the app package name to override the change for + * @param enabled if the change should be enabled or disabled + * @return {@code true} if the change existed before adding the override + * @throws IllegalStateException if overriding is not allowed */ - boolean addOverride(long changeId, String packageName, boolean enabled) - throws SecurityException { + boolean addOverride(long changeId, String packageName, boolean enabled) { + boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, enabled); + saveOverrides(); + invalidateCache(); + return alreadyKnown; + } + + /** + * Unsafe version of {@link #addOverride(long, String, boolean)}. + * It does not invalidate the cache nor save the overrides. + */ + private boolean addOverrideUnsafe(long changeId, String packageName, boolean enabled) { boolean alreadyKnown = true; OverrideAllowedState allowedState = mOverrideValidator.getOverrideAllowedState(changeId, packageName); @@ -201,18 +241,13 @@ final class CompatConfig { break; default: throw new IllegalStateException("Should only be able to override changes that " - + "are allowed or can be deferred."); + + "are allowed or can be deferred."); } - invalidateCache(); } return alreadyKnown; } - /** - * Check whether the change is known to the compat config. - * - * @return {@code true} if the change is known. - */ + /** Checks whether the change is known to the compat config. */ boolean isKnownChangeId(long changeId) { synchronized (mChanges) { CompatChange c = mChanges.get(changeId); @@ -221,16 +256,13 @@ final class CompatConfig { } /** - * Returns the maximum sdk version for which this change can be opted in (or -1 if it is not - * target sdk gated). + * Returns the maximum SDK version for which this change can be opted in (or -1 if it is not + * target SDK gated). */ int maxTargetSdkForChangeIdOptIn(long changeId) { synchronized (mChanges) { CompatChange c = mChanges.get(changeId); - if (c == null) { - return -1; - } - if (c.getEnableSinceTargetSdk() != -1) { + if (c != null && c.getEnableSinceTargetSdk() != -1) { return c.getEnableSinceTargetSdk() - 1; } return -1; @@ -243,10 +275,7 @@ final class CompatConfig { boolean isLoggingOnly(long changeId) { synchronized (mChanges) { CompatChange c = mChanges.get(changeId); - if (c == null) { - return false; - } - return c.getLoggingOnly(); + return c != null && c.getLoggingOnly(); } } @@ -256,24 +285,32 @@ final class CompatConfig { boolean isDisabled(long changeId) { synchronized (mChanges) { CompatChange c = mChanges.get(changeId); - if (c == null) { - return false; - } - return c.getDisabled(); + return c != null && c.getDisabled(); } } /** - * Removes an override previously added via {@link #addOverride(long, String, boolean)}. This - * restores the default behaviour for the given change and app, once any app processes have been - * restarted. + * Removes an override previously added via {@link #addOverride(long, String, boolean)}. + * + * <p>This restores the default behaviour for the given change and app, once any app processes + * have been restarted. * - * @param changeId The ID of the change that was overridden. - * @param packageName The app package name that was overridden. + * @param changeId the ID of the change that was overridden + * @param packageName the app package name that was overridden * @return {@code true} if an override existed; */ - boolean removeOverride(long changeId, String packageName) - throws SecurityException { + boolean removeOverride(long changeId, String packageName) { + boolean overrideExists = removeOverrideUnsafe(changeId, packageName); + saveOverrides(); + invalidateCache(); + return overrideExists; + } + + /** + * Unsafe version of {@link #removeOverride(long, String)}. + * It does not invalidate the cache nor save the overrides. + */ + private boolean removeOverrideUnsafe(long changeId, String packageName) { boolean overrideExists = false; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); @@ -292,28 +329,27 @@ final class CompatConfig { } } } - invalidateCache(); return overrideExists; } /** * Overrides the enabled state for a given change and app. * - * <p>Note, package overrides are not persistent and will be lost on system or runtime restart. + * <p>Note: package overrides are not persistent and will be lost on system or runtime restart. * - * @param overrides list of overrides to default changes config. - * @param packageName app for which the overrides will be applied. + * @param overrides list of overrides to default changes config + * @param packageName app for which the overrides will be applied */ - void addOverrides(CompatibilityChangeConfig overrides, String packageName) - throws RemoteException, SecurityException { + void addOverrides(CompatibilityChangeConfig overrides, String packageName) { synchronized (mChanges) { for (Long changeId : overrides.enabledChanges()) { - addOverride(changeId, packageName, true); + addOverrideUnsafe(changeId, packageName, true); } for (Long changeId : overrides.disabledChanges()) { - addOverride(changeId, packageName, false); + addOverrideUnsafe(changeId, packageName, false); } + saveOverrides(); invalidateCache(); } } @@ -324,21 +360,21 @@ final class CompatConfig { * * <p>This restores the default behaviour for the given app. * - * @param packageName The package for which the overrides should be purged. + * @param packageName the package for which the overrides should be purged */ - void removePackageOverrides(String packageName) throws SecurityException { + void removePackageOverrides(String packageName) { synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { CompatChange change = mChanges.valueAt(i); - removeOverride(change.getId(), packageName); + removeOverrideUnsafe(change.getId(), packageName); } + saveOverrides(); invalidateCache(); } } private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName, - int targetSdkVersion) - throws RemoteException { + int targetSdkVersion) { LongArray allowed = new LongArray(); synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { @@ -348,7 +384,7 @@ final class CompatConfig { } OverrideAllowedState allowedState = mOverrideValidator.getOverrideAllowedState(change.getId(), - packageName); + packageName); if (allowedState.state == OverrideAllowedState.ALLOWED) { allowed.add(change.getId()); } @@ -361,30 +397,31 @@ final class CompatConfig { * Enables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for * {@param packageName}. * - * @return The number of changes that were toggled. + * @return the number of changes that were toggled */ - int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) - throws RemoteException { + int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) { long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); for (long changeId : changes) { - addOverride(changeId, packageName, true); + addOverrideUnsafe(changeId, packageName, true); } + saveOverrides(); + invalidateCache(); return changes.length; } - /** * Disables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for * {@param packageName}. * - * @return The number of changes that were toggled. + * @return the number of changes that were toggled */ - int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) - throws RemoteException { + int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) { long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); for (long changeId : changes) { - addOverride(changeId, packageName, false); + addOverrideUnsafe(changeId, packageName, false); } + saveOverrides(); + invalidateCache(); return changes.length; } @@ -425,7 +462,7 @@ final class CompatConfig { /** * Dumps the current list of compatibility config information. * - * @param pw The {@link PrintWriter} instance to which the information will be dumped. + * @param pw {@link PrintWriter} instance to which the information will be dumped */ void dumpConfig(PrintWriter pw) { synchronized (mChanges) { @@ -441,13 +478,10 @@ final class CompatConfig { } /** - * Get the config for a given app. + * Returns config for a given app. * - * @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped. - * @return A {@link CompatibilityChangeConfig} which contains the compat config info for the - * given app. + * @param applicationInfo the {@link ApplicationInfo} for which the info should be dumped */ - CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) { Set<Long> enabled = new HashSet<>(); Set<Long> disabled = new HashSet<>(); @@ -467,7 +501,7 @@ final class CompatConfig { /** * Dumps all the compatibility change information. * - * @return An array of {@link CompatibilityChangeInfo} with the current changes. + * @return an array of {@link CompatibilityChangeInfo} with the current changes */ CompatibilityChangeInfo[] dumpChanges() { synchronized (mChanges) { @@ -480,22 +514,6 @@ final class CompatConfig { } } - static CompatConfig create(AndroidBuildClassifier androidBuildClassifier, Context context) { - CompatConfig config = new CompatConfig(androidBuildClassifier, context); - config.initConfigFromLib(Environment.buildPath( - Environment.getRootDirectory(), "etc", "compatconfig")); - config.initConfigFromLib(Environment.buildPath( - Environment.getRootDirectory(), "system_ext", "etc", "compatconfig")); - - List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos(); - for (ApexManager.ActiveApexInfo apex : apexes) { - config.initConfigFromLib(Environment.buildPath( - apex.apexDirectory, "etc", "compatconfig")); - } - config.invalidateCache(); - return config; - } - void initConfigFromLib(File libraryDir) { if (!libraryDir.exists() || !libraryDir.isDirectory()) { Slog.d(TAG, "No directory " + libraryDir + ", skipping"); @@ -510,7 +528,8 @@ final class CompatConfig { private void readConfig(File configFile) { try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { - for (Change change : XmlParser.read(in).getCompatChange()) { + Config config = com.android.server.compat.config.XmlParser.read(in); + for (Change change : config.getCompatChange()) { Slog.d(TAG, "Adding: " + change.toString()); addChange(new CompatChange(change)); } @@ -519,6 +538,65 @@ final class CompatConfig { } } + void initOverrides(File overridesFile) { + if (!overridesFile.exists()) { + mOverridesFile = overridesFile; + // There have not been any overrides added yet. + return; + } + + try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) { + Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in); + for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) { + long changeId = changeOverrides.getChangeId(); + CompatChange compatChange = mChanges.get(changeId); + if (compatChange == null) { + Slog.w(TAG, "Change ID " + changeId + " not found. " + + "Skipping overrides for it."); + continue; + } + compatChange.loadOverrides(changeOverrides); + } + } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { + Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString()); + return; + } + mOverridesFile = overridesFile; + } + + /** + * Persist compat framework overrides to /data/misc/appcompat/compat_framework_overrides.xml + */ + void saveOverrides() { + if (mOverridesFile == null) { + return; + } + synchronized (mChanges) { + // Create the file if it doesn't already exist + try { + mOverridesFile.createNewFile(); + } catch (IOException e) { + Slog.e(TAG, "Could not create override config file: " + e.toString()); + return; + } + try (PrintWriter out = new PrintWriter(mOverridesFile)) { + XmlWriter writer = new XmlWriter(out); + Overrides overrides = new Overrides(); + List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides(); + for (int idx = 0; idx < mChanges.size(); ++idx) { + CompatChange c = mChanges.valueAt(idx); + ChangeOverrides changeOverrides = c.saveOverrides(); + if (changeOverrides != null) { + changeOverridesList.add(changeOverrides); + } + } + XmlWriter.write(writer, overrides); + } catch (IOException e) { + Slog.e(TAG, e.toString()); + } + } + } + IOverrideValidator getOverrideValidator() { return mOverrideValidator; } @@ -526,6 +604,7 @@ final class CompatConfig { private void invalidateCache() { ChangeIdStateCache.invalidate(); } + /** * Rechecks all the existing overrides for a package. */ diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 1ea468c341d2..6b2a1c950e38 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -63,45 +63,43 @@ public class PlatformCompat extends IPlatformCompat.Stub { private final ChangeReporter mChangeReporter; private final CompatConfig mCompatConfig; - private static int sMinTargetSdk = Build.VERSION_CODES.Q; - public PlatformCompat(Context context) { mContext = context; - mChangeReporter = new ChangeReporter( - ChangeReporter.SOURCE_SYSTEM_SERVER); + mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER); mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext); } @VisibleForTesting PlatformCompat(Context context, CompatConfig compatConfig) { mContext = context; - mChangeReporter = new ChangeReporter( - ChangeReporter.SOURCE_SYSTEM_SERVER); + mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER); mCompatConfig = compatConfig; + registerPackageReceiver(context); } @Override public void reportChange(long changeId, ApplicationInfo appInfo) { - checkCompatChangeLogPermission(); - reportChange(changeId, appInfo.uid, - ChangeReporter.STATE_LOGGED); + reportChangeByUid(changeId, appInfo.uid); } @Override - public void reportChangeByPackageName(long changeId, String packageName, int userId) { - checkCompatChangeLogPermission(); + public void reportChangeByPackageName(long changeId, String packageName, + @UserIdInt int userId) { ApplicationInfo appInfo = getApplicationInfo(packageName, userId); - if (appInfo == null) { - return; + if (appInfo != null) { + reportChangeByUid(changeId, appInfo.uid); } - reportChange(changeId, appInfo); } @Override public void reportChangeByUid(long changeId, int uid) { checkCompatChangeLogPermission(); - reportChange(changeId, uid, ChangeReporter.STATE_LOGGED); + reportChangeInternal(changeId, uid, ChangeReporter.STATE_LOGGED); + } + + private void reportChangeInternal(long changeId, int uid, int state) { + mChangeReporter.reportChange(uid, changeId, state); } @Override @@ -110,28 +108,6 @@ public class PlatformCompat extends IPlatformCompat.Stub { return isChangeEnabledInternal(changeId, appInfo); } - /** - * Internal version of the above method, without logging. Does not perform costly permission - * check. - * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property. - */ - public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) { - return mCompatConfig.isChangeEnabled(changeId, appInfo); - } - - /** - * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. Does not perform costly - * permission check. - */ - public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) { - boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo); - if (appInfo != null) { - reportChange(changeId, appInfo.uid, - enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED); - } - return enabled; - } - @Override public boolean isChangeEnabledByPackageName(long changeId, String packageName, @UserIdInt int userId) { @@ -140,7 +116,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { if (appInfo == null) { return mCompatConfig.willChangeBeEnabled(changeId, packageName); } - return isChangeEnabled(changeId, appInfo); + return isChangeEnabledInternal(changeId, appInfo); } @Override @@ -152,81 +128,82 @@ public class PlatformCompat extends IPlatformCompat.Stub { } boolean enabled = true; for (String packageName : packages) { - enabled = enabled && isChangeEnabledByPackageName(changeId, packageName, + enabled &= isChangeEnabledByPackageName(changeId, packageName, UserHandle.getUserId(uid)); } return enabled; } /** - * Register a listener for change state overrides. Only one listener per change is allowed. + * Internal version of the above method, without logging. * - * <p>{@code listener.onCompatChange(String)} method is guaranteed to be called with - * packageName before the app is killed upon an override change. The state of a change is not - * guaranteed to change when {@code listener.onCompatChange(String)} is called. + * <p>Does not perform costly permission check. + * TODO(b/167551701): Remove this method and add 'loggability' as a changeid property. + */ + public boolean isChangeEnabledInternalNoLogging(long changeId, ApplicationInfo appInfo) { + return mCompatConfig.isChangeEnabled(changeId, appInfo); + } + + /** + * Internal version of {@link #isChangeEnabled(long, ApplicationInfo)}. * - * @param changeId to get updates for - * @param listener the listener that will be called upon a potential change for package. - * @throws IllegalStateException if a listener was already registered for changeId - * @returns {@code true} if a change with changeId was already known, or (@code false} - * otherwise. + * <p>Does not perform costly permission check. */ - public boolean registerListener(long changeId, CompatChange.ChangeListener listener) { - return mCompatConfig.registerListener(changeId, listener); + public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) { + boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo); + if (appInfo != null) { + reportChangeInternal(changeId, appInfo.uid, + enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED); + } + return enabled; } @Override - public void setOverrides(CompatibilityChangeConfig overrides, String packageName) - throws RemoteException, SecurityException { + public void setOverrides(CompatibilityChangeConfig overrides, String packageName) { checkCompatChangeOverridePermission(); mCompatConfig.addOverrides(overrides, packageName); killPackage(packageName); } @Override - public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) - throws RemoteException, SecurityException { + public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) { checkCompatChangeOverridePermission(); mCompatConfig.addOverrides(overrides, packageName); } @Override - public int enableTargetSdkChanges(String packageName, int targetSdkVersion) - throws RemoteException, SecurityException { + public int enableTargetSdkChanges(String packageName, int targetSdkVersion) { checkCompatChangeOverridePermission(); - int numChanges = mCompatConfig.enableTargetSdkChangesForPackage(packageName, - targetSdkVersion); + int numChanges = + mCompatConfig.enableTargetSdkChangesForPackage(packageName, targetSdkVersion); killPackage(packageName); return numChanges; } @Override - public int disableTargetSdkChanges(String packageName, int targetSdkVersion) - throws RemoteException, SecurityException { + public int disableTargetSdkChanges(String packageName, int targetSdkVersion) { checkCompatChangeOverridePermission(); - int numChanges = mCompatConfig.disableTargetSdkChangesForPackage(packageName, - targetSdkVersion); + int numChanges = + mCompatConfig.disableTargetSdkChangesForPackage(packageName, targetSdkVersion); killPackage(packageName); return numChanges; } @Override - public void clearOverrides(String packageName) throws RemoteException, SecurityException { + public void clearOverrides(String packageName) { checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); killPackage(packageName); } @Override - public void clearOverridesForTest(String packageName) - throws RemoteException, SecurityException { + public void clearOverridesForTest(String packageName) { checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); } @Override - public boolean clearOverride(long changeId, String packageName) - throws RemoteException, SecurityException { + public boolean clearOverride(long changeId, String packageName) { checkCompatChangeOverridePermission(); boolean existed = mCompatConfig.removeOverride(changeId, packageName); killPackage(packageName); @@ -234,6 +211,12 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + public void clearOverrideForTest(long changeId, String packageName) { + checkCompatChangeOverridePermission(); + mCompatConfig.removeOverride(changeId, packageName); + } + + @Override public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { checkCompatChangeReadAndLogPermission(); return mCompatConfig.getAppConfig(appInfo); @@ -247,18 +230,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public CompatibilityChangeInfo[] listUIChanges() { - return Arrays.stream(listAllChanges()).filter( - x -> isShownInUI(x)).toArray(CompatibilityChangeInfo[]::new); + return Arrays.stream(listAllChanges()).filter(this::isShownInUI).toArray( + CompatibilityChangeInfo[]::new); } - /** - * Check whether the change is known to the compat config. - * - * @return {@code true} if the change is known. - */ + /** Checks whether the change is known to the compat config. */ public boolean isKnownChangeId(long changeId) { return mCompatConfig.isKnownChangeId(changeId); - } /** @@ -286,7 +264,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; + if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) { + return; + } checkCompatChangeReadAndLogPermission(); mCompatConfig.dumpConfig(pw); } @@ -298,7 +278,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { /** * Clears information stored about events reported on behalf of an app. - * To be called once upon app start or end. A second call would be a no-op. + * + * <p>To be called once upon app start or end. A second call would be a no-op. * * @param appInfo the app to reset */ @@ -311,13 +292,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { packageName, 0, userId, userId); } - private void reportChange(long changeId, int uid, int state) { - mChangeReporter.reportChange(uid, changeId, state); - } - private void killPackage(String packageName) { int uid = LocalServices.getService(PackageManagerInternal.class).getPackageUid(packageName, - 0, UserHandle.myUserId()); + 0, UserHandle.myUserId()); if (uid < 0) { Slog.w(TAG, "Didn't find package " + packageName + " on device."); @@ -325,21 +302,18 @@ public class PlatformCompat extends IPlatformCompat.Stub { } Slog.d(TAG, "Killing package " + packageName + " (UID " + uid + ")."); - killUid(UserHandle.getAppId(uid), - UserHandle.USER_ALL, "PlatformCompat overrides"); + killUid(UserHandle.getAppId(uid)); } - private void killUid(int appId, int userId, String reason) { + private void killUid(int appId) { final long identity = Binder.clearCallingIdentity(); try { IActivityManager am = ActivityManager.getService(); if (am != null) { - try { - am.killUid(appId, userId, reason); - } catch (RemoteException e) { - /* ignore - same process */ - } + am.killUid(appId, UserHandle.USER_ALL, "PlatformCompat overrides"); } + } catch (RemoteException e) { + /* ignore - same process */ } finally { Binder.restoreCallingIdentity(identity); } @@ -350,13 +324,12 @@ public class PlatformCompat extends IPlatformCompat.Stub { if (Binder.getCallingUid() == SYSTEM_UID) { return; } - if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) - != PERMISSION_GRANTED) { + if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) != PERMISSION_GRANTED) { throw new SecurityException("Cannot log compat change usage"); } } - private void checkCompatChangeReadPermission() throws SecurityException { + private void checkCompatChangeReadPermission() { // Don't check for permissions within the system process if (Binder.getCallingUid() == SYSTEM_UID) { return; @@ -367,7 +340,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { } } - private void checkCompatChangeOverridePermission() throws SecurityException { + private void checkCompatChangeOverridePermission() { // Don't check for permissions within the system process if (Binder.getCallingUid() == SYSTEM_UID) { return; @@ -378,7 +351,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { } } - private void checkCompatChangeReadAndLogPermission() throws SecurityException { + private void checkCompatChangeReadAndLogPermission() { checkCompatChangeReadPermission(); checkCompatChangeLogPermission(); } @@ -391,16 +364,34 @@ public class PlatformCompat extends IPlatformCompat.Stub { return false; } if (change.getEnableSinceTargetSdk() > 0) { - if (change.getEnableSinceTargetSdk() < sMinTargetSdk) { - return false; - } + return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q; } return true; } /** + * Registers a listener for change state overrides. + * + * <p>Only one listener per change is allowed. + * + * <p>{@code listener.onCompatChange(String)} method is guaranteed to be called with + * packageName before the app is killed upon an override change. The state of a change is not + * guaranteed to change when {@code listener.onCompatChange(String)} is called. + * + * @param changeId to get updates for + * @param listener the listener that will be called upon a potential change for package + * @return {@code true} if a change with changeId was already known, or (@code false} + * otherwise + * @throws IllegalStateException if a listener was already registered for changeId + */ + public boolean registerListener(long changeId, CompatChange.ChangeListener listener) { + return mCompatConfig.registerListener(changeId, listener); + } + + /** * Registers a broadcast receiver that listens for package install, replace or remove. - * @param context the context where the receiver should be registered. + * + * @param context the context where the receiver should be registered */ public void registerPackageReceiver(Context context) { final BroadcastReceiver receiver = new BroadcastReceiver() { @@ -429,8 +420,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } /** - * Register the observer for - * {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT} + * Registers the observer for + * {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT}. */ public void registerContentObserver() { mCompatConfig.registerContentObserver(); diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java index 995bb2422de2..8cd1fd6f2b64 100644 --- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java +++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java @@ -17,6 +17,8 @@ package com.android.server.connectivity; import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.metrics.DefaultNetworkEvent; import android.os.SystemClock; @@ -61,7 +63,7 @@ public class DefaultNetworkMetrics { private int mLastTransports; public DefaultNetworkMetrics() { - newDefaultNetwork(creationTimeMs, null); + newDefaultNetwork(creationTimeMs, null, 0, false, null, null); } public synchronized void listEvents(PrintWriter pw) { @@ -117,13 +119,21 @@ public class DefaultNetworkMetrics { mCurrentDefaultNetwork.validatedMs += timeMs - mLastValidationTimeMs; } - public synchronized void logDefaultNetworkEvent( - long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai) { - logCurrentDefaultNetwork(timeMs, oldNai); - newDefaultNetwork(timeMs, newNai); + /** + * Logs a default network event. + * @see {IpConnectivityLog#logDefaultNetworkEvent}. + */ + public synchronized void logDefaultNetworkEvent(long timeMs, Network defaultNetwork, int score, + boolean validated, LinkProperties lp, NetworkCapabilities nc, + Network previousDefaultNetwork, int previousScore, LinkProperties previousLp, + NetworkCapabilities previousNc) { + logCurrentDefaultNetwork(timeMs, previousDefaultNetwork, previousScore, previousLp, + previousNc); + newDefaultNetwork(timeMs, defaultNetwork, score, validated, lp, nc); } - private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) { + private void logCurrentDefaultNetwork(long timeMs, Network network, int score, + LinkProperties lp, NetworkCapabilities nc) { if (mIsCurrentlyValid) { updateValidationTime(timeMs); } @@ -131,10 +141,10 @@ public class DefaultNetworkMetrics { ev.updateDuration(timeMs); ev.previousTransports = mLastTransports; // oldNai is null if the system had no default network before the transition. - if (oldNai != null) { + if (network != null) { // The system acquired a new default network. - fillLinkInfo(ev, oldNai); - ev.finalScore = oldNai.getCurrentScore(); + fillLinkInfo(ev, network, lp, nc); + ev.finalScore = score; } // Only change transport of the previous default network if the event currently logged // corresponds to an existing default network, and not to the absence of a default network. @@ -147,14 +157,15 @@ public class DefaultNetworkMetrics { mEventsLog.append(ev); } - private void newDefaultNetwork(long timeMs, NetworkAgentInfo newNai) { + private void newDefaultNetwork(long timeMs, Network network, int score, boolean validated, + LinkProperties lp, NetworkCapabilities nc) { DefaultNetworkEvent ev = new DefaultNetworkEvent(timeMs); ev.durationMs = timeMs; // newNai is null if the system has no default network after the transition. - if (newNai != null) { - fillLinkInfo(ev, newNai); - ev.initialScore = newNai.getCurrentScore(); - if (newNai.lastValidated) { + if (network != null) { + fillLinkInfo(ev, network, lp, nc); + ev.initialScore = score; + if (validated) { mIsCurrentlyValid = true; mLastValidationTimeMs = timeMs; } @@ -164,10 +175,10 @@ public class DefaultNetworkMetrics { mCurrentDefaultNetwork = ev; } - private static void fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai) { - LinkProperties lp = nai.linkProperties; - ev.netId = nai.network().getNetId(); - ev.transports |= BitUtils.packBits(nai.networkCapabilities.getTransportTypes()); + private static void fillLinkInfo(DefaultNetworkEvent ev, Network network, LinkProperties lp, + NetworkCapabilities nc) { + ev.netId = network.getNetId(); + ev.transports |= BitUtils.packBits(nc.getTransportTypes()); ev.ipv4 |= lp.hasIpv4Address() && lp.hasIpv4DefaultRoute(); ev.ipv6 |= lp.hasGlobalIpv6Address() && lp.hasIpv6DefaultRoute(); } diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java index 2c06d8230f13..26244e62696b 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java @@ -20,11 +20,15 @@ import android.content.Context; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; import android.net.INetdEventCallback; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkStack; import android.net.metrics.ApfProgramEvent; import android.net.metrics.IpConnectivityLog; import android.os.Binder; import android.os.Process; +import android.os.SystemClock; import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateUtils; @@ -361,6 +365,23 @@ final public class IpConnectivityMetrics extends SystemService { } return mNetdListener.removeNetdEventCallback(callerType); } + + @Override + public void logDefaultNetworkValidity(boolean valid) { + NetworkStack.checkNetworkStackPermission(getContext()); + mDefaultNetworkMetrics.logDefaultNetworkValidity(SystemClock.elapsedRealtime(), valid); + } + + @Override + public void logDefaultNetworkEvent(Network defaultNetwork, int score, boolean validated, + LinkProperties lp, NetworkCapabilities nc, Network previousDefaultNetwork, + int previousScore, LinkProperties previousLp, NetworkCapabilities previousNc) { + NetworkStack.checkNetworkStackPermission(getContext()); + final long timeMs = SystemClock.elapsedRealtime(); + mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, defaultNetwork, score, validated, + lp, nc, previousDefaultNetwork, previousScore, previousLp, previousNc); + } + }; private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> { diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index c1b1b6a2f26c..952193b77681 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -246,11 +246,6 @@ public class Nat464Xlat extends BaseNetworkObserver { return; } - if (mNetwork.linkProperties == null) { - Log.e(TAG, "startClat: Can't start clat with null LinkProperties"); - return; - } - String baseIface = mNetwork.linkProperties.getInterfaceName(); if (baseIface == null) { Log.e(TAG, "startClat: Can't start clat on null interface"); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 7bde4d5a2770..1a4f20c7101e 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -36,13 +36,17 @@ import android.net.NetworkInfo; import android.net.NetworkMonitorManager; import android.net.NetworkRequest; import android.net.NetworkState; +import android.net.QosCallbackException; +import android.net.QosFilter; +import android.net.QosFilterParcelable; +import android.net.QosSession; import android.net.TcpKeepalivePacketData; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.SystemClock; +import android.telephony.data.EpsBearerQosSessionAttributes; import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -53,7 +57,7 @@ import com.android.internal.util.WakeupMessage; import com.android.server.ConnectivityService; import java.io.PrintWriter; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; @@ -136,12 +140,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // This Network object should always be used if possible, so as to encourage reuse of the // enclosed socket factory and connection pool. Avoid creating other Network objects. // This Network object is always valid. - public final Network network; - public LinkProperties linkProperties; + @NonNull public final Network network; + @NonNull public LinkProperties linkProperties; // This should only be modified by ConnectivityService, via setNetworkCapabilities(). // TODO: make this private with a getter. - public NetworkCapabilities networkCapabilities; - public final NetworkAgentConfig networkAgentConfig; + @NonNull public NetworkCapabilities networkCapabilities; + @NonNull public final NetworkAgentConfig networkAgentConfig; // Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true. // The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are @@ -189,41 +193,46 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Set to true when partial connectivity was detected. public boolean partialConnectivity; - // Captive portal info of the network, if any. + // Captive portal info of the network from RFC8908, if any. // Obtained by ConnectivityService and merged into NetworkAgent-provided information. - public CaptivePortalData captivePortalData; + public CaptivePortalData capportApiData; // The UID of the remote entity that created this Network. public final int creatorUid; + // Network agent portal info of the network, if any. This information is provided from + // non-RFC8908 sources, such as Wi-Fi Passpoint, which can provide information such as Venue + // URL, Terms & Conditions URL, and network friendly name. + public CaptivePortalData networkAgentPortalData; + // Networks are lingered when they become unneeded as a result of their NetworkRequests being // satisfied by a higher-scoring network. so as to allow communication to wrap up before the // network is taken down. This usually only happens to the default network. Lingering ends with // either the linger timeout expiring and the network being taken down, or the network // satisfying a request again. public static class LingerTimer implements Comparable<LingerTimer> { - public final NetworkRequest request; + public final int requestId; public final long expiryMs; - public LingerTimer(NetworkRequest request, long expiryMs) { - this.request = request; + public LingerTimer(int requestId, long expiryMs) { + this.requestId = requestId; this.expiryMs = expiryMs; } public boolean equals(Object o) { if (!(o instanceof LingerTimer)) return false; LingerTimer other = (LingerTimer) o; - return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs); + return (requestId == other.requestId) && (expiryMs == other.expiryMs); } public int hashCode() { - return Objects.hash(request.requestId, expiryMs); + return Objects.hash(requestId, expiryMs); } public int compareTo(LingerTimer other) { return (expiryMs != other.expiryMs) ? Long.compare(expiryMs, other.expiryMs) : - Integer.compare(request.requestId, other.request.requestId); + Integer.compare(requestId, other.requestId); } public String toString() { - return String.format("%s, expires %dms", request.toString(), + return String.format("%s, expires %dms", requestId, expiryMs - SystemClock.elapsedRealtime()); } } @@ -318,12 +327,20 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private final ConnectivityService mConnService; private final Context mContext; private final Handler mHandler; + private final QosCallbackTracker mQosCallbackTracker; public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info, - LinkProperties lp, NetworkCapabilities nc, int score, Context context, + @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context, Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber, - int creatorUid) { + int creatorUid, QosCallbackTracker qosCallbackTracker) { + Objects.requireNonNull(net); + Objects.requireNonNull(info); + Objects.requireNonNull(lp); + Objects.requireNonNull(nc); + Objects.requireNonNull(context); + Objects.requireNonNull(config); + Objects.requireNonNull(qosCallbackTracker); networkAgent = na; network = net; networkInfo = info; @@ -337,6 +354,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { networkAgentConfig = config; this.factorySerialNumber = factorySerialNumber; this.creatorUid = creatorUid; + mQosCallbackTracker = qosCallbackTracker; } private class AgentDeathMonitor implements IBinder.DeathRecipient { @@ -522,6 +540,31 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } } + /** + * Notify the NetworkAgent that the qos filter should be registered against the given qos + * callback id. + */ + public void onQosFilterCallbackRegistered(final int qosCallbackId, + final QosFilter qosFilter) { + try { + networkAgent.onQosFilterCallbackRegistered(qosCallbackId, + new QosFilterParcelable(qosFilter)); + } catch (final RemoteException e) { + Log.e(TAG, "Error registering a qos callback id against a qos filter", e); + } + } + + /** + * Notify the NetworkAgent that the given qos callback id should be unregistered. + */ + public void onQosCallbackUnregistered(final int qosCallbackId) { + try { + networkAgent.onQosCallbackUnregistered(qosCallbackId); + } catch (RemoteException e) { + Log.e(TAG, "Error unregistering a qos callback id", e); + } + } + // TODO: consider moving out of NetworkAgentInfo into its own class private class NetworkAgentMessageHandler extends INetworkAgentRegistry.Stub { private final Handler mHandler; @@ -531,19 +574,22 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } @Override - public void sendNetworkCapabilities(NetworkCapabilities nc) { + public void sendNetworkCapabilities(@NonNull NetworkCapabilities nc) { + Objects.requireNonNull(nc); mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED, new Pair<>(NetworkAgentInfo.this, nc)).sendToTarget(); } @Override - public void sendLinkProperties(LinkProperties lp) { + public void sendLinkProperties(@NonNull LinkProperties lp) { + Objects.requireNonNull(lp); mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, new Pair<>(NetworkAgentInfo.this, lp)).sendToTarget(); } @Override - public void sendNetworkInfo(NetworkInfo info) { + public void sendNetworkInfo(@NonNull NetworkInfo info) { + Objects.requireNonNull(info); mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_INFO_CHANGED, new Pair<>(NetworkAgentInfo.this, info)).sendToTarget(); } @@ -569,16 +615,25 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { @Override public void sendUnderlyingNetworks(@Nullable List<Network> networks) { - final Bundle args = new Bundle(); - if (networks instanceof ArrayList<?>) { - args.putParcelableArrayList(NetworkAgent.UNDERLYING_NETWORKS_KEY, - (ArrayList<Network>) networks); - } else { - args.putParcelableArrayList(NetworkAgent.UNDERLYING_NETWORKS_KEY, - networks == null ? null : new ArrayList<>(networks)); - } mHandler.obtainMessage(NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED, - new Pair<>(NetworkAgentInfo.this, args)).sendToTarget(); + new Pair<>(NetworkAgentInfo.this, networks)).sendToTarget(); + } + + @Override + public void sendEpsQosSessionAvailable(final int qosCallbackId, final QosSession session, + final EpsBearerQosSessionAttributes attributes) { + mQosCallbackTracker.sendEventQosSessionAvailable(qosCallbackId, session, attributes); + } + + @Override + public void sendQosSessionLost(final int qosCallbackId, final QosSession session) { + mQosCallbackTracker.sendEventQosSessionLost(qosCallbackId, session); + } + + @Override + public void sendQosCallbackError(final int qosCallbackId, + @QosCallbackException.ExceptionType final int exceptionType) { + mQosCallbackTracker.sendEventQosCallbackError(qosCallbackId, exceptionType); } } @@ -598,7 +653,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { * * @return the old capabilities of this network. */ - public synchronized NetworkCapabilities getAndSetNetworkCapabilities( + @NonNull public synchronized NetworkCapabilities getAndSetNetworkCapabilities( @NonNull final NetworkCapabilities nc) { final NetworkCapabilities oldNc = networkCapabilities; networkCapabilities = nc; @@ -693,7 +748,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { updateRequestCounts(REMOVE, existing); mNetworkRequests.remove(requestId); if (existing.isRequest()) { - unlingerRequest(existing); + unlingerRequest(existing.requestId); } } @@ -839,33 +894,33 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } /** - * Sets the specified request to linger on this network for the specified time. Called by + * Sets the specified requestId to linger on this network for the specified time. Called by * ConnectivityService when the request is moved to another network with a higher score. */ - public void lingerRequest(NetworkRequest request, long now, long duration) { - if (mLingerTimerForRequest.get(request.requestId) != null) { + public void lingerRequest(int requestId, long now, long duration) { + if (mLingerTimerForRequest.get(requestId) != null) { // Cannot happen. Once a request is lingering on a particular network, we cannot // re-linger it unless that network becomes the best for that request again, in which // case we should have unlingered it. - Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered"); + Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered"); } final long expiryMs = now + duration; - LingerTimer timer = new LingerTimer(request, expiryMs); + LingerTimer timer = new LingerTimer(requestId, expiryMs); if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString()); mLingerTimers.add(timer); - mLingerTimerForRequest.put(request.requestId, timer); + mLingerTimerForRequest.put(requestId, timer); } /** * Cancel lingering. Called by ConnectivityService when a request is added to this network. - * Returns true if the given request was lingering on this network, false otherwise. + * Returns true if the given requestId was lingering on this network, false otherwise. */ - public boolean unlingerRequest(NetworkRequest request) { - LingerTimer timer = mLingerTimerForRequest.get(request.requestId); + public boolean unlingerRequest(int requestId) { + LingerTimer timer = mLingerTimerForRequest.get(requestId); if (timer != null) { if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString()); mLingerTimers.remove(timer); - mLingerTimerForRequest.remove(request.requestId); + mLingerTimerForRequest.remove(requestId); return true; } return false; @@ -971,6 +1026,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { + (networkAgentConfig.acceptUnvalidated ? " acceptUnvalidated" : "") + (networkAgentConfig.acceptPartialConnectivity ? " acceptPartialConnectivity" : "") + (clatd.isStarted() ? " clat{" + clatd + "} " : "") + + (declaredUnderlyingNetworks != null + ? " underlying{" + Arrays.toString(declaredUnderlyingNetworks) + "}" : "") + " lp{" + linkProperties + "}" + " nc{" + networkCapabilities + "}" + "}"; diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java index a7be657ae7a3..5e6b9f39b40a 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java @@ -686,7 +686,7 @@ public class NetworkDiagnostics { mHostname = hostname; mMeasurement.description = "DNS TLS dst{" + mTarget.getHostAddress() + "} hostname{" - + TextUtils.emptyIfNull(mHostname) + "}"; + + (mHostname == null ? "" : mHostname) + "}"; } private SSLSocket setupSSLSocket() throws IOException { diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java index 06721aea5540..5dc8c1a00eaf 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacProxyInstaller.java @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2013, The Android Open Source Project +/* + * Copyright (C) 2013 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 + * 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, @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.server.connectivity; +import android.annotation.NonNull; import android.annotation.WorkerThread; import android.app.AlarmManager; import android.app.PendingIntent; @@ -52,7 +54,7 @@ import java.net.URLConnection; /** * @hide */ -public class PacManager { +public class PacProxyInstaller { private static final String PAC_PACKAGE = "com.android.pacprocessor"; private static final String PAC_SERVICE = "com.android.pacprocessor.PacService"; private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService"; @@ -60,7 +62,7 @@ public class PacManager { private static final String PROXY_PACKAGE = "com.android.proxyhandler"; private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService"; - private static final String TAG = "PacManager"; + private static final String TAG = "PacProxyInstaller"; private static final String ACTION_PAC_REFRESH = "android.net.proxy.PAC_REFRESH"; @@ -70,10 +72,6 @@ public class PacManager { private static final int DELAY_LONG = 4; private static final long MAX_PAC_SIZE = 20 * 1000 * 1000; - // Return values for #setCurrentProxyScriptUrl - public static final boolean DONT_SEND_BROADCAST = false; - public static final boolean DO_SEND_BROADCAST = true; - private String mCurrentPac; @GuardedBy("mProxyLock") private volatile Uri mPacUrl = Uri.EMPTY; @@ -92,7 +90,7 @@ public class PacManager { private volatile boolean mHasSentBroadcast; private volatile boolean mHasDownloaded; - private Handler mConnectivityHandler; + private final Handler mConnectivityHandler; private final int mProxyMessage; /** @@ -101,6 +99,13 @@ public class PacManager { private final Object mProxyLock = new Object(); /** + * Lock ensuring consistency between the values of mHasSentBroadcast, mHasDownloaded, the + * last URL and port, and the broadcast message being sent with the correct arguments. + * TODO : this should probably protect all instances of these variables + */ + private final Object mBroadcastStateLock = new Object(); + + /** * Runnable to download PAC script. * The behavior relies on the assumption it always runs on mNetThread to guarantee that the * latest data fetched from mPacUrl is stored in mProxyService. @@ -145,10 +150,10 @@ public class PacManager { } } - public PacManager(Context context, Handler handler, int proxyMessage) { + public PacProxyInstaller(@NonNull Context context, @NonNull Handler handler, int proxyMessage) { mContext = context; mLastPort = -1; - final HandlerThread netThread = new HandlerThread("android.pacmanager", + final HandlerThread netThread = new HandlerThread("android.pacproxyinstaller", android.os.Process.THREAD_PRIORITY_DEFAULT); netThread.start(); mNetThreadHandler = new Handler(netThread.getLooper()); @@ -163,43 +168,39 @@ public class PacManager { private AlarmManager getAlarmManager() { if (mAlarmManager == null) { - mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + mAlarmManager = mContext.getSystemService(AlarmManager.class); } return mAlarmManager; } /** - * Updates the PAC Manager with current Proxy information. This is called by + * Updates the PAC Proxy Installer with current Proxy information. This is called by * the ProxyTracker directly before a broadcast takes place to allow - * the PacManager to indicate that the broadcast should not be sent and the - * PacManager will trigger a new broadcast when it is ready. + * the PacProxyInstaller to indicate that the broadcast should not be sent and the + * PacProxyInstaller will trigger a new broadcast when it is ready. * * @param proxy Proxy information that is about to be broadcast. - * @return Returns whether the broadcast should be sent : either DO_ or DONT_SEND_BROADCAST */ - synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) { - if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) { - if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) { - // Allow to send broadcast, nothing to do. - return DO_SEND_BROADCAST; - } - mPacUrl = proxy.getPacFileUrl(); - mCurrentDelay = DELAY_1; - mHasSentBroadcast = false; - mHasDownloaded = false; - getAlarmManager().cancel(mPacRefreshIntent); - bind(); - return DONT_SEND_BROADCAST; - } else { - getAlarmManager().cancel(mPacRefreshIntent); - synchronized (mProxyLock) { - mPacUrl = Uri.EMPTY; - mCurrentPac = null; - if (mProxyService != null) { - unbind(); + public void setCurrentProxyScriptUrl(@NonNull ProxyInfo proxy) { + synchronized (mBroadcastStateLock) { + if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) { + if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) return; + mPacUrl = proxy.getPacFileUrl(); + mCurrentDelay = DELAY_1; + mHasSentBroadcast = false; + mHasDownloaded = false; + getAlarmManager().cancel(mPacRefreshIntent); + bind(); + } else { + getAlarmManager().cancel(mPacRefreshIntent); + synchronized (mProxyLock) { + mPacUrl = Uri.EMPTY; + mCurrentPac = null; + if (mProxyService != null) { + unbind(); + } } } - return DO_SEND_BROADCAST; } } @@ -233,10 +234,10 @@ public class PacManager { } private int getNextDelay(int currentDelay) { - if (++currentDelay > DELAY_4) { - return DELAY_4; - } - return currentDelay; + if (++currentDelay > DELAY_4) { + return DELAY_4; + } + return currentDelay; } private void longSchedule() { @@ -274,6 +275,7 @@ public class PacManager { getAlarmManager().set(AlarmManager.ELAPSED_REALTIME, timeTillTrigger, mPacRefreshIntent); } + @GuardedBy("mProxyLock") private void setCurrentProxyScript(String script) { if (mProxyService == null) { Log.e(TAG, "setCurrentProxyScript: no proxy service"); @@ -346,6 +348,9 @@ public class PacManager { public void setProxyPort(int port) { if (mLastPort != -1) { // Always need to send if port changed + // TODO: Here lacks synchronization because this write cannot + // guarantee that it's visible from sendProxyIfNeeded() when + // it's called by a Runnable which is post by mNetThread. mHasSentBroadcast = false; } mLastPort = port; @@ -385,13 +390,15 @@ public class PacManager { mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy)); } - private synchronized void sendProxyIfNeeded() { - if (!mHasDownloaded || (mLastPort == -1)) { - return; - } - if (!mHasSentBroadcast) { - sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort)); - mHasSentBroadcast = true; + private void sendProxyIfNeeded() { + synchronized (mBroadcastStateLock) { + if (!mHasDownloaded || (mLastPort == -1)) { + return; + } + if (!mHasSentBroadcast) { + sendPacBroadcast(ProxyInfo.buildPacProxy(mPacUrl, mLastPort)); + mHasSentBroadcast = true; + } } } } diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java index f6ca1523fe2d..b618d2b99a63 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018, The Android Open Source Project + * 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. @@ -67,7 +67,7 @@ public class ProxyTracker { // is not set. Individual networks have their own settings that override this. This member // is set through setDefaultProxy, which is called when the default network changes proxies // in its LinkProperties, or when ConnectivityService switches to a new default network, or - // when PacManager resolves the proxy. + // when PacProxyInstaller resolves the proxy. @Nullable @GuardedBy("mProxyLock") private volatile ProxyInfo mDefaultProxy = null; @@ -79,13 +79,14 @@ public class ProxyTracker { // The object responsible for Proxy Auto Configuration (PAC). @NonNull - private final PacManager mPacManager; + private final PacProxyInstaller mPacProxyInstaller; public ProxyTracker(@NonNull final Context context, @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) { mContext = context; mConnectivityServiceHandler = connectivityServiceInternalHandler; - mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent); + mPacProxyInstaller = new PacProxyInstaller( + context, connectivityServiceInternalHandler, pacChangedEvent); } // Convert empty ProxyInfo's to null as null-checks are used to determine if proxies are present @@ -181,7 +182,7 @@ public class ProxyTracker { if (!TextUtils.isEmpty(pacFileUrl)) { mConnectivityServiceHandler.post( - () -> mPacManager.setCurrentProxyScriptUrl(proxyProperties)); + () -> mPacProxyInstaller.setCurrentProxyScriptUrl(proxyProperties)); } } } @@ -225,7 +226,9 @@ public class ProxyTracker { final ProxyInfo defaultProxy = getDefaultProxy(); final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : ProxyInfo.buildDirectProxy("", 0, Collections.emptyList()); - if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) { + mPacProxyInstaller.setCurrentProxyScriptUrl(proxyInfo); + + if (!shouldSendBroadcast(proxyInfo)) { return; } if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo); @@ -241,6 +244,13 @@ public class ProxyTracker { } } + private boolean shouldSendBroadcast(ProxyInfo proxy) { + if (Uri.EMPTY.equals(proxy.getPacFileUrl())) return false; + if (proxy.getPacFileUrl().equals(proxy.getPacFileUrl()) + && (proxy.getPort() > 0)) return true; + return true; + } + /** * Sets the global proxy in memory. Also writes the values to the global settings of the device. * @@ -305,10 +315,10 @@ public class ProxyTracker { return; } - // This call could be coming from the PacManager, containing the port of the local - // proxy. If this new proxy matches the global proxy then copy this proxy to the + // This call could be coming from the PacProxyInstaller, containing the port of the + // local proxy. If this new proxy matches the global proxy then copy this proxy to the // global (to get the correct local port), and send a broadcast. - // TODO: Switch PacManager to have its own message to send back rather than + // TODO: Switch PacProxyInstaller to have its own message to send back rather than // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy. if ((mGlobalProxy != null) && (proxyInfo != null) && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java new file mode 100644 index 000000000000..816bf2be0d69 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java @@ -0,0 +1,192 @@ +/* + * 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 com.android.server.connectivity; + +import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE; + +import android.annotation.NonNull; +import android.net.IQosCallback; +import android.net.Network; +import android.net.QosCallbackException; +import android.net.QosFilter; +import android.net.QosSession; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.data.EpsBearerQosSessionAttributes; +import android.util.Slog; + +import java.util.Objects; + +/** + * Wraps callback related information and sends messages between network agent and the application. + * <p/> + * This is a satellite class of {@link com.android.server.ConnectivityService} and not meant + * to be used in other contexts. + * + * @hide + */ +class QosCallbackAgentConnection implements IBinder.DeathRecipient { + private static final String TAG = QosCallbackAgentConnection.class.getSimpleName(); + private static final boolean DBG = false; + + private final int mAgentCallbackId; + @NonNull private final QosCallbackTracker mQosCallbackTracker; + @NonNull private final IQosCallback mCallback; + @NonNull private final IBinder mBinder; + @NonNull private final QosFilter mFilter; + @NonNull private final NetworkAgentInfo mNetworkAgentInfo; + + private final int mUid; + + /** + * Gets the uid + * @return uid + */ + int getUid() { + return mUid; + } + + /** + * Gets the binder + * @return binder + */ + @NonNull + IBinder getBinder() { + return mBinder; + } + + /** + * Gets the callback id + * + * @return callback id + */ + int getAgentCallbackId() { + return mAgentCallbackId; + } + + /** + * Gets the network tied to the callback of this connection + * + * @return network + */ + @NonNull + Network getNetwork() { + return mFilter.getNetwork(); + } + + QosCallbackAgentConnection(@NonNull final QosCallbackTracker qosCallbackTracker, + final int agentCallbackId, + @NonNull final IQosCallback callback, + @NonNull final QosFilter filter, + final int uid, + @NonNull final NetworkAgentInfo networkAgentInfo) { + Objects.requireNonNull(qosCallbackTracker, "qosCallbackTracker must be non-null"); + Objects.requireNonNull(callback, "callback must be non-null"); + Objects.requireNonNull(filter, "filter must be non-null"); + Objects.requireNonNull(networkAgentInfo, "networkAgentInfo must be non-null"); + + mQosCallbackTracker = qosCallbackTracker; + mAgentCallbackId = agentCallbackId; + mCallback = callback; + mFilter = filter; + mUid = uid; + mBinder = mCallback.asBinder(); + mNetworkAgentInfo = networkAgentInfo; + } + + @Override + public void binderDied() { + logw("binderDied: binder died with callback id: " + mAgentCallbackId); + mQosCallbackTracker.unregisterCallback(mCallback); + } + + void unlinkToDeathRecipient() { + mBinder.unlinkToDeath(this, 0); + } + + // Returns false if the NetworkAgent was never notified. + boolean sendCmdRegisterCallback() { + final int exceptionType = mFilter.validate(); + if (exceptionType != EX_TYPE_FILTER_NONE) { + try { + if (DBG) log("sendCmdRegisterCallback: filter validation failed"); + mCallback.onError(exceptionType); + } catch (final RemoteException e) { + loge("sendCmdRegisterCallback:", e); + } + return false; + } + + try { + mBinder.linkToDeath(this, 0); + } catch (final RemoteException e) { + loge("failed linking to death recipient", e); + return false; + } + mNetworkAgentInfo.onQosFilterCallbackRegistered(mAgentCallbackId, mFilter); + return true; + } + + void sendCmdUnregisterCallback() { + if (DBG) log("sendCmdUnregisterCallback: unregistering"); + mNetworkAgentInfo.onQosCallbackUnregistered(mAgentCallbackId); + } + + void sendEventQosSessionAvailable(final QosSession session, + final EpsBearerQosSessionAttributes attributes) { + try { + if (DBG) log("sendEventQosSessionAvailable: sending..."); + mCallback.onQosEpsBearerSessionAvailable(session, attributes); + } catch (final RemoteException e) { + loge("sendEventQosSessionAvailable: remote exception", e); + } + } + + void sendEventQosSessionLost(@NonNull final QosSession session) { + try { + if (DBG) log("sendEventQosSessionLost: sending..."); + mCallback.onQosSessionLost(session); + } catch (final RemoteException e) { + loge("sendEventQosSessionLost: remote exception", e); + } + } + + void sendEventQosCallbackError(@QosCallbackException.ExceptionType final int exceptionType) { + try { + if (DBG) log("sendEventQosCallbackError: sending..."); + mCallback.onError(exceptionType); + } catch (final RemoteException e) { + loge("sendEventQosCallbackError: remote exception", e); + } + } + + private static void log(@NonNull final String msg) { + Slog.d(TAG, msg); + } + + private static void logw(@NonNull final String msg) { + Slog.w(TAG, msg); + } + + private static void loge(@NonNull final String msg, final Throwable t) { + Slog.e(TAG, msg, t); + } + + private static void logwtf(@NonNull final String msg) { + Slog.wtf(TAG, msg); + } +} diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java new file mode 100644 index 000000000000..87b4c162a2cc --- /dev/null +++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java @@ -0,0 +1,277 @@ +/* + * 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 com.android.server.connectivity; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.IQosCallback; +import android.net.Network; +import android.net.QosCallbackException; +import android.net.QosFilter; +import android.net.QosSession; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.telephony.data.EpsBearerQosSessionAttributes; +import android.util.Slog; + +import com.android.internal.util.CollectionUtils; +import com.android.server.ConnectivityService; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tracks qos callbacks and handles the communication between the network agent and application. + * <p/> + * Any method prefixed by handle must be called from the + * {@link com.android.server.ConnectivityService} handler thread. + * + * @hide + */ +public class QosCallbackTracker { + private static final String TAG = QosCallbackTracker.class.getSimpleName(); + private static final boolean DBG = true; + + @NonNull + private final Handler mConnectivityServiceHandler; + + @NonNull + private final ConnectivityService.PerUidCounter mNetworkRequestCounter; + + /** + * Each agent gets a unique callback id that is used to proxy messages back to the original + * callback. + * <p/> + * Note: The fact that this is initialized to 0 is to ensure that the thread running + * {@link #handleRegisterCallback(IQosCallback, QosFilter, int, NetworkAgentInfo)} sees the + * initialized value. This would not necessarily be the case if the value was initialized to + * the non-default value. + * <p/> + * Note: The term previous does not apply to the first callback id that is assigned. + */ + private int mPreviousAgentCallbackId = 0; + + @NonNull + private final List<QosCallbackAgentConnection> mConnections = new ArrayList<>(); + + /** + * + * @param connectivityServiceHandler must be the same handler used with + * {@link com.android.server.ConnectivityService} + * @param networkRequestCounter keeps track of the number of open requests under a given + * uid + */ + public QosCallbackTracker(@NonNull final Handler connectivityServiceHandler, + final ConnectivityService.PerUidCounter networkRequestCounter) { + mConnectivityServiceHandler = connectivityServiceHandler; + mNetworkRequestCounter = networkRequestCounter; + } + + /** + * Registers the callback with the tracker + * + * @param callback the callback to register + * @param filter the filter being registered alongside the callback + */ + public void registerCallback(@NonNull final IQosCallback callback, + @NonNull final QosFilter filter, @NonNull final NetworkAgentInfo networkAgentInfo) { + final int uid = Binder.getCallingUid(); + + // Enforce that the number of requests under this uid has exceeded the allowed number + mNetworkRequestCounter.incrementCountOrThrow(uid); + + mConnectivityServiceHandler.post( + () -> handleRegisterCallback(callback, filter, uid, networkAgentInfo)); + } + + private void handleRegisterCallback(@NonNull final IQosCallback callback, + @NonNull final QosFilter filter, final int uid, + @NonNull final NetworkAgentInfo networkAgentInfo) { + final QosCallbackAgentConnection ac = + handleRegisterCallbackInternal(callback, filter, uid, networkAgentInfo); + if (ac != null) { + if (DBG) log("handleRegisterCallback: added callback " + ac.getAgentCallbackId()); + mConnections.add(ac); + } else { + mNetworkRequestCounter.decrementCount(uid); + } + } + + private QosCallbackAgentConnection handleRegisterCallbackInternal( + @NonNull final IQosCallback callback, + @NonNull final QosFilter filter, final int uid, + @NonNull final NetworkAgentInfo networkAgentInfo) { + final IBinder binder = callback.asBinder(); + if (CollectionUtils.any(mConnections, c -> c.getBinder().equals(binder))) { + // A duplicate registration would have only made this far due to a programming error. + logwtf("handleRegisterCallback: Callbacks can only be register once."); + return null; + } + + mPreviousAgentCallbackId = mPreviousAgentCallbackId + 1; + final int newCallbackId = mPreviousAgentCallbackId; + + final QosCallbackAgentConnection ac = + new QosCallbackAgentConnection(this, newCallbackId, callback, + filter, uid, networkAgentInfo); + + final int exceptionType = filter.validate(); + if (exceptionType != QosCallbackException.EX_TYPE_FILTER_NONE) { + ac.sendEventQosCallbackError(exceptionType); + return null; + } + + // Only add to the callback maps if the NetworkAgent successfully registered it + if (!ac.sendCmdRegisterCallback()) { + // There was an issue when registering the agent + if (DBG) log("handleRegisterCallback: error sending register callback"); + mNetworkRequestCounter.decrementCount(uid); + return null; + } + return ac; + } + + /** + * Unregisters callback + * @param callback callback to unregister + */ + public void unregisterCallback(@NonNull final IQosCallback callback) { + mConnectivityServiceHandler.post(() -> handleUnregisterCallback(callback.asBinder(), true)); + } + + private void handleUnregisterCallback(@NonNull final IBinder binder, + final boolean sendToNetworkAgent) { + final QosCallbackAgentConnection agentConnection = + CollectionUtils.find(mConnections, c -> c.getBinder().equals(binder)); + if (agentConnection == null) { + logw("handleUnregisterCallback: agentConnection is null"); + return; + } + + if (DBG) { + log("handleUnregisterCallback: unregister " + + agentConnection.getAgentCallbackId()); + } + + mNetworkRequestCounter.decrementCount(agentConnection.getUid()); + mConnections.remove(agentConnection); + + if (sendToNetworkAgent) { + agentConnection.sendCmdUnregisterCallback(); + } + agentConnection.unlinkToDeathRecipient(); + } + + /** + * Called when the NetworkAgent sends the qos session available event + * + * @param qosCallbackId the callback id that the qos session is now available to + * @param session the qos session that is now available + * @param attributes the qos attributes that are now available on the qos session + */ + public void sendEventQosSessionAvailable(final int qosCallbackId, + final QosSession session, + final EpsBearerQosSessionAttributes attributes) { + runOnAgentConnection(qosCallbackId, "sendEventQosSessionAvailable: ", + ac -> ac.sendEventQosSessionAvailable(session, attributes)); + } + + /** + * Called when the NetworkAgent sends the qos session lost event + * + * @param qosCallbackId the callback id that lost the qos session + * @param session the corresponding qos session + */ + public void sendEventQosSessionLost(final int qosCallbackId, + final QosSession session) { + runOnAgentConnection(qosCallbackId, "sendEventQosSessionLost: ", + ac -> ac.sendEventQosSessionLost(session)); + } + + /** + * Called when the NetworkAgent sends the qos session on error event + * + * @param qosCallbackId the callback id that should receive the exception + * @param exceptionType the type of exception that caused the callback to error + */ + public void sendEventQosCallbackError(final int qosCallbackId, + @QosCallbackException.ExceptionType final int exceptionType) { + runOnAgentConnection(qosCallbackId, "sendEventQosCallbackError: ", + ac -> { + ac.sendEventQosCallbackError(exceptionType); + handleUnregisterCallback(ac.getBinder(), false); + }); + } + + /** + * Unregisters all callbacks associated to this network agent + * + * Note: Must be called on the connectivity service handler thread + * + * @param network the network that was released + */ + public void handleNetworkReleased(@Nullable final Network network) { + final List<QosCallbackAgentConnection> connections = + CollectionUtils.filter(mConnections, ac -> ac.getNetwork().equals(network)); + + for (final QosCallbackAgentConnection agentConnection : connections) { + agentConnection.sendEventQosCallbackError( + QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED); + + // Call unregister workflow w\o sending anything to agent since it is disconnected. + handleUnregisterCallback(agentConnection.getBinder(), false); + } + } + + private interface AgentConnectionAction { + void execute(@NonNull QosCallbackAgentConnection agentConnection); + } + + @Nullable + private void runOnAgentConnection(final int qosCallbackId, + @NonNull final String logPrefix, + @NonNull final AgentConnectionAction action) { + mConnectivityServiceHandler.post(() -> { + final QosCallbackAgentConnection ac = + CollectionUtils.find(mConnections, + c -> c.getAgentCallbackId() == qosCallbackId); + if (ac == null) { + loge(logPrefix + ": " + qosCallbackId + " missing callback id"); + return; + } + + action.execute(ac); + }); + } + + private static void log(final String msg) { + Slog.d(TAG, msg); + } + + private static void logw(final String msg) { + Slog.w(TAG, msg); + } + + private static void loge(final String msg) { + Slog.e(TAG, msg); + } + + private static void logwtf(final String msg) { + Slog.wtf(TAG, msg); + } +} diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 07a4b89be4e9..b455a3f4169f 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -70,6 +70,7 @@ import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.UidRange; import android.net.UidRangeParcel; +import android.net.UnderlyingNetworkInfo; import android.net.VpnManager; import android.net.VpnService; import android.net.ipsec.ike.ChildSessionCallback; @@ -109,7 +110,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; -import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnProfile; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; @@ -426,6 +426,7 @@ public class Vpn { mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); + mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); loadAlwaysOnPackage(keyStore); } @@ -439,6 +440,11 @@ public class Vpn { mEnableTeardown = enableTeardown; } + @VisibleForTesting + public boolean getEnableTeardown() { + return mEnableTeardown; + } + /** * Update current state, dispatching event to listeners. */ @@ -1229,7 +1235,8 @@ public class Vpn { private boolean canHaveRestrictedProfile(int userId) { final long token = Binder.clearCallingIdentity(); try { - return UserManager.get(mContext).canHaveRestrictedProfile(userId); + final Context userContext = mContext.createContextAsUser(UserHandle.of(userId), 0); + return userContext.getSystemService(UserManager.class).canHaveRestrictedProfile(); } finally { Binder.restoreCallingIdentity(token); } @@ -1810,18 +1817,15 @@ public class Vpn { } /** - * This method should only be called by ConnectivityService because it doesn't - * have enough data to fill VpnInfo.primaryUnderlyingIface field. + * This method should not be called if underlying interfaces field is needed, because it doesn't + * have enough data to fill VpnInfo.underlyingIfaces field. */ - public synchronized VpnInfo getVpnInfo() { + public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() { if (!isRunningLocked()) { return null; } - VpnInfo info = new VpnInfo(); - info.ownerUid = mOwnerUID; - info.vpnIface = mInterface; - return info; + return new UnderlyingNetworkInfo(mOwnerUID, mInterface, new ArrayList<>()); } public synchronized boolean appliesToUid(int uid) { @@ -1850,34 +1854,6 @@ public class Vpn { } } - /** - * @param uid The target uid. - * - * @return {@code true} if {@code uid} is included in one of the mBlockedUidsAsToldToNetd - * ranges and the VPN is not connected, or if the VPN is connected but does not apply to - * the {@code uid}. - * - * @apiNote This method don't check VPN lockdown status. - * @see #mBlockedUidsAsToldToConnectivity - */ - public synchronized boolean isBlockingUid(int uid) { - if (mNetworkInfo.isConnected()) { - return !appliesToUid(uid); - } else { - return containsUid(mBlockedUidsAsToldToConnectivity, uid); - } - } - - private boolean containsUid(Collection<UidRangeParcel> ranges, int uid) { - if (ranges == null) return false; - for (UidRangeParcel range : ranges) { - if (range.start <= uid && uid <= range.stop) { - return true; - } - } - return false; - } - private void updateAlwaysOnNotification(DetailedState networkState) { final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED); @@ -2173,6 +2149,11 @@ public class Vpn { // Start a new LegacyVpnRunner and we are done! mVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile); + startLegacyVpnRunner(); + } + + @VisibleForTesting + protected void startLegacyVpnRunner() { mVpnRunner.start(); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index ab289ea6f081..f876e1ad4b88 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -901,7 +901,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { @ServiceThreadOnly void setArcStatus(boolean enabled) { - // TODO(shubang): add tests assertRunOnServiceThread(); HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 8e50bb4885d8..5d1c4e6715f1 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -621,7 +621,14 @@ public class HdmiControlService extends SystemService { mWakeUpMessageReceived = false; if (isTvDeviceEnabled()) { - mCecController.setOption(OptionKey.WAKEUP, tv().getAutoWakeup()); + boolean autoWakeupEnabled = + readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true); + boolean autoDeviceOffEnabled = + readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true); + + mCecController.setOption(OptionKey.WAKEUP, autoWakeupEnabled); + tv().setAutoWakeup(autoWakeupEnabled); + tv().setAutoDeviceOff(autoDeviceOffEnabled); } int reason = -1; switch (initiatedBy) { diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java index 6c8694ea74ad..9a52c19c2771 100644 --- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java +++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java @@ -17,8 +17,8 @@ package com.android.server.hdmi; import android.annotation.Nullable; -import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.os.RemoteException; import android.util.Slog; diff --git a/services/core/java/com/android/server/location/contexthub/OWNERS b/services/core/java/com/android/server/location/contexthub/OWNERS index d4393d6a83d2..90c233030ed1 100644 --- a/services/core/java/com/android/server/location/contexthub/OWNERS +++ b/services/core/java/com/android/server/location/contexthub/OWNERS @@ -1,2 +1,3 @@ arthuri@google.com bduddie@google.com +stange@google.com diff --git a/services/core/java/com/android/server/location/timezone/OWNERS b/services/core/java/com/android/server/location/timezone/OWNERS deleted file mode 100644 index 28aff188dbd8..000000000000 --- a/services/core/java/com/android/server/location/timezone/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 847766 -nfuller@google.com -include /core/java/android/app/timedetector/OWNERS diff --git a/services/core/java/com/android/server/locksettings/AesEncryptionUtil.java b/services/core/java/com/android/server/locksettings/AesEncryptionUtil.java new file mode 100644 index 000000000000..8e7e419a6b0e --- /dev/null +++ b/services/core/java/com/android/server/locksettings/AesEncryptionUtil.java @@ -0,0 +1,109 @@ +/* + * 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 com.android.server.locksettings; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Objects; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; + +class AesEncryptionUtil { + /** The algorithm used for the encryption of the key blob. */ + private static final String CIPHER_ALGO = "AES/GCM/NoPadding"; + + private AesEncryptionUtil() {} + + static byte[] decrypt(SecretKey key, DataInputStream cipherStream) throws IOException { + Objects.requireNonNull(key); + Objects.requireNonNull(cipherStream); + + int ivSize = cipherStream.readInt(); + if (ivSize < 0 || ivSize > 32) { + throw new IOException("IV out of range: " + ivSize); + } + byte[] iv = new byte[ivSize]; + cipherStream.readFully(iv); + + int rawCipherTextSize = cipherStream.readInt(); + if (rawCipherTextSize < 0) { + throw new IOException("Invalid cipher text size: " + rawCipherTextSize); + } + + byte[] rawCipherText = new byte[rawCipherTextSize]; + cipherStream.readFully(rawCipherText); + + final byte[] plainText; + try { + Cipher c = Cipher.getInstance(CIPHER_ALGO); + c.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv)); + plainText = c.doFinal(rawCipherText); + } catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException + | IllegalBlockSizeException | NoSuchPaddingException + | InvalidAlgorithmParameterException e) { + throw new IOException("Could not decrypt cipher text", e); + } + + return plainText; + } + + static byte[] decrypt(SecretKey key, byte[] cipherText) throws IOException { + Objects.requireNonNull(key); + Objects.requireNonNull(cipherText); + + DataInputStream cipherStream = new DataInputStream(new ByteArrayInputStream(cipherText)); + return decrypt(key, cipherStream); + } + + static byte[] encrypt(SecretKey key, byte[] plainText) throws IOException { + Objects.requireNonNull(key); + Objects.requireNonNull(plainText); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + + final byte[] cipherText; + final byte[] iv; + try { + Cipher cipher = Cipher.getInstance(CIPHER_ALGO); + cipher.init(Cipher.ENCRYPT_MODE, key); + cipherText = cipher.doFinal(plainText); + iv = cipher.getIV(); + } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException + | NoSuchPaddingException | InvalidKeyException e) { + throw new IOException("Could not encrypt input data", e); + } + + dos.writeInt(iv.length); + dos.write(iv); + dos.writeInt(cipherText.length); + dos.write(cipherText); + + return bos.toByteArray(); + } +} diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index d003b89e8877..c005af4e9696 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -89,6 +89,7 @@ import android.os.storage.StorageManager; import android.provider.Settings; import android.provider.Settings.Secure; import android.provider.Settings.SettingNotFoundException; +import android.security.Authorization; import android.security.KeyStore; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyProperties; @@ -1272,6 +1273,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void unlockKeystore(byte[] password, int userHandle) { if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle); + new Authorization().onLockScreenEvent(false, userHandle, password); // TODO(b/120484642): Update keystore to accept byte[] passwords String passwordString = password == null ? null : new String(password); final KeyStore ks = KeyStore.getInstance(); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 81d07cc11527..c4225eda7d24 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -88,6 +88,7 @@ class LockSettingsStorage { private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key"; private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key"; + private static final String REBOOT_ESCROW_SERVER_BLOB = "reboot.escrow.server.blob.key"; private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/"; @@ -317,6 +318,22 @@ class LockSettingsStorage { deleteFile(getRebootEscrowFile(userId)); } + public void writeRebootEscrowServerBlob(byte[] serverBlob) { + writeFile(getRebootEscrowServerBlob(), serverBlob); + } + + public byte[] readRebootEscrowServerBlob() { + return readFile(getRebootEscrowServerBlob()); + } + + public boolean hasRebootEscrowServerBlob() { + return hasFile(getRebootEscrowServerBlob()); + } + + public void removeRebootEscrowServerBlob() { + deleteFile(getRebootEscrowServerBlob()); + } + public boolean hasPassword(int userId) { return hasFile(getLockPasswordFilename(userId)); } @@ -445,6 +462,12 @@ class LockSettingsStorage { return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE); } + @VisibleForTesting + String getRebootEscrowServerBlob() { + // There is a single copy of server blob for all users. + return getLockCredentialFilePathForUser(UserHandle.USER_SYSTEM, REBOOT_ESCROW_SERVER_BLOB); + } + private String getLockCredentialFilePathForUser(int userId, String basename) { String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() + SYSTEM_DIRECTORY; diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowData.java b/services/core/java/com/android/server/locksettings/RebootEscrowData.java index 2b1907985aeb..38eeb88e63b0 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowData.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowData.java @@ -16,22 +16,14 @@ package com.android.server.locksettings; -import com.android.internal.util.Preconditions; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import java.util.Objects; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; +import javax.crypto.SecretKey; /** * Holds the data necessary to complete a reboot escrow of the Synthetic Password. @@ -41,22 +33,17 @@ class RebootEscrowData { * This is the current version of the escrow data format. This should be incremented if the * format on disk is changed. */ - private static final int CURRENT_VERSION = 1; - - /** The algorithm used for the encryption of the key blob. */ - private static final String CIPHER_ALGO = "AES/GCM/NoPadding"; + private static final int CURRENT_VERSION = 2; - private RebootEscrowData(byte spVersion, byte[] iv, byte[] syntheticPassword, byte[] blob, + private RebootEscrowData(byte spVersion, byte[] syntheticPassword, byte[] blob, RebootEscrowKey key) { mSpVersion = spVersion; - mIv = iv; mSyntheticPassword = syntheticPassword; mBlob = blob; mKey = key; } private final byte mSpVersion; - private final byte[] mIv; private final byte[] mSyntheticPassword; private final byte[] mBlob; private final RebootEscrowKey mKey; @@ -65,10 +52,6 @@ class RebootEscrowData { return mSpVersion; } - public byte[] getIv() { - return mIv; - } - public byte[] getSyntheticPassword() { return mSyntheticPassword; } @@ -81,76 +64,43 @@ class RebootEscrowData { return mKey; } - static RebootEscrowData fromEncryptedData(RebootEscrowKey key, byte[] blob) + static RebootEscrowData fromEncryptedData(RebootEscrowKey ks, byte[] blob, SecretKey kk) throws IOException { - Preconditions.checkNotNull(key); - Preconditions.checkNotNull(blob); + Objects.requireNonNull(ks); + Objects.requireNonNull(blob); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob)); int version = dis.readInt(); if (version != CURRENT_VERSION) { throw new IOException("Unsupported version " + version); } - byte spVersion = dis.readByte(); - int ivSize = dis.readInt(); - if (ivSize < 0 || ivSize > 32) { - throw new IOException("IV out of range: " + ivSize); - } - byte[] iv = new byte[ivSize]; - dis.readFully(iv); + // Decrypt the blob with the key from keystore first, then decrypt again with the reboot + // escrow key. + byte[] ksEncryptedBlob = AesEncryptionUtil.decrypt(kk, dis); + final byte[] syntheticPassword = AesEncryptionUtil.decrypt(ks.getKey(), ksEncryptedBlob); - int cipherTextSize = dis.readInt(); - if (cipherTextSize < 0) { - throw new IOException("Invalid cipher text size: " + cipherTextSize); - } - - byte[] cipherText = new byte[cipherTextSize]; - dis.readFully(cipherText); - - final byte[] syntheticPassword; - try { - Cipher c = Cipher.getInstance(CIPHER_ALGO); - c.init(Cipher.DECRYPT_MODE, key.getKey(), new IvParameterSpec(iv)); - syntheticPassword = c.doFinal(cipherText); - } catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException - | IllegalBlockSizeException | NoSuchPaddingException - | InvalidAlgorithmParameterException e) { - throw new IOException("Could not decrypt ciphertext", e); - } - - return new RebootEscrowData(spVersion, iv, syntheticPassword, blob, key); + return new RebootEscrowData(spVersion, syntheticPassword, blob, ks); } - static RebootEscrowData fromSyntheticPassword(RebootEscrowKey key, byte spVersion, - byte[] syntheticPassword) + static RebootEscrowData fromSyntheticPassword(RebootEscrowKey ks, byte spVersion, + byte[] syntheticPassword, SecretKey kk) throws IOException { - Preconditions.checkNotNull(syntheticPassword); + Objects.requireNonNull(syntheticPassword); + + // Encrypt synthetic password with the escrow key first; then encrypt the blob again with + // the key from keystore. + byte[] ksEncryptedBlob = AesEncryptionUtil.encrypt(ks.getKey(), syntheticPassword); + byte[] kkEncryptedBlob = AesEncryptionUtil.encrypt(kk, ksEncryptedBlob); ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); - final byte[] cipherText; - final byte[] iv; - try { - Cipher cipher = Cipher.getInstance(CIPHER_ALGO); - cipher.init(Cipher.ENCRYPT_MODE, key.getKey()); - cipherText = cipher.doFinal(syntheticPassword); - iv = cipher.getIV(); - } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException - | NoSuchPaddingException | InvalidKeyException e) { - throw new IOException("Could not encrypt reboot escrow data", e); - } - dos.writeInt(CURRENT_VERSION); dos.writeByte(spVersion); - dos.writeInt(iv.length); - dos.write(iv); - dos.writeInt(cipherText.length); - dos.write(cipherText); + dos.write(kkEncryptedBlob); - return new RebootEscrowData(spVersion, iv, syntheticPassword, bos.toByteArray(), - key); + return new RebootEscrowData(spVersion, syntheticPassword, bos.toByteArray(), ks); } } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java new file mode 100644 index 000000000000..bae029c79968 --- /dev/null +++ b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java @@ -0,0 +1,134 @@ +/* + * 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 com.android.server.locksettings; + +import android.security.keystore.AndroidKeyStoreSpi; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; +import android.security.keystore2.AndroidKeyStoreProvider; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +/** + * This class loads and generates the key used for resume on reboot from android keystore. + */ +public class RebootEscrowKeyStoreManager { + private static final String TAG = "RebootEscrowKeyStoreManager"; + + /** + * The key alias in keystore. This key is used to wrap both escrow key and escrow data. + */ + public static final String REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME = + "reboot_escrow_key_store_encryption_key"; + + public static final int KEY_LENGTH = 256; + + /** + * Use keystore2 once it's installed. + */ + private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeystore"; + + /** + * The selinux namespace for resume_on_reboot_key + */ + private static final int KEY_STORE_NAMESPACE = 120; + + /** + * Hold this lock when getting or generating the encryption key in keystore. + */ + private final Object mKeyStoreLock = new Object(); + + @GuardedBy("mKeyStoreLock") + private SecretKey getKeyStoreEncryptionKeyLocked() { + try { + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); + KeyStore.LoadStoreParameter loadStoreParameter = null; + // Load from the specific namespace if keystore2 is enabled. + if (AndroidKeyStoreProvider.isInstalled()) { + loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE); + } + keyStore.load(loadStoreParameter); + return (SecretKey) keyStore.getKey(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME, + null); + } catch (IOException | GeneralSecurityException e) { + Slog.e(TAG, "Unable to get encryption key from keystore.", e); + } + return null; + } + + protected SecretKey getKeyStoreEncryptionKey() { + synchronized (mKeyStoreLock) { + return getKeyStoreEncryptionKeyLocked(); + } + } + + protected void clearKeyStoreEncryptionKey() { + synchronized (mKeyStoreLock) { + try { + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); + KeyStore.LoadStoreParameter loadStoreParameter = null; + // Load from the specific namespace if keystore2 is enabled. + if (AndroidKeyStoreProvider.isInstalled()) { + loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE); + } + keyStore.load(loadStoreParameter); + keyStore.deleteEntry(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME); + } catch (IOException | GeneralSecurityException e) { + Slog.e(TAG, "Unable to delete encryption key in keystore.", e); + } + } + } + + protected SecretKey generateKeyStoreEncryptionKeyIfNeeded() { + synchronized (mKeyStoreLock) { + SecretKey kk = getKeyStoreEncryptionKeyLocked(); + if (kk != null) { + return kk; + } + + try { + KeyGenerator generator = KeyGenerator.getInstance( + KeyProperties.KEY_ALGORITHM_AES, AndroidKeyStoreSpi.NAME); + KeyGenParameterSpec.Builder parameterSpecBuilder = new KeyGenParameterSpec.Builder( + REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setKeySize(KEY_LENGTH) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE); + // Generate the key with the correct namespace if keystore2 is enabled. + if (AndroidKeyStoreProvider.isInstalled()) { + parameterSpecBuilder.setNamespace(KEY_STORE_NAMESPACE); + } + generator.init(parameterSpecBuilder.build()); + return generator.generateKey(); + } catch (GeneralSecurityException e) { + // Should never happen. + Slog.e(TAG, "Unable to generate key from keystore.", e); + } + return null; + } + } +} diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 8d5f553dba5c..06962d414009 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -40,6 +40,18 @@ import java.util.Date; import java.util.List; import java.util.Locale; +import javax.crypto.SecretKey; + +/** + * This class aims to persists the synthetic password(SP) across reboot in a secure way. In + * particular, it manages the encryption of the sp before reboot, and decryption of the sp after + * reboot. Here are the meaning of some terms. + * SP: synthetic password + * K_s: The RebootEscrowKey, i.e. AES-GCM key stored in memory + * K_k: AES-GCM key in android keystore + * RebootEscrowData: The synthetic password and its encrypted blob. We encrypt SP with K_s first, + * then with K_k, i.e. E(K_k, E(K_s, SP)) + */ class RebootEscrowManager { private static final String TAG = "RebootEscrowManager"; @@ -101,6 +113,8 @@ class RebootEscrowManager { private final Callbacks mCallbacks; + private final RebootEscrowKeyStoreManager mKeyStoreManager; + interface Callbacks { boolean isUserSecure(int userId); @@ -109,25 +123,29 @@ class RebootEscrowManager { static class Injector { protected Context mContext; + private final RebootEscrowKeyStoreManager mKeyStoreManager; + private final LockSettingsStorage mStorage; + private RebootEscrowProviderInterface mRebootEscrowProvider; - private final RebootEscrowProviderInterface mRebootEscrowProvider; - - Injector(Context context) { + Injector(Context context, LockSettingsStorage storage) { mContext = context; - RebootEscrowProviderInterface rebootEscrowProvider = null; - // TODO(xunchang) add implementation for server based ror. + mStorage = storage; + mKeyStoreManager = new RebootEscrowKeyStoreManager(); + } + + private RebootEscrowProviderInterface createRebootEscrowProvider() { + RebootEscrowProviderInterface rebootEscrowProvider; if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA, "server_based_ror_enabled", false)) { - Slog.e(TAG, "Server based ror isn't implemented yet."); + rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage); } else { rebootEscrowProvider = new RebootEscrowProviderHalImpl(); } - if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) { - mRebootEscrowProvider = rebootEscrowProvider; - } else { - mRebootEscrowProvider = null; + if (rebootEscrowProvider.hasRebootEscrowSupport()) { + return rebootEscrowProvider; } + return null; } public Context getContext() { @@ -138,7 +156,17 @@ class RebootEscrowManager { return (UserManager) mContext.getSystemService(Context.USER_SERVICE); } + public RebootEscrowKeyStoreManager getKeyStoreManager() { + return mKeyStoreManager; + } + public RebootEscrowProviderInterface getRebootEscrowProvider() { + // Initialize for the provider lazily. Because the device_config and service + // implementation apps may change when system server is running. + if (mRebootEscrowProvider == null) { + mRebootEscrowProvider = createRebootEscrowProvider(); + } + return mRebootEscrowProvider; } @@ -157,7 +185,7 @@ class RebootEscrowManager { } RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) { - this(new Injector(context), callbacks, storage); + this(new Injector(context, storage), callbacks, storage); } @VisibleForTesting @@ -168,6 +196,7 @@ class RebootEscrowManager { mStorage = storage; mUserManager = injector.getUserManager(); mEventLog = injector.getEventLog(); + mKeyStoreManager = injector.getKeyStoreManager(); } void loadRebootEscrowDataIfAvailable() { @@ -183,8 +212,12 @@ class RebootEscrowManager { return; } - RebootEscrowKey escrowKey = getAndClearRebootEscrowKey(); - if (escrowKey == null) { + // Fetch the key from keystore to decrypt the escrow data & escrow key; this key is + // generated before reboot. Note that we will clear the escrow key even if the keystore key + // is null. + SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey(); + RebootEscrowKey escrowKey = getAndClearRebootEscrowKey(kk); + if (kk == null || escrowKey == null) { Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage."); for (UserInfo user : users) { mStorage.removeRebootEscrow(user.id); @@ -197,8 +230,12 @@ class RebootEscrowManager { boolean allUsersUnlocked = true; for (UserInfo user : rebootEscrowUsers) { - allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey); + allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk); } + + // Clear the old key in keystore. A new key will be generated by new RoR requests. + mKeyStoreManager.clearKeyStoreEncryptionKey(); + onEscrowRestoreComplete(allUsersUnlocked); } @@ -212,7 +249,7 @@ class RebootEscrowManager { } } - private RebootEscrowKey getAndClearRebootEscrowKey() { + private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) { RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider(); if (rebootEscrowProvider == null) { Slog.w(TAG, @@ -220,14 +257,16 @@ class RebootEscrowManager { return null; } - RebootEscrowKey key = rebootEscrowProvider.getAndClearRebootEscrowKey(null); + // The K_s blob maybe encrypted by K_k as well. + RebootEscrowKey key = rebootEscrowProvider.getAndClearRebootEscrowKey(kk); if (key != null) { mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_STORED_KEK); } return key; } - private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey key) { + private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey ks, + SecretKey kk) { if (!mStorage.hasRebootEscrow(userId)) { return false; } @@ -236,7 +275,7 @@ class RebootEscrowManager { byte[] blob = mStorage.readRebootEscrow(userId); mStorage.removeRebootEscrow(userId); - RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(key, blob); + RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(ks, blob, kk); mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(), escrowData.getSyntheticPassword(), userId); @@ -267,11 +306,16 @@ class RebootEscrowManager { return; } + SecretKey kk = mKeyStoreManager.generateKeyStoreEncryptionKeyIfNeeded(); + if (kk == null) { + Slog.e(TAG, "Failed to generate encryption key from keystore."); + return; + } + final RebootEscrowData escrowData; try { - // TODO(xunchang) further wrap the escrowData with a key from keystore. escrowData = RebootEscrowData.fromSyntheticPassword(escrowKey, spVersion, - syntheticPassword); + syntheticPassword, kk); } catch (IOException e) { setRebootEscrowReady(false); Slog.w(TAG, "Could not escrow reboot data", e); @@ -348,7 +392,13 @@ class RebootEscrowManager { return false; } - boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, null); + // We will use the same key from keystore to encrypt the escrow key and escrow data blob. + SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey(); + if (kk == null) { + Slog.e(TAG, "Failed to get encryption key from keystore."); + return false; + } + boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk); if (armedRebootEscrow) { mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM); mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS); diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java new file mode 100644 index 000000000000..ba1a680ba7fb --- /dev/null +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java @@ -0,0 +1,202 @@ +/* + * 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.locksettings; + +import android.annotation.Nullable; +import android.content.Context; +import android.os.RemoteException; +import android.provider.DeviceConfig; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +import javax.crypto.SecretKey; + +/** + * An implementation of the {@link RebootEscrowProviderInterface} by communicating with server to + * encrypt & decrypt the blob. + */ +class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface { + private static final String TAG = "RebootEscrowProvider"; + + // Timeout for service binding + private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10; + + /** + * Use the default lifetime of 10 minutes. The lifetime covers the following activities: + * Server wrap secret -> device reboot -> server unwrap blob. + */ + private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000; + + private final LockSettingsStorage mStorage; + + private final Injector mInjector; + + static class Injector { + private ResumeOnRebootServiceConnection mServiceConnection = null; + + Injector(Context context) { + mServiceConnection = new ResumeOnRebootServiceProvider(context).getServiceConnection(); + if (mServiceConnection == null) { + Slog.e(TAG, "Failed to resolve resume on reboot server service."); + } + } + + Injector(ResumeOnRebootServiceConnection serviceConnection) { + mServiceConnection = serviceConnection; + } + + @Nullable + private ResumeOnRebootServiceConnection getServiceConnection() { + return mServiceConnection; + } + + long getServiceTimeoutInSeconds() { + return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA, + "server_based_service_timeout_in_seconds", + DEFAULT_SERVICE_TIMEOUT_IN_SECONDS); + } + + long getServerBlobLifetimeInMillis() { + return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA, + "server_based_server_blob_lifetime_in_millis", + DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS); + } + } + + RebootEscrowProviderServerBasedImpl(Context context, LockSettingsStorage storage) { + this(storage, new Injector(context)); + } + + @VisibleForTesting + RebootEscrowProviderServerBasedImpl(LockSettingsStorage storage, Injector injector) { + mStorage = storage; + mInjector = injector; + } + + @Override + public boolean hasRebootEscrowSupport() { + return mInjector.getServiceConnection() != null; + } + + private byte[] unwrapServerBlob(byte[] serverBlob, SecretKey decryptionKey) throws + TimeoutException, RemoteException, IOException { + ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection(); + if (serviceConnection == null) { + Slog.w(TAG, "Had reboot escrow data for users, but resume on reboot server" + + " service is unavailable"); + return null; + } + + // Decrypt with k_k from the key store first. + byte[] decryptedBlob = AesEncryptionUtil.decrypt(decryptionKey, serverBlob); + if (decryptedBlob == null) { + Slog.w(TAG, "Decrypted server blob should not be null"); + return null; + } + + // Ask the server connection service to decrypt the inner layer, to get the reboot + // escrow key (k_s). + serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds()); + byte[] escrowKeyBytes = serviceConnection.unwrap(decryptedBlob, + mInjector.getServiceTimeoutInSeconds()); + serviceConnection.unbindService(); + + return escrowKeyBytes; + } + + @Override + public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) { + byte[] serverBlob = mStorage.readRebootEscrowServerBlob(); + // Delete the server blob in storage. + mStorage.removeRebootEscrowServerBlob(); + if (serverBlob == null) { + Slog.w(TAG, "Failed to read reboot escrow server blob from storage"); + return null; + } + + try { + byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey); + if (escrowKeyBytes == null) { + Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null"); + return null; + } else if (escrowKeyBytes.length != 32) { + Slog.e(TAG, "Decrypted reboot escrow key has incorrect size " + + escrowKeyBytes.length); + return null; + } + + return RebootEscrowKey.fromKeyBytes(escrowKeyBytes); + } catch (TimeoutException | RemoteException | IOException e) { + Slog.w(TAG, "Failed to decrypt the server blob ", e); + return null; + } + } + + @Override + public void clearRebootEscrowKey() { + mStorage.removeRebootEscrowServerBlob(); + } + + private byte[] wrapEscrowKey(byte[] escrowKeyBytes, SecretKey encryptionKey) throws + TimeoutException, RemoteException, IOException { + ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection(); + if (serviceConnection == null) { + Slog.w(TAG, "Failed to encrypt the reboot escrow key: resume on reboot server" + + " service is unavailable"); + return null; + } + + serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds()); + // Ask the server connection service to encrypt the reboot escrow key. + byte[] serverEncryptedBlob = serviceConnection.wrapBlob(escrowKeyBytes, + mInjector.getServerBlobLifetimeInMillis(), mInjector.getServiceTimeoutInSeconds()); + serviceConnection.unbindService(); + + if (serverEncryptedBlob == null) { + Slog.w(TAG, "Server encrypted reboot escrow key cannot be null"); + return null; + } + + // Additionally wrap the server blob with a local key. + return AesEncryptionUtil.encrypt(encryptionKey, serverEncryptedBlob); + } + + @Override + public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) { + mStorage.removeRebootEscrowServerBlob(); + try { + byte[] wrappedBlob = wrapEscrowKey(escrowKey.getKeyBytes(), encryptionKey); + if (wrappedBlob == null) { + Slog.w(TAG, "Failed to encrypt the reboot escrow key"); + return false; + } + mStorage.writeRebootEscrowServerBlob(wrappedBlob); + + Slog.i(TAG, "Reboot escrow key encrypted and stored."); + return true; + } catch (TimeoutException | RemoteException | IOException e) { + Slog.w(TAG, "Failed to encrypt the reboot escrow key ", e); + } + + return false; + } +} diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java new file mode 100644 index 000000000000..a1e18bd5a6bd --- /dev/null +++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java @@ -0,0 +1,245 @@ +/* + * 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.locksettings; + +import android.Manifest; +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.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelableException; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.service.resumeonreboot.IResumeOnRebootService; +import android.service.resumeonreboot.ResumeOnRebootService; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** @hide */ +public class ResumeOnRebootServiceProvider { + + private static final String PROVIDER_PACKAGE = DeviceConfig.getString( + DeviceConfig.NAMESPACE_OTA, "resume_on_reboot_service_package", ""); + private static final String PROVIDER_REQUIRED_PERMISSION = + Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE; + private static final String TAG = "ResumeOnRebootServiceProvider"; + + private final Context mContext; + private final PackageManager mPackageManager; + + public ResumeOnRebootServiceProvider(Context context) { + this(context, context.getPackageManager()); + } + + @VisibleForTesting + public ResumeOnRebootServiceProvider(Context context, PackageManager packageManager) { + this.mContext = context; + this.mPackageManager = packageManager; + } + + @Nullable + private ServiceInfo resolveService() { + Intent intent = new Intent(); + intent.setAction(ResumeOnRebootService.SERVICE_INTERFACE); + if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) { + intent.setPackage(PROVIDER_PACKAGE); + } + + List<ResolveInfo> resolvedIntents = + mPackageManager.queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY); + for (ResolveInfo resolvedInfo : resolvedIntents) { + if (resolvedInfo.serviceInfo != null + && PROVIDER_REQUIRED_PERMISSION.equals(resolvedInfo.serviceInfo.permission)) { + return resolvedInfo.serviceInfo; + } + } + return null; + } + + /** Creates a new {@link ResumeOnRebootServiceConnection} */ + @Nullable + public ResumeOnRebootServiceConnection getServiceConnection() { + ServiceInfo serviceInfo = resolveService(); + if (serviceInfo == null) { + return null; + } + return new ResumeOnRebootServiceConnection(mContext, serviceInfo.getComponentName()); + } + + /** + * Connection class used for contacting the registered {@link IResumeOnRebootService} + */ + public static class ResumeOnRebootServiceConnection { + + private static final String TAG = "ResumeOnRebootServiceConnection"; + private final Context mContext; + private final ComponentName mComponentName; + private IResumeOnRebootService mBinder; + @Nullable + ServiceConnection mServiceConnection; + + private ResumeOnRebootServiceConnection(Context context, + @NonNull ComponentName componentName) { + mContext = context; + mComponentName = componentName; + } + + /** Unbind from the service */ + public void unbindService() { + if (mServiceConnection != null) { + mContext.unbindService(mServiceConnection); + } + } + + /** Bind to the service */ + public void bindToService(long timeOut) throws TimeoutException { + if (mBinder == null || !mBinder.asBinder().isBinderAlive()) { + CountDownLatch connectionLatch = new CountDownLatch(1); + Intent intent = new Intent(); + intent.setComponent(mComponentName); + mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mBinder = IResumeOnRebootService.Stub.asInterface(service); + connectionLatch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mBinder = null; + } + }; + final boolean success = mContext.bindServiceAsUser(intent, mServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + BackgroundThread.getHandler(), UserHandle.SYSTEM); + + if (!success) { + Slog.e(TAG, "Binding: " + mComponentName + " u" + UserHandle.SYSTEM + + " failed."); + return; + } + waitForLatch(connectionLatch, "serviceConnection", timeOut); + } + } + + /** Wrap opaque blob */ + public byte[] wrapBlob(byte[] unwrappedBlob, long lifeTimeInMillis, + long timeOutInMillis) + throws RemoteException, TimeoutException, IOException { + if (mBinder == null || !mBinder.asBinder().isBinderAlive()) { + throw new RemoteException("Service not bound"); + } + CountDownLatch binderLatch = new CountDownLatch(1); + ResumeOnRebootServiceCallback + resultCallback = + new ResumeOnRebootServiceCallback( + binderLatch); + mBinder.wrapSecret(unwrappedBlob, lifeTimeInMillis, new RemoteCallback(resultCallback)); + waitForLatch(binderLatch, "wrapSecret", timeOutInMillis); + if (resultCallback.getResult().containsKey(ResumeOnRebootService.EXCEPTION_KEY)) { + throwTypedException(resultCallback.getResult().getParcelable( + ResumeOnRebootService.EXCEPTION_KEY)); + } + return resultCallback.mResult.getByteArray(ResumeOnRebootService.WRAPPED_BLOB_KEY); + } + + /** Unwrap wrapped blob */ + public byte[] unwrap(byte[] wrappedBlob, long timeOut) + throws RemoteException, TimeoutException, IOException { + if (mBinder == null || !mBinder.asBinder().isBinderAlive()) { + throw new RemoteException("Service not bound"); + } + CountDownLatch binderLatch = new CountDownLatch(1); + ResumeOnRebootServiceCallback + resultCallback = + new ResumeOnRebootServiceCallback( + binderLatch); + mBinder.unwrap(wrappedBlob, new RemoteCallback(resultCallback)); + waitForLatch(binderLatch, "unWrapSecret", timeOut); + if (resultCallback.getResult().containsKey(ResumeOnRebootService.EXCEPTION_KEY)) { + throwTypedException(resultCallback.getResult().getParcelable( + ResumeOnRebootService.EXCEPTION_KEY)); + } + return resultCallback.getResult().getByteArray( + ResumeOnRebootService.UNWRAPPED_BLOB_KEY); + } + + private void throwTypedException( + ParcelableException exception) + throws IOException { + if (exception.getCause() instanceof IOException) { + exception.maybeRethrow(IOException.class); + } else if (exception.getCause() instanceof IllegalStateException) { + exception.maybeRethrow(IllegalStateException.class); + } else { + // This should not happen. Wrap the cause in IllegalStateException so that it + // doesn't disrupt the exception handling + throw new IllegalStateException(exception.getCause()); + } + } + + private void waitForLatch(CountDownLatch latch, String reason, long timeOut) + throws TimeoutException { + try { + if (!latch.await(timeOut, TimeUnit.SECONDS)) { + throw new TimeoutException("Latch wait for " + reason + " elapsed"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Latch wait for " + reason + " interrupted"); + } + } + } + + private static class ResumeOnRebootServiceCallback implements + RemoteCallback.OnResultListener { + + private final CountDownLatch mResultLatch; + private Bundle mResult; + + private ResumeOnRebootServiceCallback(CountDownLatch resultLatch) { + this.mResultLatch = resultLatch; + } + + @Override + public void onResult(@Nullable Bundle result) { + this.mResult = result; + mResultLatch.countDown(); + } + + private Bundle getResult() { + return mResult; + } + } +} diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index f882c57e49ba..edc9d7c64146 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -77,7 +77,7 @@ abstract class MediaRoute2Provider { @NonNull public List<RoutingSessionInfo> getSessionInfos() { synchronized (mLock) { - return mSessionInfos; + return new ArrayList<>(mSessionInfos); } } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java index 85af346aa88a..ab38dca2387d 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java @@ -108,8 +108,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider mLastDiscoveryPreference = discoveryPreference; if (mConnectionReady) { mActiveConnection.updateDiscoveryPreference(discoveryPreference); - updateBinding(); } + updateBinding(); } @Override @@ -205,9 +205,11 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider } private boolean shouldBind() { - //TODO: Binding could be delayed until it's necessary. if (mRunning) { - return true; + // Bind when there is a discovery preference or an active route session. + return (mLastDiscoveryPreference != null + && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty()) + || !getSessionInfos().isEmpty(); } return false; } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 1114fe0d9bf8..31edf43679e9 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -16,6 +16,7 @@ package com.android.server.media; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; import static android.media.MediaRouter2Utils.getOriginalId; import static android.media.MediaRouter2Utils.getProviderId; @@ -73,10 +74,12 @@ class MediaRouter2ServiceImpl { // TODO: (In Android S or later) if we add callback methods for generic failures // in MediaRouter2, remove this constant and replace the usages with the real request IDs. private static final long DUMMY_REQUEST_ID = -1; + private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND; private final Context mContext; private final Object mLock = new Object(); final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1); + final ActivityManager mActivityManager; @GuardedBy("mLock") private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); @@ -87,8 +90,21 @@ class MediaRouter2ServiceImpl { @GuardedBy("mLock") private int mCurrentUserId = -1; + private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = + (uid, importance) -> { + synchronized (mLock) { + final int count = mUserRecords.size(); + for (int i = 0; i < count; i++) { + mUserRecords.valueAt(i).mHandler.maybeUpdateDiscoveryPreferenceForUid(uid); + } + } + }; + MediaRouter2ServiceImpl(Context context) { mContext = context; + mActivityManager = mContext.getSystemService(ActivityManager.class); + mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener, + PACKAGE_IMPORTANCE_FOR_DISCOVERY); } //////////////////////////////////////////////////////////////// @@ -388,6 +404,30 @@ class MediaRouter2ServiceImpl { } } + public void startScan(IMediaRouter2Manager manager) { + Objects.requireNonNull(manager, "manager must not be null"); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + startScanLocked(manager); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void stopScan(IMediaRouter2Manager manager) { + Objects.requireNonNull(manager, "manager must not be null"); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + stopScanLocked(manager); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + public void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, MediaRoute2Info route, int volume) { Objects.requireNonNull(manager, "manager must not be null"); @@ -839,6 +879,24 @@ class MediaRouter2ServiceImpl { disposeUserIfNeededLocked(userRecord); // since manager removed from user } + private void startScanLocked(@NonNull IMediaRouter2Manager manager) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + if (managerRecord == null) { + return; + } + managerRecord.startScan(); + } + + private void stopScanLocked(@NonNull IMediaRouter2Manager manager) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + if (managerRecord == null) { + return; + } + managerRecord.stopScan(); + } + private void setRouteVolumeWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager, @NonNull MediaRoute2Info route, int volume) { @@ -1122,6 +1180,7 @@ class MediaRouter2ServiceImpl { public final String mPackageName; public final int mManagerId; public SessionCreationRequest mLastSessionCreationRequest; + public boolean mIsScanning; ManagerRecord(UserRecord userRecord, IMediaRouter2Manager manager, int uid, int pid, String packageName) { @@ -1146,6 +1205,24 @@ class MediaRouter2ServiceImpl { pw.println(prefix + this); } + public void startScan() { + if (mIsScanning) { + return; + } + mIsScanning = true; + mUserRecord.mHandler.sendMessage(PooledLambda.obtainMessage( + UserHandler::updateDiscoveryPreferenceOnHandler, mUserRecord.mHandler)); + } + + public void stopScan() { + if (!mIsScanning) { + return; + } + mIsScanning = false; + mUserRecord.mHandler.sendMessage(PooledLambda.obtainMessage( + UserHandler::updateDiscoveryPreferenceOnHandler, mUserRecord.mHandler)); + } + @Override public String toString() { return "Manager " + mPackageName + " (pid " + mPid + ")"; @@ -1262,6 +1339,24 @@ class MediaRouter2ServiceImpl { return null; } + public void maybeUpdateDiscoveryPreferenceForUid(int uid) { + MediaRouter2ServiceImpl service = mServiceRef.get(); + if (service == null) { + return; + } + boolean isUidRelevant; + synchronized (service.mLock) { + isUidRelevant = mUserRecord.mRouterRecords.stream().anyMatch( + router -> router.mUid == uid) + | mUserRecord.mManagerRecords.stream().anyMatch( + manager -> manager.mUid == uid); + } + if (isUidRelevant) { + sendMessage(PooledLambda.obtainMessage( + UserHandler::updateDiscoveryPreferenceOnHandler, this)); + } + } + private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) { int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId()); MediaRoute2ProviderInfo currentInfo = provider.getProviderInfo(); @@ -1767,6 +1862,16 @@ class MediaRouter2ServiceImpl { return managers; } + private List<RouterRecord> getRouterRecords() { + MediaRouter2ServiceImpl service = mServiceRef.get(); + if (service == null) { + return Collections.emptyList(); + } + synchronized (service.mLock) { + return new ArrayList<>(mUserRecord.mRouterRecords); + } + } + private List<ManagerRecord> getManagerRecords() { MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { @@ -2001,13 +2106,28 @@ class MediaRouter2ServiceImpl { return; } List<RouteDiscoveryPreference> discoveryPreferences = new ArrayList<>(); - synchronized (service.mLock) { - for (RouterRecord routerRecord : mUserRecord.mRouterRecords) { + List<RouterRecord> routerRecords = getRouterRecords(); + List<ManagerRecord> managerRecords = getManagerRecords(); + boolean isAnyManagerScanning = + managerRecords.stream().anyMatch(manager -> manager.mIsScanning + && service.mActivityManager.getPackageImportance(manager.mPackageName) + <= PACKAGE_IMPORTANCE_FOR_DISCOVERY); + + for (RouterRecord routerRecord : routerRecords) { + if (isAnyManagerScanning + || service.mActivityManager.getPackageImportance(routerRecord.mPackageName) + <= PACKAGE_IMPORTANCE_FOR_DISCOVERY) { discoveryPreferences.add(routerRecord.mDiscoveryPreference); } - mUserRecord.mCompositeDiscoveryPreference = - new RouteDiscoveryPreference.Builder(discoveryPreferences) - .build(); + } + + synchronized (service.mLock) { + RouteDiscoveryPreference newPreference = + new RouteDiscoveryPreference.Builder(discoveryPreferences).build(); + if (newPreference.equals(mUserRecord.mCompositeDiscoveryPreference)) { + return; + } + mUserRecord.mCompositeDiscoveryPreference = newPreference; } for (MediaRoute2Provider provider : mRouteProviders) { provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 0e52a67c8d39..b6d6cc48d0cd 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -544,6 +544,18 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override + public void startScan(IMediaRouter2Manager manager) { + mService2.startScan(manager); + } + + // Binder call + @Override + public void stopScan(IMediaRouter2Manager manager) { + mService2.stopScan(manager); + } + + // Binder call + @Override public void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId, MediaRoute2Info route, int volume) { mService2.setRouteVolumeWithManager(manager, requestId, route, volume); diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index ea1d8da74185..ea2788c0c3d8 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -34,7 +34,6 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; import android.os.Handler; -import android.security.Credentials; import android.security.KeyStore; import android.text.TextUtils; import android.util.Log; @@ -70,6 +69,7 @@ public class LockdownVpnTracker { @NonNull private final Handler mHandler; @NonNull private final Vpn mVpn; @NonNull private final VpnProfile mProfile; + @NonNull private final KeyStore mKeyStore; @NonNull private final Object mStateLock = new Object(); @@ -81,13 +81,10 @@ public class LockdownVpnTracker { private int mErrorCount; - public static boolean isEnabled() { - return KeyStore.getInstance().contains(Credentials.LOCKDOWN_VPN); - } - public LockdownVpnTracker(@NonNull Context context, @NonNull ConnectivityService connService, @NonNull Handler handler, + @NonNull KeyStore keyStore, @NonNull Vpn vpn, @NonNull VpnProfile profile) { mContext = Objects.requireNonNull(context); @@ -95,6 +92,7 @@ public class LockdownVpnTracker { mHandler = Objects.requireNonNull(handler); mVpn = Objects.requireNonNull(vpn); mProfile = Objects.requireNonNull(profile); + mKeyStore = Objects.requireNonNull(keyStore); mNotificationManager = mContext.getSystemService(NotificationManager.class); final Intent configIntent = new Intent(ACTION_VPN_SETTINGS); @@ -157,7 +155,7 @@ public class LockdownVpnTracker { try { // Use the privileged method because Lockdown VPN is initiated by the system, so // no additional permission checks are necessary. - mVpn.startLegacyVpnPrivileged(mProfile, KeyStore.getInstance(), egressProp); + mVpn.startLegacyVpnPrivileged(mProfile, mKeyStore, egressProp); } catch (IllegalStateException e) { mAcceptedEgressIface = null; Log.e(TAG, "Failed to start VPN", e); diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 5bd352c8f8e8..676f4218f745 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -78,6 +78,7 @@ public class NetworkPolicyLogger { static final int NTWK_BLOCKED_BG_RESTRICT = 5; static final int NTWK_ALLOWED_DEFAULT = 6; static final int NTWK_ALLOWED_SYSTEM = 7; + static final int NTWK_BLOCKED_RESTRICTED_MODE = 8; private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE); private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE); @@ -281,6 +282,8 @@ public class NetworkPolicyLogger { return "blocked when background is restricted"; case NTWK_ALLOWED_DEFAULT: return "allowed by default"; + case NTWK_BLOCKED_RESTRICTED_MODE: + return "blocked by restricted networking mode"; default: return String.valueOf(reason); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 407cedf38917..f92f3dcd77ef 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -39,17 +39,6 @@ public abstract class NetworkPolicyManagerInternal { public abstract void resetUserState(int userId); /** - * @return true if the given uid is restricted from doing networking on metered networks. - */ - public abstract boolean isUidRestrictedOnMeteredNetworks(int uid); - - /** - * @return true if networking is blocked on the given interface for the given uid according - * to current networking policies. - */ - public abstract boolean isUidNetworkingBlocked(int uid, String ifname); - - /** * Figure out if networking is blocked for a given set of conditions. * * This is used by ConnectivityService via passing stale copies of conditions, so it must not diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index bd80befe84be..01d4faf5c594 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -18,6 +18,7 @@ package com.android.server.net; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; +import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS; import static android.Manifest.permission.NETWORK_SETTINGS; @@ -44,6 +45,7 @@ import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELI import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED; import static android.net.INetd.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; @@ -57,6 +59,7 @@ import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS; +import static android.net.NetworkPolicyManager.MASK_RESTRICTED_MODE_NETWORKS; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; @@ -65,12 +68,14 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE; import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; import static android.net.NetworkPolicyManager.resolveNetworkId; import static android.net.NetworkPolicyManager.uidPoliciesToString; import static android.net.NetworkPolicyManager.uidRulesToString; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateMobileAll; @@ -111,6 +116,7 @@ import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_ALLOWL import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT; import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_DENYLIST; import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER; +import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_RESTRICTED_MODE; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -143,6 +149,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.Resources; +import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.IConnectivityManager; @@ -270,6 +277,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.IntConsumer; /** * Service that maintains low-level network policy rules, using @@ -444,7 +452,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower; @GuardedBy("mUidRulesFirstLock") volatile boolean mDeviceIdleMode; // Store whether user flipped restrict background in battery saver mode - @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackgroundChangedInBsm; + @GuardedBy("mUidRulesFirstLock") + volatile boolean mRestrictBackgroundChangedInBsm; + @GuardedBy("mUidRulesFirstLock") + volatile boolean mRestrictedNetworkingMode; private final boolean mSuppressDefaultPolicy; @@ -478,6 +489,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseIntArray mUidFirewallDozableRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray(); + @GuardedBy("mUidRulesFirstLock") + final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray(); /** Set of states for the child firewall chains. True if the chain is active. */ @GuardedBy("mUidRulesFirstLock") @@ -597,6 +610,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray(); + private RestrictedModeObserver mRestrictedModeObserver; + // TODO: keep allowlist of system-critical services that should never have // rules enforced, such as system, phone, and radio UIDs. @@ -610,7 +625,35 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { int COUNT = IS_UID_NETWORKING_BLOCKED + 1; } - public final StatLogger mStatLogger = new StatLogger(new String[] { + private static class RestrictedModeObserver extends ContentObserver { + private final Context mContext; + private final RestrictedModeListener mListener; + + RestrictedModeObserver(Context ctx, RestrictedModeListener listener) { + super(null); + mContext = ctx; + mListener = listener; + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.RESTRICTED_NETWORKING_MODE), false, + this); + } + + public boolean isRestrictedModeEnabled() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.RESTRICTED_NETWORKING_MODE, 0) != 0; + } + + @Override + public void onChange(boolean selfChange) { + mListener.onChange(isRestrictedModeEnabled()); + } + + public interface RestrictedModeListener { + void onChange(boolean enabled); + } + } + + public final StatLogger mStatLogger = new StatLogger(new String[]{ "updateNetworkEnabledNL()", "isUidNetworkingBlocked()", }); @@ -785,6 +828,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mRestrictPower = mPowerManagerInternal.getLowPowerState( ServiceType.NETWORK_FIREWALL).batterySaverEnabled; + mRestrictedModeObserver = new RestrictedModeObserver(mContext, + enabled -> { + synchronized (mUidRulesFirstLock) { + mRestrictedNetworkingMode = enabled; + updateRestrictedModeAllowlistUL(); + } + }); + mRestrictedNetworkingMode = mRestrictedModeObserver.isRestrictedModeEnabled(); + mSystemReady = true; waitForAdminData(); @@ -3500,6 +3552,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("Restrict background: "); fout.println(mRestrictBackground); fout.print("Restrict power: "); fout.println(mRestrictPower); fout.print("Device idle: "); fout.println(mDeviceIdleMode); + fout.print("Restricted networking mode: "); fout.println(mRestrictedNetworkingMode); synchronized (mMeteredIfacesLock) { fout.print("Metered ifaces: "); fout.println(mMeteredIfaces); @@ -3811,6 +3864,100 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + @VisibleForTesting + boolean isRestrictedModeEnabled() { + synchronized (mUidRulesFirstLock) { + return mRestrictedNetworkingMode; + } + } + + /** + * updates restricted mode state / access for all apps + * Called on initialization and when restricted mode is enabled / disabled. + */ + @VisibleForTesting + @GuardedBy("mUidRulesFirstLock") + void updateRestrictedModeAllowlistUL() { + mUidFirewallRestrictedModeRules.clear(); + forEachUid("updateRestrictedModeAllowlist", uid -> { + final int oldUidRule = mUidRules.get(uid); + final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule); + final boolean hasUidRuleChanged = oldUidRule != newUidRule; + final int newFirewallRule = getRestrictedModeFirewallRule(newUidRule); + + // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add + // non-default rules. + if (newFirewallRule != FIREWALL_RULE_DEFAULT) { + mUidFirewallRestrictedModeRules.append(uid, newFirewallRule); + } + + if (hasUidRuleChanged) { + mUidRules.put(uid, newUidRule); + mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget(); + } + }); + if (mRestrictedNetworkingMode) { + // firewall rules only need to be set when this mode is being enabled. + setUidFirewallRulesUL(FIREWALL_CHAIN_RESTRICTED, mUidFirewallRestrictedModeRules); + } + enableFirewallChainUL(FIREWALL_CHAIN_RESTRICTED, mRestrictedNetworkingMode); + } + + // updates restricted mode state / access for a single app / uid. + @VisibleForTesting + @GuardedBy("mUidRulesFirstLock") + void updateRestrictedModeForUidUL(int uid) { + final int oldUidRule = mUidRules.get(uid); + final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule); + final boolean hasUidRuleChanged = oldUidRule != newUidRule; + + if (hasUidRuleChanged) { + mUidRules.put(uid, newUidRule); + mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget(); + } + + // if restricted networking mode is on, and the app has an access exemption, the uid rule + // will not change, but the firewall rule will have to be updated. + if (mRestrictedNetworkingMode) { + // Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules. + // In this case, default firewall rules can also be added. + setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid, + getRestrictedModeFirewallRule(newUidRule)); + } + } + + private int getNewRestrictedModeUidRule(int uid, int oldUidRule) { + int newRule = oldUidRule; + newRule &= ~MASK_RESTRICTED_MODE_NETWORKS; + if (mRestrictedNetworkingMode && !hasRestrictedModeAccess(uid)) { + newRule |= RULE_REJECT_RESTRICTED_MODE; + } + return newRule; + } + + private static int getRestrictedModeFirewallRule(int uidRule) { + if ((uidRule & RULE_REJECT_RESTRICTED_MODE) != 0) { + // rejected in restricted mode, this is the default behavior. + return FIREWALL_RULE_DEFAULT; + } else { + return FIREWALL_RULE_ALLOW; + } + } + + private boolean hasRestrictedModeAccess(int uid) { + try { + // TODO: this needs to be kept in sync with + // PermissionMonitor#hasRestrictedNetworkPermission + return mIPm.checkUidPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid) + == PERMISSION_GRANTED + || mIPm.checkUidPermission(NETWORK_STACK, uid) == PERMISSION_GRANTED + || mIPm.checkUidPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid) + == PERMISSION_GRANTED; + } catch (RemoteException e) { + return false; + } + } + @GuardedBy("mUidRulesFirstLock") void updateRulesForPowerSaveUL() { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL"); @@ -4032,6 +4179,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForAppIdleUL(); updateRulesForRestrictPowerUL(); updateRulesForRestrictBackgroundUL(); + updateRestrictedModeAllowlistUL(); // If the set of restricted networks may have changed, re-evaluate those. if (restrictedNetworksChanged) { @@ -4050,7 +4198,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { updateRulesForDeviceIdleUL(); updateRulesForPowerSaveUL(); - updateRulesForAllAppsUL(TYPE_RESTRICT_POWER); + forEachUid("updateRulesForRestrictPower", + uid -> updateRulesForPowerRestrictionsUL(uid)); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } @@ -4060,31 +4209,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void updateRulesForRestrictBackgroundUL() { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictBackgroundUL"); try { - updateRulesForAllAppsUL(TYPE_RESTRICT_BACKGROUND); + forEachUid("updateRulesForRestrictBackground", + uid -> updateRulesForDataUsageRestrictionsUL(uid)); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } - private static final int TYPE_RESTRICT_BACKGROUND = 1; - private static final int TYPE_RESTRICT_POWER = 2; - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = false, value = { - TYPE_RESTRICT_BACKGROUND, - TYPE_RESTRICT_POWER, - }) - public @interface RestrictType { - } - - // TODO: refactor / consolidate all those updateXyz methods, there are way too many of them... - @GuardedBy("mUidRulesFirstLock") - private void updateRulesForAllAppsUL(@RestrictType int type) { + private void forEachUid(String tag, IntConsumer consumer) { if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { - Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL-" + type); + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "forEachUid-" + tag); } try { // update rules for all installed applications - final PackageManager pm = mContext.getPackageManager(); final List<UserInfo> users; final List<ApplicationInfo> apps; @@ -4112,16 +4249,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (int j = 0; j < appsSize; j++) { final ApplicationInfo app = apps.get(j); final int uid = UserHandle.getUid(user.id, app.uid); - switch (type) { - case TYPE_RESTRICT_BACKGROUND: - updateRulesForDataUsageRestrictionsUL(uid); - break; - case TYPE_RESTRICT_POWER: - updateRulesForPowerRestrictionsUL(uid); - break; - default: - Slog.w(TAG, "Invalid type for updateRulesForAllApps: " + type); - } + consumer.accept(uid); } } } finally { @@ -4268,6 +4396,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mPowerSaveWhitelistAppIds.delete(uid); mPowerSaveTempWhitelistAppIds.delete(uid); mAppIdleTempWhitelistAppIds.delete(uid); + mUidFirewallRestrictedModeRules.delete(uid); // ...then update iptables asynchronously. mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget(); @@ -4293,6 +4422,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForAppIdleUL(uid); updateRuleForRestrictPowerUL(uid); + // If the uid has the necessary permissions, then it should be added to the restricted mode + // firewall allowlist. + updateRestrictedModeForUidUL(uid); + // Update internal state for power-related modes. updateRulesForPowerRestrictionsUL(uid); @@ -4365,26 +4498,26 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0; - final int oldRule = oldUidRules & MASK_METERED_NETWORKS; - int newRule = RULE_NONE; + + // copy oldUidRules and clear out METERED_NETWORKS rules. + int newUidRules = oldUidRules & (~MASK_METERED_NETWORKS); // First step: define the new rule based on user restrictions and foreground state. if (isRestrictedByAdmin) { - newRule = RULE_REJECT_METERED; + newUidRules |= RULE_REJECT_METERED; } else if (isForeground) { if (isDenied || (mRestrictBackground && !isAllowed)) { - newRule = RULE_TEMPORARY_ALLOW_METERED; + newUidRules |= RULE_TEMPORARY_ALLOW_METERED; } else if (isAllowed) { - newRule = RULE_ALLOW_METERED; + newUidRules |= RULE_ALLOW_METERED; } } else { if (isDenied) { - newRule = RULE_REJECT_METERED; + newUidRules |= RULE_REJECT_METERED; } else if (mRestrictBackground && isAllowed) { - newRule = RULE_ALLOW_METERED; + newUidRules |= RULE_ALLOW_METERED; } } - final int newUidRules = newRule | (oldUidRules & MASK_ALL_NETWORKS); if (LOGV) { Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")" @@ -4392,8 +4525,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", isDenied=" + isDenied + ", isAllowed=" + isAllowed + ", isRestrictedByAdmin=" + isRestrictedByAdmin - + ", oldRule=" + uidRulesToString(oldRule) - + ", newRule=" + uidRulesToString(newRule) + + ", oldRule=" + uidRulesToString(oldUidRules & MASK_METERED_NETWORKS) + + ", newRule=" + uidRulesToString(newUidRules & MASK_METERED_NETWORKS) + ", newUidRules=" + uidRulesToString(newUidRules) + ", oldUidRules=" + uidRulesToString(oldUidRules)); } @@ -4405,8 +4538,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } // Second step: apply bw changes based on change of state. - if (newRule != oldRule) { - if (hasRule(newRule, RULE_TEMPORARY_ALLOW_METERED)) { + if (newUidRules != oldUidRules) { + if (hasRule(newUidRules, RULE_TEMPORARY_ALLOW_METERED)) { // Temporarily allow foreground app, removing from denylist if necessary // (since bw_penalty_box prevails over bw_happy_box). @@ -4417,7 +4550,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (isDenied) { setMeteredNetworkDenylist(uid, false); } - } else if (hasRule(oldRule, RULE_TEMPORARY_ALLOW_METERED)) { + } else if (hasRule(oldUidRules, RULE_TEMPORARY_ALLOW_METERED)) { // Remove temporary exemption from app that is not on foreground anymore. // TODO: if statements below are used to avoid unnecessary calls to netd / iptables, @@ -4430,18 +4563,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (isDenied || isRestrictedByAdmin) { setMeteredNetworkDenylist(uid, true); } - } else if (hasRule(newRule, RULE_REJECT_METERED) - || hasRule(oldRule, RULE_REJECT_METERED)) { + } else if (hasRule(newUidRules, RULE_REJECT_METERED) + || hasRule(oldUidRules, RULE_REJECT_METERED)) { // Flip state because app was explicitly added or removed to denylist. setMeteredNetworkDenylist(uid, (isDenied || isRestrictedByAdmin)); - if (hasRule(oldRule, RULE_REJECT_METERED) && isAllowed) { + if (hasRule(oldUidRules, RULE_REJECT_METERED) && isAllowed) { // Since denial prevails over allowance, we need to handle the special case // where app is allowed and denied at the same time (although such // scenario should be blocked by the UI), then it is removed from the denylist. setMeteredNetworkAllowlist(uid, isAllowed); } - } else if (hasRule(newRule, RULE_ALLOW_METERED) - || hasRule(oldRule, RULE_ALLOW_METERED)) { + } else if (hasRule(newUidRules, RULE_ALLOW_METERED) + || hasRule(oldUidRules, RULE_ALLOW_METERED)) { // Flip state because app was explicitly added or removed to allowlist. setMeteredNetworkAllowlist(uid, isAllowed); } else { @@ -4527,8 +4660,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode); - final int oldRule = oldUidRules & MASK_ALL_NETWORKS; - int newRule = RULE_NONE; + + // Copy existing uid rules and clear ALL_NETWORK rules. + int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS); // First step: define the new rule based on user restrictions and foreground state. @@ -4536,14 +4670,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // by considering the foreground and non-foreground states. if (isForeground) { if (restrictMode) { - newRule = RULE_ALLOW_ALL; + newUidRules |= RULE_ALLOW_ALL; } } else if (restrictMode) { - newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; + newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; } - final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule; - if (LOGV) { Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")" + ", isIdle: " + isUidIdle @@ -4551,17 +4683,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", mDeviceIdleMode: " + mDeviceIdleMode + ", isForeground=" + isForeground + ", isWhitelisted=" + isWhitelisted - + ", oldRule=" + uidRulesToString(oldRule) - + ", newRule=" + uidRulesToString(newRule) + + ", oldRule=" + uidRulesToString(oldUidRules & MASK_ALL_NETWORKS) + + ", newRule=" + uidRulesToString(newUidRules & MASK_ALL_NETWORKS) + ", newUidRules=" + uidRulesToString(newUidRules) + ", oldUidRules=" + uidRulesToString(oldUidRules)); } // Second step: notify listeners if state changed. - if (newRule != oldRule) { - if (newRule == RULE_NONE || hasRule(newRule, RULE_ALLOW_ALL)) { + if (newUidRules != oldUidRules) { + if ((newUidRules & MASK_ALL_NETWORKS) == RULE_NONE || hasRule(newUidRules, + RULE_ALLOW_ALL)) { if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid); - } else if (hasRule(newRule, RULE_REJECT_ALL)) { + } else if (hasRule(newUidRules, RULE_REJECT_ALL)) { if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid); } else { // All scenarios should have been covered above @@ -5018,6 +5151,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallStandbyRules.put(uid, rule); } else if (chain == FIREWALL_CHAIN_POWERSAVE) { mUidFirewallPowerSaveRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_RESTRICTED) { + mUidFirewallRestrictedModeRules.put(uid, rule); } try { @@ -5063,6 +5198,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); mNetworkManager .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT); + mNetworkManager + .setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, FIREWALL_RULE_DEFAULT); mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); } catch (IllegalStateException e) { @@ -5239,6 +5376,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return ret; } + @Override + public boolean isUidRestrictedOnMeteredNetworks(int uid) { + mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); + final int uidRules; + final boolean isBackgroundRestricted; + synchronized (mUidRulesFirstLock) { + uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); + isBackgroundRestricted = mRestrictBackground; + } + //TODO(b/177490332): The logic here might not be correct because it doesn't consider + // RULE_REJECT_METERED condition. And it could be replaced by + // isUidNetworkingBlockedInternal(). + return isBackgroundRestricted + && !hasRule(uidRules, RULE_ALLOW_METERED) + && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); + } + private static boolean isSystem(int uid) { return uid < Process.FIRST_APPLICATION_UID; } @@ -5249,26 +5403,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Networks are never blocked for system components if (isSystem(uid)) { reason = NTWK_ALLOWED_SYSTEM; - } - else if (hasRule(uidRules, RULE_REJECT_ALL)) { + } else if (hasRule(uidRules, RULE_REJECT_RESTRICTED_MODE)) { + reason = NTWK_BLOCKED_RESTRICTED_MODE; + } else if (hasRule(uidRules, RULE_REJECT_ALL)) { reason = NTWK_BLOCKED_POWER; - } - else if (!isNetworkMetered) { + } else if (!isNetworkMetered) { reason = NTWK_ALLOWED_NON_METERED; - } - else if (hasRule(uidRules, RULE_REJECT_METERED)) { + } else if (hasRule(uidRules, RULE_REJECT_METERED)) { reason = NTWK_BLOCKED_DENYLIST; - } - else if (hasRule(uidRules, RULE_ALLOW_METERED)) { + } else if (hasRule(uidRules, RULE_ALLOW_METERED)) { reason = NTWK_ALLOWED_ALLOWLIST; - } - else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { + } else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { reason = NTWK_ALLOWED_TMP_ALLOWLIST; - } - else if (isBackgroundRestricted) { + } else if (isBackgroundRestricted) { reason = NTWK_BLOCKED_BG_RESTRICT; - } - else { + } else { reason = NTWK_ALLOWED_DEFAULT; } @@ -5281,6 +5430,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { case NTWK_ALLOWED_SYSTEM: blocked = false; break; + case NTWK_BLOCKED_RESTRICTED_MODE: case NTWK_BLOCKED_POWER: case NTWK_BLOCKED_DENYLIST: case NTWK_BLOCKED_BG_RESTRICT: @@ -5311,48 +5461,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - /** - * @return true if the given uid is restricted from doing networking on metered networks. - */ - @Override - public boolean isUidRestrictedOnMeteredNetworks(int uid) { - final int uidRules; - final boolean isBackgroundRestricted; - synchronized (mUidRulesFirstLock) { - uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); - isBackgroundRestricted = mRestrictBackground; - } - return isBackgroundRestricted - && !hasRule(uidRules, RULE_ALLOW_METERED) - && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); - } - - /** - * @return true if networking is blocked on the given interface for the given uid according - * to current networking policies. - */ - @Override - public boolean isUidNetworkingBlocked(int uid, String ifname) { - final long startTime = mStatLogger.getTime(); - - final int uidRules; - final boolean isBackgroundRestricted; - synchronized (mUidRulesFirstLock) { - uidRules = mUidRules.get(uid, RULE_NONE); - isBackgroundRestricted = mRestrictBackground; - } - final boolean isNetworkMetered; - synchronized (mMeteredIfacesLock) { - isNetworkMetered = mMeteredIfaces.contains(ifname); - } - final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered, - isBackgroundRestricted, mLogger); - - mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime); - - return ret; - } - @Override public void onTempPowerSaveWhitelistChange(int appId, boolean added) { synchronized (mUidRulesFirstLock) { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java index 7bcf3183bf69..47bb8f009920 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java @@ -119,6 +119,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { switch(type) { case "restrict-background": return getRestrictBackground(); + case "restricted-mode": + return getRestrictedModeState(); } pw.println("Error: unknown get type '" + type + "'"); return -1; @@ -255,6 +257,13 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return listUidList("App Idle whitelisted UIDs", uids); } + private int getRestrictedModeState() { + final PrintWriter pw = getOutPrintWriter(); + pw.print("Restricted mode status: "); + pw.println(mInterface.isRestrictedModeEnabled() ? "enabled" : "disabled"); + return 0; + } + private int getRestrictBackground() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); pw.print("Restrict background status: "); diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index e9868fde3059..d042b882fee1 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -27,6 +27,7 @@ import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import android.annotation.Nullable; import android.net.INetd; import android.net.NetworkStats; +import android.net.UnderlyingNetworkInfo; import android.net.util.NetdService; import android.os.RemoteException; import android.os.StrictMode; @@ -34,7 +35,6 @@ import android.os.SystemClock; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.VpnInfo; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ProcFileReader; @@ -81,7 +81,7 @@ public class NetworkStatsFactory { private final Object mPersistentDataLock = new Object(); /** Set containing info about active VPNs and their underlying networks. */ - private volatile VpnInfo[] mVpnInfos = new VpnInfo[0]; + private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0]; // A persistent snapshot of cumulative stats since device start @GuardedBy("mPersistentDataLock") @@ -116,8 +116,8 @@ public class NetworkStatsFactory { * * @param vpnArray The snapshot of the currently-running VPNs. */ - public void updateVpnInfos(VpnInfo[] vpnArray) { - mVpnInfos = vpnArray.clone(); + public void updateUnderlyingNetworkInfos(UnderlyingNetworkInfo[] vpnArray) { + mUnderlyingNetworkInfos = vpnArray.clone(); } /** @@ -319,7 +319,7 @@ public class NetworkStatsFactory { // code that will acquire other locks within the system server. See b/134244752. synchronized (mPersistentDataLock) { // Take a reference. If this gets swapped out, we still have the old reference. - final VpnInfo[] vpnArray = mVpnInfos; + final UnderlyingNetworkInfo[] vpnArray = mUnderlyingNetworkInfos; // Take a defensive copy. mPersistSnapshot is mutated in some cases below final NetworkStats prev = mPersistSnapshot.clone(); @@ -369,8 +369,8 @@ public class NetworkStatsFactory { } @GuardedBy("mPersistentDataLock") - private NetworkStats adjustForTunAnd464Xlat( - NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) { + private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats, + NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) { // Calculate delta from last snapshot final NetworkStats delta = uidDetailStats.subtract(previousStats); @@ -381,8 +381,9 @@ public class NetworkStatsFactory { delta.apply464xlatAdjustments(mStackedIfaces); // Migrate data usage over a VPN to the TUN network. - for (VpnInfo info : vpnArray) { - delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); + for (UnderlyingNetworkInfo info : vpnArray) { + delta.migrateTun(info.ownerUid, info.iface, + info.underlyingIfaces.toArray(new String[0])); // Filter out debug entries as that may lead to over counting. delta.filterDebugEntries(); } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 81a6641de8a4..0ab35a911025 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -104,6 +104,7 @@ import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; +import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.netstats.provider.INetworkStatsProvider; import android.net.netstats.provider.INetworkStatsProviderCallback; @@ -143,7 +144,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.VpnInfo; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FileRotator; @@ -973,7 +973,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Network[] defaultNetworks, NetworkState[] networkStates, String activeIface, - VpnInfo[] vpnInfos) { + UnderlyingNetworkInfo[] underlyingNetworkInfos) { checkNetworkStackPermission(mContext); final long token = Binder.clearCallingIdentity(); @@ -986,7 +986,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // Update the VPN underlying interfaces only after the poll is made and tun data has been // migrated. Otherwise the migration would use the new interfaces instead of the ones that // were current when the polled data was transferred. - mStatsFactory.updateVpnInfos(vpnInfos); + mStatsFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); } @Override diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 7a6792c121c0..fc5654144b3f 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -249,7 +249,7 @@ public final class OverlayManagerService extends SystemService { FgThread.getHandler().post(() -> { List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId); updateActivityManager(affectedTargets, pair.userId); - broadcastActionOverlayChanged(affectedTargets, pair.userId); + broadcastActionOverlayChanged(pair.packageName, pair.userId); }); }; @@ -922,20 +922,24 @@ public final class OverlayManagerService extends SystemService { throw new IllegalArgumentException("null transaction"); } - // map: userId -> list<targetPackageName> - SparseArray<List<String>> affectedTargetsToUpdate = new SparseArray<>(); + // map: userId -> set<package-name>: target packages of overlays in + // this transaction + SparseArray<Set<String>> transactionTargets = new SparseArray<>(); + + // map: userId -> set<package-name>: packages that need to reload + // their resources due to changes to the overlays in this + // transaction + SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>(); synchronized (mLock) { - // map: userId -> set<targetPackageName> - SparseArray<Set<String>> targetsToUpdate = new SparseArray<>(); // execute the requests (as calling user) for (final OverlayManagerTransaction.Request request : transaction) { executeRequest(request).ifPresent(target -> { - Set<String> userTargets = targetsToUpdate.get(target.userId); + Set<String> userTargets = transactionTargets.get(target.userId); if (userTargets == null) { userTargets = new ArraySet<String>(); - targetsToUpdate.put(target.userId, userTargets); + transactionTargets.put(target.userId, userTargets); } userTargets.add(target.packageName); }); @@ -949,11 +953,11 @@ public final class OverlayManagerService extends SystemService { persistSettings(); // inform the package manager about the new paths - for (int index = 0; index < targetsToUpdate.size(); index++) { - final int userId = targetsToUpdate.keyAt(index); + for (int index = 0; index < transactionTargets.size(); index++) { + final int userId = transactionTargets.keyAt(index); final List<String> affectedTargets = - updatePackageManager(targetsToUpdate.valueAt(index), userId); - affectedTargetsToUpdate.put(userId, affectedTargets); + updatePackageManager(transactionTargets.valueAt(index), userId); + affectedPackagesToUpdate.put(userId, affectedTargets); } } finally { Binder.restoreCallingIdentity(ident); @@ -963,13 +967,18 @@ public final class OverlayManagerService extends SystemService { FgThread.getHandler().post(() -> { final long ident = Binder.clearCallingIdentity(); try { - // schedule apps to refresh + broadcast the ACTION_OVERLAY_CHANGED intents - for (int index = 0; index < affectedTargetsToUpdate.size(); index++) { - final int userId = affectedTargetsToUpdate.keyAt(index); - final List<String> packageNames = affectedTargetsToUpdate.valueAt(index); + // schedule apps to refresh + for (int index = 0; index < affectedPackagesToUpdate.size(); index++) { + final int userId = affectedPackagesToUpdate.keyAt(index); + updateActivityManager(affectedPackagesToUpdate.valueAt(index), userId); + } - updateActivityManager(packageNames, userId); - broadcastActionOverlayChanged(packageNames, userId); + // broadcast the ACTION_OVERLAY_CHANGED intents + for (int index = 0; index < transactionTargets.size(); index++) { + final int userId = transactionTargets.keyAt(index); + for (String pkg: transactionTargets.valueAt(index)) { + broadcastActionOverlayChanged(pkg, userId); + } } } finally { Binder.restoreCallingIdentity(ident); @@ -1312,13 +1321,6 @@ public final class OverlayManagerService extends SystemService { // Helper methods to update other parts of the system or read/write // settings: these methods should never call into each other! - private void broadcastActionOverlayChanged(@NonNull final Collection<String> packageNames, - final int userId) { - for (final String packageName : packageNames) { - broadcastActionOverlayChanged(packageName, userId); - } - } - private void broadcastActionOverlayChanged(@NonNull final String targetPackageName, final int userId) { final Intent intent = new Intent(ACTION_OVERLAY_CHANGED, diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index dd507a3b1f81..4ff75fa06077 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -21,6 +21,7 @@ import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.Binder; import android.os.BugreportParams; @@ -31,6 +32,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserManager; +import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Slog; @@ -53,11 +55,13 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { private final Object mLock = new Object(); private final Context mContext; private final AppOpsManager mAppOps; + private final TelephonyManager mTelephonyManager; private final ArraySet<String> mBugreportWhitelistedPackages; BugreportManagerServiceImpl(Context context) { mContext = context; - mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mAppOps = context.getSystemService(AppOpsManager.class); + mTelephonyManager = context.getSystemService(TelephonyManager.class); mBugreportWhitelistedPackages = SystemConfig.getInstance().getBugreportWhitelistedPackages(); } @@ -67,11 +71,14 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { public void startBugreport(int callingUidUnused, String callingPackage, FileDescriptor bugreportFd, FileDescriptor screenshotFd, int bugreportMode, IDumpstateListener listener, boolean isScreenshotRequested) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport"); Objects.requireNonNull(callingPackage); Objects.requireNonNull(bugreportFd); Objects.requireNonNull(listener); validateBugreportMode(bugreportMode); + + int callingUid = Binder.getCallingUid(); + enforcePermission(callingPackage, callingUid, bugreportMode + == BugreportParams.BUGREPORT_MODE_TELEPHONY /* checkCarrierPrivileges */); final long identity = Binder.clearCallingIdentity(); try { ensureIsPrimaryUser(); @@ -79,13 +86,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { Binder.restoreCallingIdentity(identity); } - int callingUid = Binder.getCallingUid(); - mAppOps.checkPackage(callingUid, callingPackage); - - if (!mBugreportWhitelistedPackages.contains(callingPackage)) { - throw new SecurityException( - callingPackage + " is not whitelisted to use Bugreport API"); - } synchronized (mLock) { startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd, bugreportMode, listener, isScreenshotRequested); @@ -93,10 +93,11 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } @Override - @RequiresPermission(android.Manifest.permission.DUMP) - public void cancelBugreport() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, - "cancelBugreport"); + @RequiresPermission(android.Manifest.permission.DUMP) // or carrier privileges + public void cancelBugreport(int callingUidUnused, String callingPackage) { + int callingUid = Binder.getCallingUid(); + enforcePermission(callingPackage, callingUid, true /* checkCarrierPrivileges */); + synchronized (mLock) { IDumpstate ds = getDumpstateBinderServiceLocked(); if (ds == null) { @@ -104,7 +105,11 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { return; } try { - ds.cancelBugreport(); + // Note: this may throw SecurityException back out to the caller if they aren't + // allowed to cancel the report, in which case we should NOT be setting ctl.stop, + // since that would unintentionally kill some other app's bugreport, which we + // specifically disallow. + ds.cancelBugreport(callingUid, callingPackage); } catch (RemoteException e) { Slog.e(TAG, "RemoteException in cancelBugreport", e); } @@ -127,6 +132,34 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } } + private void enforcePermission( + String callingPackage, int callingUid, boolean checkCarrierPrivileges) { + mAppOps.checkPackage(callingUid, callingPackage); + + // To gain access through the DUMP permission, the OEM has to allow this package explicitly + // via sysconfig and privileged permissions. + if (mBugreportWhitelistedPackages.contains(callingPackage) + && mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + == PackageManager.PERMISSION_GRANTED) { + return; + } + // For carrier privileges, this can include user-installed apps. This is essentially a + // function of the current active SIM(s) in the device to let carrier apps through. + if (checkCarrierPrivileges + && mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return; + } + + String message = + callingPackage + + " does not hold the DUMP permission or is not bugreport-whitelisted " + + (checkCarrierPrivileges ? "and does not have carrier privileges " : "") + + "to request a bugreport"; + Slog.w(TAG, message); + throw new SecurityException(message); + } + /** * Validates that the current user is the primary user. * @@ -182,7 +215,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { // lifecycle correctly. If we don't subsequent callers will get // BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS error. // Note that listener will be notified by the death recipient below. - cancelBugreport(); + cancelBugreport(callingUid, callingPackage); } } @@ -303,6 +336,13 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @Override public void binderDied() { + try { + // Allow a small amount of time for any error or finished callbacks to be made. + // This ensures that the listener does not receive an erroneous runtime error + // callback. + Thread.sleep(1000); + } catch (InterruptedException ignored) { + } synchronized (mLock) { if (!mDone) { // If we have not gotten a "done" callback this must be a crash. diff --git a/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS new file mode 100644 index 000000000000..a52e9cf2f4c3 --- /dev/null +++ b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS @@ -0,0 +1,7 @@ +# OWNERS of Multiuser related files related to Enterprise + +include /MULTIUSER_OWNERS + +# Enterprise owners +rubinxu@google.com +sandness@google.com diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java index 06706cd06e11..0ffc1ed9e90c 100644 --- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java +++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java @@ -184,7 +184,7 @@ public class ModuleInfoProvider { List<PackageInfo> allPackages; try { allPackages = mPackageManager.getInstalledPackages( - flags | PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM).getList(); + flags | PackageManager.MATCH_APEX, UserHandle.getCallingUserId()).getList(); } catch (RemoteException e) { Slog.w(TAG, "Unable to retrieve all package names", e); return Collections.emptyList(); diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index 004259b7478c..43c5d5e4015e 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -30,13 +30,12 @@ per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google. per-file CrossProfileAppsService.java = omakoto@google.com, yamasani@google.com per-file CrossProfileIntentFilter.java = omakoto@google.com, yamasani@google.com per-file CrossProfileIntentResolver.java = omakoto@google.com, yamasani@google.com -per-file RestrictionsSet.java = bookatz@google.com, omakoto@google.com, yamasani@google.com, rubinxu@google.com, sandness@google.com -per-file UserManagerInternal.java = bookatz@google.com, omakoto@google.com, yamasani@google.com -per-file UserManagerService.java = bookatz@google.com, omakoto@google.com, yamasani@google.com -per-file UserRestrictionsUtils.java = omakoto@google.com, rubinxu@google.com, sandness@google.com, yamasani@google.com -per-file UserSystemPackageInstaller.java = bookatz@google.com, omakoto@google.com, yamasani@google.com -per-file UserTypeDetails.java = bookatz@google.com, omakoto@google.com, yamasani@google.com -per-file UserTypeFactory.java = bookatz@google.com, omakoto@google.com, yamasani@google.com +per-file RestrictionsSet.java = file:MULTIUSER_AND_ENTERPRISE_OWNERS +per-file UserManager* = file:/MULTIUSER_OWNERS +per-file UserRestriction* = file:MULTIUSER_AND_ENTERPRISE_OWNERS +per-file UserSystemPackageInstaller* = file:/MULTIUSER_OWNERS +per-file UserTypeDetails.java = file:/MULTIUSER_OWNERS +per-file UserTypeFactory.java = file:/MULTIUSER_OWNERS # security per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index 5c01e43af5a9..fd2d8e1b834b 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.content.Context; import android.content.IntentSender; import android.content.pm.PackageManager; +import android.hardware.boot.V1_0.IBootControl; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.Binder; @@ -73,6 +74,8 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb"; @VisibleForTesting static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb"; + @VisibleForTesting + static final String AB_UPDATE = "ro.build.ab_update"; private static final Object sRequestLock = new Object(); @@ -177,6 +180,25 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return socket; } + /** + * Throws remote exception if there's an error getting the boot control HAL. + * Returns null if the boot control HAL's version is older than V1_2. + */ + public android.hardware.boot.V1_2.IBootControl getBootControl() throws RemoteException { + IBootControl bootControlV10 = IBootControl.getService(true); + if (bootControlV10 == null) { + throw new RemoteException("Failed to get boot control HAL V1_0."); + } + + android.hardware.boot.V1_2.IBootControl bootControlV12 = + android.hardware.boot.V1_2.IBootControl.castFrom(bootControlV10); + if (bootControlV12 == null) { + Slog.w(TAG, "Device doesn't implement boot control HAL V1_2."); + return null; + } + return bootControlV12; + } + public void threadSleep(long millis) throws InterruptedException { Thread.sleep(millis); } @@ -476,6 +498,56 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR; } + private boolean isAbDevice() { + return "true".equalsIgnoreCase(mInjector.systemPropertiesGet(AB_UPDATE)); + } + + private boolean verifySlotForNextBoot(boolean slotSwitch) { + if (!isAbDevice()) { + Slog.w(TAG, "Device isn't a/b, skipping slot verification."); + return true; + } + + android.hardware.boot.V1_2.IBootControl bootControl; + try { + bootControl = mInjector.getBootControl(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to get the boot control HAL " + e); + return false; + } + + // TODO(xunchang) enforce boot control V1_2 HAL on devices using multi client RoR + if (bootControl == null) { + Slog.w(TAG, "Cannot get the boot control HAL, skipping slot verification."); + return true; + } + + int current_slot; + int next_active_slot; + try { + current_slot = bootControl.getCurrentSlot(); + if (current_slot != 0 && current_slot != 1) { + throw new IllegalStateException("Current boot slot should be 0 or 1, got " + + current_slot); + } + next_active_slot = bootControl.getActiveBootSlot(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to query the active slots", e); + return false; + } + + int expected_active_slot = current_slot; + if (slotSwitch) { + expected_active_slot = current_slot == 0 ? 1 : 0; + } + if (next_active_slot != expected_active_slot) { + Slog.w(TAG, "The next active boot slot doesn't match the expected value, " + + "expected " + expected_active_slot + ", got " + next_active_slot); + return false; + } + return true; + } + private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) { if (packageName == null) { Slog.w(TAG, "Missing packageName when rebooting with lskf."); @@ -485,7 +557,10 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return false; } - // TODO(xunchang) check the slot to boot into, and fail the reboot upon slot mismatch. + if (!verifySlotForNextBoot(slotSwitch)) { + return false; + } + // TODO(xunchang) write the vbmeta digest along with the escrowKey before reboot. if (!mInjector.getLockSettingsService().armRebootEscrow()) { Slog.w(TAG, "Failure to escrow key for reboot"); diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java index f20d80d57476..ae71c1a1e444 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java @@ -76,7 +76,7 @@ public class RecoverySystemShellCommand extends ShellCommand { private int rebootAndApply() throws RemoteException { String packageName = getNextArgRequired(); String rebootReason = getNextArgRequired(); - boolean success = mService.rebootWithLskf(packageName, rebootReason, true); + boolean success = mService.rebootWithLskf(packageName, rebootReason, false); PrintWriter pw = getOutPrintWriter(); // Keep the old message for cts test. pw.printf("%s Reboot and apply status: %s\n", packageName, diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS new file mode 100644 index 000000000000..91b240bcb189 --- /dev/null +++ b/services/core/java/com/android/server/security/OWNERS @@ -0,0 +1,4 @@ +# Bug component: 36824 + +per-file FileIntegrityService.java = victorhsieh@google.com +per-file VerityUtils.java = victorhsieh@google.com diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index 0d059ae389e9..8345424712dd 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -361,7 +361,11 @@ public final class StorageSessionController { } } + private static boolean isSupportedVolume(VolumeInfo vol) { + return isEmulatedOrPublic(vol) || vol.type == VolumeInfo.TYPE_STUB; + } + private boolean shouldHandle(@Nullable VolumeInfo vol) { - return mIsFuseEnabled && !mIsResetting && (vol == null || isEmulatedOrPublic(vol)); + return mIsFuseEnabled && !mIsResetting && (vol == null || isSupportedVolume(vol)); } } diff --git a/services/core/java/com/android/server/textservices/OWNERS b/services/core/java/com/android/server/textservices/OWNERS new file mode 100644 index 000000000000..9fa9b296706d --- /dev/null +++ b/services/core/java/com/android/server/textservices/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 816455 + +include /services/core/java/com/android/server/inputmethod/OWNERS diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 25cd6416d9c8..75277d1c338d 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -53,6 +53,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.security.Authorization; import android.security.KeyStore; import android.service.trust.TrustAgentService; import android.text.TextUtils; @@ -185,6 +186,8 @@ public class TrustManagerService extends SystemService { private boolean mTrustAgentsCanRun = false; private int mCurrentUser = UserHandle.USER_SYSTEM; + private Authorization mAuthorizationService; + public TrustManagerService(Context context) { super(context); mContext = context; @@ -194,6 +197,7 @@ public class TrustManagerService extends SystemService { mStrongAuthTracker = new StrongAuthTracker(context); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mSettingsObserver = new SettingsObserver(mHandler); + mAuthorizationService = new Authorization(); } @Override @@ -696,11 +700,13 @@ public class TrustManagerService extends SystemService { if (changed) { dispatchDeviceLocked(userId, locked); + mAuthorizationService.onLockScreenEvent(locked, userId, null); KeyStore.getInstance().onUserLockedStateChanged(userId, locked); // Also update the user's profiles who have unified challenge, since they // share the same unlocked state (see {@link #isDeviceLocked(int)}) for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) { if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) { + mAuthorizationService.onLockScreenEvent(locked, profileHandle, null); KeyStore.getInstance().onUserLockedStateChanged(profileHandle, locked); } } @@ -1252,6 +1258,7 @@ public class TrustManagerService extends SystemService { mDeviceLockedForUser.put(userId, locked); } + mAuthorizationService.onLockScreenEvent(locked, userId, null); KeyStore.getInstance().onUserLockedStateChanged(userId, locked); if (locked) { diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java index 42f12eb23d39..07725038255e 100644 --- a/services/core/java/com/android/server/tv/TvInputHal.java +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -51,7 +51,8 @@ final class TvInputHal implements Handler.Callback { public interface Callback { void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs); void onDeviceUnavailable(int deviceId); - void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs); + void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs, + int cableConnectionStatus); void onFirstFrameCaptured(int deviceId, int streamId); } @@ -142,8 +143,9 @@ final class TvInputHal implements Handler.Callback { mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0).sendToTarget(); } - private void streamConfigsChangedFromNative(int deviceId) { - mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0).sendToTarget(); + private void streamConfigsChangedFromNative(int deviceId, int cableConnectionStatus) { + mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, + cableConnectionStatus).sendToTarget(); } private void firstFrameCapturedFromNative(int deviceId, int streamId) { @@ -184,6 +186,7 @@ final class TvInputHal implements Handler.Callback { case EVENT_STREAM_CONFIGURATION_CHANGED: { TvStreamConfig[] configs; int deviceId = msg.arg1; + int cableConnectionStatus = msg.arg2; synchronized (mLock) { if (DEBUG) { Slog.d(TAG, "EVENT_STREAM_CONFIGURATION_CHANGED: deviceId = " + deviceId); @@ -191,7 +194,7 @@ final class TvInputHal implements Handler.Callback { retrieveStreamConfigsLocked(deviceId); configs = mStreamConfigs.get(deviceId); } - mCallback.onStreamConfigurationChanged(deviceId, configs); + mCallback.onStreamConfigurationChanged(deviceId, configs, cableConnectionStatus); break; } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 2314afc787c3..3dfb99e5c0fc 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -156,6 +156,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { synchronized (mLock) { Connection connection = new Connection(info); connection.updateConfigsLocked(configs); + connection.updateCableConnectionStatusLocked(info.getCableConnectionStatus()); mConnections.put(info.getDeviceId(), connection); buildHardwareListLocked(); mHandler.obtainMessage( @@ -202,7 +203,8 @@ class TvInputHardwareManager implements TvInputHal.Callback { } @Override - public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) { + public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs, + int cableConnectionStatus) { synchronized (mLock) { Connection connection = mConnections.get(deviceId); if (connection == null) { @@ -211,12 +213,22 @@ class TvInputHardwareManager implements TvInputHal.Callback { return; } int previousConfigsLength = connection.getConfigsLengthLocked(); + int previousCableConnectionStatus = connection.getInputStateLocked(); connection.updateConfigsLocked(configs); String inputId = mHardwareInputIdMap.get(deviceId); - if (inputId != null - && (previousConfigsLength == 0) != (connection.getConfigsLengthLocked() == 0)) { - mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, - connection.getInputStateLocked(), 0, inputId).sendToTarget(); + if (inputId != null) { + if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) { + if (previousCableConnectionStatus != connection.getInputStateLocked()) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId).sendToTarget(); + } + } else { + if ((previousConfigsLength == 0) + != (connection.getConfigsLengthLocked() == 0)) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + connection.getInputStateLocked(), 0, inputId).sendToTarget(); + } + } } ITvInputHardwareCallback callback = connection.getCallbackLocked(); if (callback != null) { @@ -624,7 +636,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } private class Connection implements IBinder.DeathRecipient { - private final TvInputHardwareInfo mHardwareInfo; + private TvInputHardwareInfo mHardwareInfo; private TvInputInfo mInfo; private TvInputHardwareImpl mHardware = null; private ITvInputHardwareCallback mCallback; @@ -633,6 +645,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { private Integer mResolvedUserId = null; private Runnable mOnFirstFrameCaptured; private ResourceClientProfile mResourceClientProfile = null; + private boolean mIsCableConnectionStatusUpdated = false; public Connection(TvInputHardwareInfo hardwareInfo) { mHardwareInfo = hardwareInfo; @@ -735,6 +748,17 @@ class TvInputHardwareManager implements TvInputHal.Callback { + " }"; } + public boolean updateCableConnectionStatusLocked(int cableConnectionStatus) { + // Update connection status only if it's not default value + if (cableConnectionStatus != TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN + || mIsCableConnectionStatusUpdated) { + mIsCableConnectionStatusUpdated = true; + mHardwareInfo = mHardwareInfo.toBuilder() + .cableConnectionStatus(cableConnectionStatus).build(); + } + return mIsCableConnectionStatusUpdated; + } + private int getConfigsLengthLocked() { return mConfigs == null ? 0 : mConfigs.length; } @@ -742,7 +766,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { private int getInputStateLocked() { int configsLength = getConfigsLengthLocked(); if (configsLength > 0) { - return INPUT_STATE_CONNECTED; + if (!mIsCableConnectionStatusUpdated) { + return INPUT_STATE_CONNECTED; + } } switch (mHardwareInfo.getCableConnectionStatus()) { case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_CONNECTED: diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 6cd02581ee67..d858ae41cee8 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -20,6 +20,7 @@ import static android.media.AudioManager.DEVICE_NONE; import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.BroadcastReceiver; @@ -1728,6 +1729,46 @@ public final class TvInputManagerService extends SystemService { } @Override + public void pauseRecording(IBinder sessionToken, @NonNull Bundle params, int userId) { + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "pauseRecording"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + getSessionLocked(sessionToken, callingUid, resolvedUserId) + .pauseRecording(params); + } catch (RemoteException | SessionNotFoundException e) { + Slog.e(TAG, "error in pauseRecording", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void resumeRecording(IBinder sessionToken, @NonNull Bundle params, int userId) { + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "resumeRecording"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + getSessionLocked(sessionToken, callingUid, resolvedUserId) + .resumeRecording(params); + } catch (RemoteException | SessionNotFoundException e) { + Slog.e(TAG, "error in resumeRecording", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public List<TvInputHardwareInfo> getHardwareList() throws RemoteException { if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE) != PackageManager.PERMISSION_GRANTED) { diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java index e1feb5aab869..6427ae2dc13c 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -24,6 +24,9 @@ import android.net.NetworkCapabilities; import android.os.Handler; import android.os.ParcelUuid; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; + import java.util.Objects; /** @@ -72,7 +75,8 @@ public class UnderlyingNetworkTracker extends Handler { @NonNull public final LinkProperties linkProperties; public final boolean blocked; - private UnderlyingNetworkRecord( + @VisibleForTesting(visibility = Visibility.PRIVATE) + UnderlyingNetworkRecord( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties, diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 9d21b9241c0d..132883e4a041 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -168,8 +168,8 @@ public class Vcn extends Handler { @NonNull NetworkRequest request, int score, int providerId) { if (score > getNetworkScore()) { Slog.v(getLogTag(), - "Request " + request.requestId + " already satisfied by higher-scoring (" - + score + ") network from provider " + providerId); + "Request already satisfied by higher-scoring (" + score + ") network from " + + "provider " + providerId + ": " + request); return; } @@ -177,8 +177,7 @@ public class Vcn extends Handler { for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { Slog.v(getLogTag(), - "Request " + request.requestId - + " satisfied by existing VcnGatewayConnection"); + "Request already satisfied by existing VcnGatewayConnection: " + request); return; } } @@ -202,12 +201,12 @@ public class Vcn extends Handler { private boolean requestSatisfiedByGatewayConnectionConfig( @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) { - final NetworkCapabilities configCaps = new NetworkCapabilities(); + final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); for (int cap : config.getAllExposedCapabilities()) { - configCaps.addCapability(cap); + builder.addCapability(cap); } - return request.networkCapabilities.satisfiedByNetworkCapabilities(configCaps); + return request.canBeSatisfiedBy(builder.build()); } private String getLogTag() { diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 7ea8e04580f7..3cfa00eb6079 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -16,33 +16,443 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; + +import static com.android.server.VcnManagementService.VDBG; + import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.IpSecManager; +import android.net.IpSecManager.IpSecTunnelInterface; +import android.net.IpSecManager.ResourceUnavailableException; +import android.net.IpSecTransform; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkAgent; +import android.net.NetworkCapabilities; +import android.net.RouteInfo; +import android.net.annotations.PolicyDirection; +import android.net.ipsec.ike.ChildSessionCallback; +import android.net.ipsec.ike.ChildSessionConfiguration; +import android.net.ipsec.ike.ChildSessionParams; +import android.net.ipsec.ike.IkeSession; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.ipsec.ike.IkeSessionConfiguration; +import android.net.ipsec.ike.IkeSessionParams; +import android.net.ipsec.ike.exceptions.IkeException; +import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.net.vcn.VcnGatewayConnectionConfig; import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Message; import android.os.ParcelUuid; +import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; import java.util.Objects; +import java.util.concurrent.TimeUnit; /** * A single VCN Gateway Connection, providing a single public-facing VCN network. * * <p>This class handles mobility events, performs retries, and tracks safe-mode conditions. * + * <pre>Internal state transitions are as follows: + * + * +----------------------------+ +------------------------------+ + * | DisconnectedState | Teardown or | DisconnectingState | + * | |<--no available--| | + * | Initial state. | underlying | Transitive state for tearing | + * +----------------------------+ networks | tearing down an IKE session. | + * | +------------------------------+ + * | ^ | + * Underlying Network Teardown requested | Not tearing down + * changed +--or retriable error--+ and has available + * | | occurred underlying network + * | ^ | + * v | v + * +----------------------------+ | +------------------------------+ + * | ConnectingState |<----------------| RetryTimeoutState | + * | | | | | + * | Transitive state for | | | Transitive state for | + * | starting IKE negotiation. |---+ | handling retriable errors. | + * +----------------------------+ | +------------------------------+ + * | | + * IKE session | + * negotiated | + * | | + * v | + * +----------------------------+ ^ + * | ConnectedState | | + * | | | + * | Stable state where | | + * | gateway connection is set | | + * | up, and Android Network is | | + * | connected. |---+ + * +----------------------------+ + * </pre> + * * @hide */ -public class VcnGatewayConnection extends Handler implements UnderlyingNetworkTrackerCallback { +public class VcnGatewayConnection extends StateMachine { private static final String TAG = VcnGatewayConnection.class.getSimpleName(); + private static final InetAddress DUMMY_ADDR = InetAddresses.parseNumericAddress("192.0.2.0"); + private static final int ARG_NOT_PRESENT = Integer.MIN_VALUE; + + private static final String DISCONNECT_REASON_INTERNAL_ERROR = "Uncaught exception: "; + private static final String DISCONNECT_REASON_UNDERLYING_NETWORK_LOST = + "Underlying Network lost"; + private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel"; + private static final int TOKEN_ALL = Integer.MIN_VALUE; + + private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int TEARDOWN_TIMEOUT_SECONDS = 5; + + private interface EventInfo {} + + /** + * Sent when there are changes to the underlying network (per the UnderlyingNetworkTracker). + * + * <p>May indicate an entirely new underlying network, OR a change in network properties. + * + * <p>Relevant in ALL states. + * + * <p>In the Connected state, this MAY indicate a mobility even occurred. + * + * @param arg1 The "all" token; this event is always applicable. + * @param obj @NonNull An EventUnderlyingNetworkChangedInfo instance with relevant data. + */ + private static final int EVENT_UNDERLYING_NETWORK_CHANGED = 1; + + private static class EventUnderlyingNetworkChangedInfo implements EventInfo { + @Nullable public final UnderlyingNetworkRecord newUnderlying; + + EventUnderlyingNetworkChangedInfo(@Nullable UnderlyingNetworkRecord newUnderlying) { + this.newUnderlying = newUnderlying; + } + + @Override + public int hashCode() { + return Objects.hash(newUnderlying); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof EventUnderlyingNetworkChangedInfo)) { + return false; + } + + final EventUnderlyingNetworkChangedInfo rhs = (EventUnderlyingNetworkChangedInfo) other; + return Objects.equals(newUnderlying, rhs.newUnderlying); + } + } + + /** + * Sent (delayed) to trigger an attempt to reestablish the tunnel. + * + * <p>Only relevant in the Retry-timeout state, discarded in all other states. + * + * <p>Upon receipt of this signal, the state machine will transition from the Retry-timeout + * state to the Connecting state. + * + * @param arg1 The "all" token; no sessions are active in the RetryTimeoutState. + */ + private static final int EVENT_RETRY_TIMEOUT_EXPIRED = 2; + + /** + * Sent when a gateway connection has been lost, either due to a IKE or child failure. + * + * <p>Relevant in all states that have an IKE session. + * + * <p>Upon receipt of this signal, the state machine will (unless loss of the session is + * expected) transition to the Disconnecting state, to ensure IKE session closure before + * retrying, or fully shutting down. + * + * @param arg1 The session token for the IKE Session that was lost, used to prevent out-of-date + * signals from propagating. + * @param obj @NonNull An EventSessionLostInfo instance with relevant data. + */ + private static final int EVENT_SESSION_LOST = 3; + + private static class EventSessionLostInfo implements EventInfo { + @Nullable public final Exception exception; + + EventSessionLostInfo(@NonNull Exception exception) { + this.exception = exception; + } + + @Override + public int hashCode() { + return Objects.hash(exception); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof EventSessionLostInfo)) { + return false; + } + + final EventSessionLostInfo rhs = (EventSessionLostInfo) other; + return Objects.equals(exception, rhs.exception); + } + } + + /** + * Sent when an IKE session has completely closed. + * + * <p>Relevant only in the Disconnecting State, used to identify that a session being torn down + * was fully closed. If this event is not fired within a timely fashion, the IKE session will be + * forcibly terminated. + * + * <p>Upon receipt of this signal, the state machine will (unless closure of the session is + * expected) transition to the Disconnected or RetryTimeout states, depending on whether the + * GatewayConnection is being fully torn down. + * + * @param arg1 The session token for the IKE Session that was lost, used to prevent out-of-date + * signals from propagating. + * @param obj @NonNull An EventSessionLostInfo instance with relevant data. + */ + private static final int EVENT_SESSION_CLOSED = 4; + + /** + * Sent when an IKE Child Transform was created, and should be applied to the tunnel. + * + * <p>Only relevant in the Connecting, Connected and Migrating states. This callback MUST be + * handled in the Connected or Migrating states, and should be deferred if necessary. + * + * @param arg1 The session token for the IKE Session that had a new child created, used to + * prevent out-of-date signals from propagating. + * @param obj @NonNull An EventTransformCreatedInfo instance with relevant data. + */ + private static final int EVENT_TRANSFORM_CREATED = 5; + + private static class EventTransformCreatedInfo implements EventInfo { + @PolicyDirection public final int direction; + @NonNull public final IpSecTransform transform; + + EventTransformCreatedInfo( + @PolicyDirection int direction, @NonNull IpSecTransform transform) { + this.direction = direction; + this.transform = Objects.requireNonNull(transform); + } + + @Override + public int hashCode() { + return Objects.hash(direction, transform); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof EventTransformCreatedInfo)) { + return false; + } + + final EventTransformCreatedInfo rhs = (EventTransformCreatedInfo) other; + return direction == rhs.direction && Objects.equals(transform, rhs.transform); + } + } + + /** + * Sent when an IKE Child Session was completely opened and configured successfully. + * + * <p>Only relevant in the Connected and Migrating states. + * + * @param arg1 The session token for the IKE Session for which a child was opened and configured + * successfully, used to prevent out-of-date signals from propagating. + * @param obj @NonNull An EventSetupCompletedInfo instance with relevant data. + */ + private static final int EVENT_SETUP_COMPLETED = 6; + + private static class EventSetupCompletedInfo implements EventInfo { + @NonNull public final ChildSessionConfiguration childSessionConfig; + + EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) { + this.childSessionConfig = Objects.requireNonNull(childSessionConfig); + } + + @Override + public int hashCode() { + return Objects.hash(childSessionConfig); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof EventSetupCompletedInfo)) { + return false; + } + + final EventSetupCompletedInfo rhs = (EventSetupCompletedInfo) other; + return Objects.equals(childSessionConfig, rhs.childSessionConfig); + } + } + + /** + * Sent when conditions (internal or external) require a disconnect. + * + * <p>Relevant in all states except the Disconnected state. + * + * <p>This signal is often fired with a timeout in order to prevent disconnecting during + * transient conditions, such as network switches. Upon the transient passing, the signal is + * canceled based on the disconnect reason. + * + * <p>Upon receipt of this signal, the state machine MUST tear down all active sessions, cancel + * any pending work items, and move to the Disconnected state. + * + * @param arg1 The "all" token; this signal is always honored. + * @param obj @NonNull An EventDisconnectRequestedInfo instance with relevant data. + */ + private static final int EVENT_DISCONNECT_REQUESTED = 7; + + private static class EventDisconnectRequestedInfo implements EventInfo { + /** The reason why the disconnect was requested. */ + @NonNull public final String reason; + + EventDisconnectRequestedInfo(@NonNull String reason) { + this.reason = Objects.requireNonNull(reason); + } + + @Override + public int hashCode() { + return Objects.hash(reason); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof EventDisconnectRequestedInfo)) { + return false; + } + + final EventDisconnectRequestedInfo rhs = (EventDisconnectRequestedInfo) other; + return reason.equals(rhs.reason); + } + } + + /** + * Sent (delayed) to trigger a forcible close of an IKE session. + * + * <p>Only relevant in the Disconnecting state, discarded in all other states. + * + * <p>Upon receipt of this signal, the state machine will transition from the Disconnecting + * state to the Disconnected state. + * + * @param arg1 The session token for the IKE Session that is being torn down, used to prevent + * out-of-date signals from propagating. + */ + private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @NonNull + final DisconnectedState mDisconnectedState = new DisconnectedState(); + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @NonNull + final DisconnectingState mDisconnectingState = new DisconnectingState(); + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @NonNull + final ConnectingState mConnectingState = new ConnectingState(); + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @NonNull + final ConnectedState mConnectedState = new ConnectedState(); + + @VisibleForTesting(visibility = Visibility.PRIVATE) + @NonNull + final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState(); + @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker; @NonNull private final VcnGatewayConnectionConfig mConnectionConfig; @NonNull private final Dependencies mDeps; + @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback; + + @NonNull private final IpSecManager mIpSecManager; + @NonNull private final IpSecTunnelInterface mTunnelIface; + + /** Running state of this VcnGatewayConnection. */ + private boolean mIsRunning = true; + + /** + * The token used by the primary/current/active session. + * + * <p>This token MUST be updated when a new stateful/async session becomes the + * primary/current/active session. Example cases where the session changes are: + * + * <ul> + * <li>Switching to an IKE session as the primary session + * </ul> + * + * <p>In the migrating state, where two sessions may be active, this value MUST represent the + * primary session. This is USUALLY the existing session, and is only switched to the new + * session when: + * + * <ul> + * <li>The new session connects successfully, and becomes the primary session + * <li>The existing session is lost, and the remaining (new) session becomes the primary + * session + * </ul> + */ + private int mCurrentToken = -1; + + /** + * The number of unsuccessful attempts since the last successful connection. + * + * <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared + * each time the Connected state is entered. + */ + private int mFailedAttempts = 0; + + /** + * The current underlying network. + * + * <p>Set in any states, always @NonNull in all states except Disconnected, null otherwise. + */ + private UnderlyingNetworkRecord mUnderlying; + + /** + * The active IKE session. + * + * <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and + * Migrating states, null otherwise. + */ + private VcnIkeSession mIkeSession; + + /** + * The last known child configuration. + * + * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating + * states, @Nullable otherwise. + */ + private ChildSessionConfiguration mChildConfig; + + /** + * The active network agent. + * + * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable + * otherwise. + */ + private NetworkAgent mNetworkAgent; + public VcnGatewayConnection( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @@ -50,35 +460,746 @@ public class VcnGatewayConnection extends Handler implements UnderlyingNetworkTr this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies()); } - private VcnGatewayConnection( + @VisibleForTesting(visibility = Visibility.PRIVATE) + VcnGatewayConnection( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull Dependencies deps) { - super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); + super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); mVcnContext = vcnContext; mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); mDeps = Objects.requireNonNull(deps, "Missing deps"); + mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback(); + mUnderlyingNetworkTracker = - mDeps.newUnderlyingNetworkTracker(mVcnContext, subscriptionGroup, this); + mDeps.newUnderlyingNetworkTracker( + mVcnContext, subscriptionGroup, mUnderlyingNetworkTrackerCallback); + mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class); + + IpSecTunnelInterface iface; + try { + iface = + mIpSecManager.createIpSecTunnelInterface( + DUMMY_ADDR, DUMMY_ADDR, new Network(-1)); + } catch (IOException | ResourceUnavailableException e) { + teardownAsynchronously(); + mTunnelIface = null; + + return; + } + + mTunnelIface = iface; + + addState(mDisconnectedState); + addState(mDisconnectingState); + addState(mConnectingState); + addState(mConnectedState); + addState(mRetryTimeoutState); + + setInitialState(mDisconnectedState); + setDbg(VDBG); + start(); } - /** Asynchronously tears down this GatewayConnection, and any resources used */ + /** + * Asynchronously tears down this GatewayConnection, and any resources used. + * + * <p>Once torn down, this VcnTunnel CANNOT be started again. + */ public void teardownAsynchronously() { + sendMessage( + EVENT_DISCONNECT_REQUESTED, + TOKEN_ALL, + new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN)); + + // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this + // is also called asynchronously when a NetworkAgent becomes unwanted + } + + @Override + protected void onQuitting() { + // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down. + if (mTunnelIface != null) { + mTunnelIface.close(); + } + mUnderlyingNetworkTracker.teardown(); } - private static class Dependencies { + private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback { + @Override + public void onSelectedUnderlyingNetworkChanged( + @Nullable UnderlyingNetworkRecord underlying) { + // If underlying is null, all underlying networks have been lost. Disconnect VCN after a + // timeout. + if (underlying == null) { + sendMessageDelayed( + EVENT_DISCONNECT_REQUESTED, + TOKEN_ALL, + new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST), + TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS)); + } else if (getHandler() != null) { + // Cancel any existing disconnect due to loss of underlying network + // getHandler() can return null if the state machine has already quit. Since this is + // called from other classes, this condition must be verified. + + getHandler() + .removeEqualMessages( + EVENT_DISCONNECT_REQUESTED, + new EventDisconnectRequestedInfo( + DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)); + } + + sendMessage( + EVENT_UNDERLYING_NETWORK_CHANGED, + TOKEN_ALL, + new EventUnderlyingNetworkChangedInfo(underlying)); + } + } + + private void sendMessage(int what, int token, EventInfo data) { + super.sendMessage(what, token, ARG_NOT_PRESENT, data); + } + + private void sendMessage(int what, int token, int arg2, EventInfo data) { + super.sendMessage(what, token, arg2, data); + } + + private void sendMessageDelayed(int what, int token, EventInfo data, long timeout) { + super.sendMessageDelayed(what, token, ARG_NOT_PRESENT, data, timeout); + } + + private void sendMessageDelayed(int what, int token, int arg2, EventInfo data, long timeout) { + super.sendMessageDelayed(what, token, arg2, data, timeout); + } + + private void sessionLost(int token, @Nullable Exception exception) { + sendMessage(EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); + } + + private void sessionClosed(int token, @Nullable Exception exception) { + // SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the + // Disconnecting state. + sessionLost(token, exception); + sendMessage(EVENT_SESSION_CLOSED, token); + } + + private void childTransformCreated( + int token, @NonNull IpSecTransform transform, int direction) { + sendMessage( + EVENT_TRANSFORM_CREATED, + token, + new EventTransformCreatedInfo(direction, transform)); + } + + private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) { + sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig)); + } + + private abstract class BaseState extends State { + @Override + public void enter() { + try { + enterState(); + } catch (Exception e) { + Slog.wtf(TAG, "Uncaught exception", e); + sendMessage( + EVENT_DISCONNECT_REQUESTED, + TOKEN_ALL, + new EventDisconnectRequestedInfo( + DISCONNECT_REASON_INTERNAL_ERROR + e.toString())); + } + } + + protected void enterState() throws Exception {} + + /** + * Top-level processMessage with safeguards to prevent crashing the System Server on non-eng + * builds. + */ + @Override + public boolean processMessage(Message msg) { + try { + processStateMsg(msg); + } catch (Exception e) { + Slog.wtf(TAG, "Uncaught exception", e); + sendMessage( + EVENT_DISCONNECT_REQUESTED, + TOKEN_ALL, + new EventDisconnectRequestedInfo( + DISCONNECT_REASON_INTERNAL_ERROR + e.toString())); + } + + return HANDLED; + } + + protected abstract void processStateMsg(Message msg) throws Exception; + + @Override + public void exit() { + try { + exitState(); + } catch (Exception e) { + Slog.wtf(TAG, "Uncaught exception", e); + sendMessage( + EVENT_DISCONNECT_REQUESTED, + TOKEN_ALL, + new EventDisconnectRequestedInfo( + DISCONNECT_REASON_INTERNAL_ERROR + e.toString())); + } + } + + protected void exitState() throws Exception {} + + protected void logUnhandledMessage(Message msg) { + // Log as unexpected all known messages, and log all else as unknown. + switch (msg.what) { + case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough + case EVENT_RETRY_TIMEOUT_EXPIRED: // Fallthrough + case EVENT_SESSION_LOST: // Fallthrough + case EVENT_SESSION_CLOSED: // Fallthrough + case EVENT_TRANSFORM_CREATED: // Fallthrough + case EVENT_SETUP_COMPLETED: // Fallthrough + case EVENT_DISCONNECT_REQUESTED: // Fallthrough + case EVENT_TEARDOWN_TIMEOUT_EXPIRED: + logUnexpectedEvent(msg.what); + break; + default: + logWtfUnknownEvent(msg.what); + break; + } + } + + protected void teardownNetwork() { + if (mNetworkAgent != null) { + mNetworkAgent.unregister(); + mNetworkAgent = null; + } + } + + protected void handleDisconnectRequested(String msg) { + Slog.v(TAG, "Tearing down. Cause: " + msg); + mIsRunning = false; + + teardownNetwork(); + + if (mIkeSession == null) { + // Already disconnected, go straight to DisconnectedState + transitionTo(mDisconnectedState); + } else { + // Still need to wait for full closure + transitionTo(mDisconnectingState); + } + } + + protected void logUnexpectedEvent(int what) { + Slog.d(TAG, String.format( + "Unexpected event code %d in state %s", what, this.getClass().getSimpleName())); + } + + protected void logWtfUnknownEvent(int what) { + Slog.wtf(TAG, String.format( + "Unknown event code %d in state %s", what, this.getClass().getSimpleName())); + } + } + + /** + * State representing the a disconnected VCN tunnel. + * + * <p>This is also is the initial state. + */ + private class DisconnectedState extends BaseState { + @Override + protected void enterState() { + if (!mIsRunning) { + quitNow(); // Ignore all queued events; cleanup is complete. + } + + if (mIkeSession != null || mNetworkAgent != null) { + Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState"); + } + } + + @Override + protected void processStateMsg(Message msg) { + switch (msg.what) { + case EVENT_UNDERLYING_NETWORK_CHANGED: + // First network found; start tunnel + mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; + + if (mUnderlying != null) { + transitionTo(mConnectingState); + } + break; + case EVENT_DISCONNECT_REQUESTED: + mIsRunning = false; + + quitNow(); + break; + default: + logUnhandledMessage(msg); + break; + } + } + } + + private abstract class ActiveBaseState extends BaseState { + /** + * Handles all incoming messages, discarding messages for previous networks. + * + * <p>States that handle mobility events may need to override this method to receive + * messages for all underlying networks. + */ + @Override + public boolean processMessage(Message msg) { + final int token = msg.arg1; + // Only process if a valid token is presented. + if (isValidToken(token)) { + return super.processMessage(msg); + } + + Slog.v(TAG, "Message called with obsolete token: " + token + "; what: " + msg.what); + return HANDLED; + } + + protected boolean isValidToken(int token) { + return (token == TOKEN_ALL || token == mCurrentToken); + } + } + + /** + * Transitive state representing a VCN that is tearing down an IKE session. + * + * <p>In this state, the IKE session is in the process of being torn down. If the IKE session + * does not complete teardown in a timely fashion, it will be killed (forcibly closed). + */ + private class DisconnectingState extends ActiveBaseState { + /** + * Whether to skip the RetryTimeoutState and go straight to the ConnectingState. + * + * <p>This is used when an underlying network change triggered a restart on a new network. + * + * <p>Reset (to false) upon exit of the DisconnectingState. + */ + private boolean mSkipRetryTimeout = false; + + // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change. + public void setSkipRetryTimeout(boolean shouldSkip) { + mSkipRetryTimeout = shouldSkip; + } + + @Override + protected void enterState() throws Exception { + if (mIkeSession == null) { + Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state."); + sendMessage(EVENT_SESSION_CLOSED, mCurrentToken); + return; + } + + // If underlying network has already been lost, save some time and just kill the session + if (mUnderlying == null) { + // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down. + mIkeSession.kill(); + return; + } + + mIkeSession.close(); + sendMessageDelayed( + EVENT_TEARDOWN_TIMEOUT_EXPIRED, + mCurrentToken, + TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); + } + + @Override + protected void processStateMsg(Message msg) { + switch (msg.what) { + case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough + mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; + + // If we received a new underlying network, continue. + if (mUnderlying != null) { + break; + } + + // Fallthrough; no network exists to send IKE close session requests. + case EVENT_TEARDOWN_TIMEOUT_EXPIRED: + // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED + mIkeSession.kill(); + + break; + case EVENT_DISCONNECT_REQUESTED: + teardownNetwork(); + + String reason = ((EventDisconnectRequestedInfo) msg.obj).reason; + if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) { + // Will trigger EVENT_SESSION_CLOSED immediately. + mIkeSession.kill(); + break; + } + + // Otherwise we are already in the process of shutting down. + break; + case EVENT_SESSION_CLOSED: + mIkeSession = null; + + if (mIsRunning && mUnderlying != null) { + transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState); + } else { + teardownNetwork(); + transitionTo(mDisconnectedState); + } + break; + default: + logUnhandledMessage(msg); + break; + } + } + + @Override + protected void exitState() throws Exception { + mSkipRetryTimeout = false; + } + } + + /** + * Transitive state representing a VCN that is making an primary (non-handover) connection. + * + * <p>This state starts IKE negotiation, but defers transform application & network setup to the + * Connected state. + */ + private class ConnectingState extends ActiveBaseState { + @Override + protected void enterState() { + if (mIkeSession != null) { + Slog.wtf(TAG, "ConnectingState entered with active session"); + + // Attempt to recover. + mIkeSession.kill(); + mIkeSession = null; + } + + mIkeSession = buildIkeSession(); + } + + @Override + protected void processStateMsg(Message msg) { + switch (msg.what) { + case EVENT_UNDERLYING_NETWORK_CHANGED: + final UnderlyingNetworkRecord oldUnderlying = mUnderlying; + mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; + + if (oldUnderlying == null) { + // This should never happen, but if it does, there's likely a nasty bug. + Slog.wtf(TAG, "Old underlying network was null in connected state. Bug?"); + } + + // If new underlying is null, all underlying networks have been lost; disconnect + if (mUnderlying == null) { + transitionTo(mDisconnectingState); + break; + } + + if (oldUnderlying != null + && mUnderlying.network.equals(oldUnderlying.network)) { + break; // Only network properties have changed; continue connecting. + } + // Else, retry on the new network. + + // Immediately come back to the ConnectingState (skip RetryTimeout, since this + // isn't a failure) + mDisconnectingState.setSkipRetryTimeout(true); + + // fallthrough - disconnect, and retry on new network. + case EVENT_SESSION_LOST: + transitionTo(mDisconnectingState); + break; + case EVENT_SESSION_CLOSED: + deferMessage(msg); + + transitionTo(mDisconnectingState); + break; + case EVENT_SETUP_COMPLETED: // fallthrough + case EVENT_TRANSFORM_CREATED: + // Child setup complete; move to ConnectedState for NetworkAgent registration + deferMessage(msg); + transitionTo(mConnectedState); + break; + case EVENT_DISCONNECT_REQUESTED: + handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); + break; + default: + logUnhandledMessage(msg); + break; + } + } + } + + private abstract class ConnectedStateBase extends ActiveBaseState {} + + /** + * Stable state representing a VCN that has a functioning connection to the mobility anchor. + * + * <p>This state handles IPsec transform application (initial and rekey), NetworkAgent setup, + * and monitors for mobility events. + */ + class ConnectedState extends ConnectedStateBase { + @Override + protected void processStateMsg(Message msg) {} + } + + /** + * Transitive state representing a VCN that failed to establish a connection, and will retry. + * + * <p>This state will be exited upon a new underlying network being found, or timeout expiry. + */ + class RetryTimeoutState extends ActiveBaseState { + @Override + protected void processStateMsg(Message msg) {} + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + static NetworkCapabilities buildNetworkCapabilities( + @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) { + final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); + + builder.addTransportType(TRANSPORT_CELLULAR); + builder.addCapability(NET_CAPABILITY_NOT_CONGESTED); + builder.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + + // Add exposed capabilities + for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) { + builder.addCapability(cap); + } + + return builder.build(); + } + + private static LinkProperties buildConnectedLinkProperties( + @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, + @NonNull IpSecTunnelInterface tunnelIface, + @NonNull ChildSessionConfiguration childConfig) { + final LinkProperties lp = new LinkProperties(); + + lp.setInterfaceName(tunnelIface.getInterfaceName()); + for (LinkAddress addr : childConfig.getInternalAddresses()) { + lp.addLinkAddress(addr); + } + for (InetAddress addr : childConfig.getInternalDnsServers()) { + lp.addDnsServer(addr); + } + + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + + lp.setMtu(gatewayConnectionConfig.getMaxMtu()); + + return lp; + } + + private class IkeSessionCallbackImpl implements IkeSessionCallback { + private final int mToken; + + IkeSessionCallbackImpl(int token) { + mToken = token; + } + + @Override + public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) { + Slog.v(TAG, "IkeOpened for token " + mToken); + // Nothing to do here. + } + + @Override + public void onClosed() { + Slog.v(TAG, "IkeClosed for token " + mToken); + sessionClosed(mToken, null); + } + + @Override + public void onClosedExceptionally(@NonNull IkeException exception) { + Slog.v(TAG, "IkeClosedExceptionally for token " + mToken, exception); + sessionClosed(mToken, exception); + } + + @Override + public void onError(@NonNull IkeProtocolException exception) { + Slog.v(TAG, "IkeError for token " + mToken, exception); + // Non-fatal, log and continue. + } + } + + private class ChildSessionCallbackImpl implements ChildSessionCallback { + private final int mToken; + + ChildSessionCallbackImpl(int token) { + mToken = token; + } + + @Override + public void onOpened(@NonNull ChildSessionConfiguration childConfig) { + Slog.v(TAG, "ChildOpened for token " + mToken); + childOpened(mToken, childConfig); + } + + @Override + public void onClosed() { + Slog.v(TAG, "ChildClosed for token " + mToken); + sessionLost(mToken, null); + } + + @Override + public void onClosedExceptionally(@NonNull IkeException exception) { + Slog.v(TAG, "ChildClosedExceptionally for token " + mToken, exception); + sessionLost(mToken, exception); + } + + @Override + public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) { + Slog.v(TAG, "ChildTransformCreated; Direction: " + direction + "; token " + mToken); + childTransformCreated(mToken, transform, direction); + } + + @Override + public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) { + // Nothing to be done; no references to the IpSecTransform are held, and this transform + // will be closed by the IKE library. + Slog.v(TAG, "ChildTransformDeleted; Direction: " + direction + "; for token " + mToken); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() { + return mUnderlyingNetworkTrackerCallback; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + UnderlyingNetworkRecord getUnderlyingNetwork() { + return mUnderlying; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + void setUnderlyingNetwork(@Nullable UnderlyingNetworkRecord record) { + mUnderlying = record; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + boolean isRunning() { + return mIsRunning; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + void setIsRunning(boolean isRunning) { + mIsRunning = isRunning; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + VcnIkeSession getIkeSession() { + return mIkeSession; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + void setIkeSession(@Nullable VcnIkeSession session) { + mIkeSession = session; + } + + private IkeSessionParams buildIkeParams() { + // TODO: Implement this once IkeSessionParams is persisted + return null; + } + + private ChildSessionParams buildChildParams() { + // TODO: Implement this once IkeSessionParams is persisted + return null; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + VcnIkeSession buildIkeSession() { + final int token = ++mCurrentToken; + + return mDeps.newIkeSession( + mVcnContext, + buildIkeParams(), + buildChildParams(), + new IkeSessionCallbackImpl(token), + new ChildSessionCallbackImpl(token)); + } + + /** External dependencies used by VcnGatewayConnection, for injection in tests */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + /** Builds a new UnderlyingNetworkTracker. */ public UnderlyingNetworkTracker newUnderlyingNetworkTracker( VcnContext vcnContext, ParcelUuid subscriptionGroup, UnderlyingNetworkTrackerCallback callback) { return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback); } + + /** Builds a new IkeSession. */ + public VcnIkeSession newIkeSession( + VcnContext vcnContext, + IkeSessionParams ikeSessionParams, + ChildSessionParams childSessionParams, + IkeSessionCallback ikeSessionCallback, + ChildSessionCallback childSessionCallback) { + return new VcnIkeSession( + vcnContext, + ikeSessionParams, + childSessionParams, + ikeSessionCallback, + childSessionCallback); + } } - @Override - public void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying) {} + /** Proxy implementation of IKE session, used for testing. */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class VcnIkeSession { + private final IkeSession mImpl; + + public VcnIkeSession( + VcnContext vcnContext, + IkeSessionParams ikeSessionParams, + ChildSessionParams childSessionParams, + IkeSessionCallback ikeSessionCallback, + ChildSessionCallback childSessionCallback) { + mImpl = + new IkeSession( + vcnContext.getContext(), + ikeSessionParams, + childSessionParams, + new HandlerExecutor(new Handler(vcnContext.getLooper())), + ikeSessionCallback, + childSessionCallback); + } + + /** Creates a new IKE Child session. */ + public void openChildSession( + @NonNull ChildSessionParams childSessionParams, + @NonNull ChildSessionCallback childSessionCallback) { + mImpl.openChildSession(childSessionParams, childSessionCallback); + } + + /** Closes an IKE session as identified by the ChildSessionCallback. */ + public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) { + mImpl.closeChildSession(childSessionCallback); + } + + /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */ + public void close() { + mImpl.close(); + } + + /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */ + public void kill() { + mImpl.kill(); + } + + /** Sets the underlying network used by the IkeSession. */ + public void setNetwork(@NonNull Network network) { + mImpl.setNetwork(network); + } + } } diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java index 7f5b23c9db6f..b9babae4c6b7 100644 --- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java +++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java @@ -21,9 +21,9 @@ import android.content.Context; import android.net.NetworkProvider; import android.net.NetworkRequest; import android.os.Looper; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; -import android.util.SparseArray; import java.util.Objects; import java.util.Set; @@ -40,7 +40,13 @@ public class VcnNetworkProvider extends NetworkProvider { private static final String TAG = VcnNetworkProvider.class.getSimpleName(); private final Set<NetworkRequestListener> mListeners = new ArraySet<>(); - private final SparseArray<NetworkRequestEntry> mRequests = new SparseArray<>(); + + /** + * Cache of NetworkRequest(s), scores and network providers, keyed by NetworkRequest + * + * <p>NetworkRequests are immutable once created, and therefore can be used as stable keys. + */ + private final ArrayMap<NetworkRequest, NetworkRequestEntry> mRequests = new ArrayMap<>(); public VcnNetworkProvider(Context context, Looper looper) { super(context, looper, VcnNetworkProvider.class.getSimpleName()); @@ -51,8 +57,8 @@ public class VcnNetworkProvider extends NetworkProvider { mListeners.add(listener); // Send listener all cached requests - for (int i = 0; i < mRequests.size(); i++) { - notifyListenerForEvent(listener, mRequests.valueAt(i)); + for (NetworkRequestEntry entry : mRequests.values()) { + notifyListenerForEvent(listener, entry); } } @@ -75,7 +81,9 @@ public class VcnNetworkProvider extends NetworkProvider { request, score, providerId)); final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId); - mRequests.put(request.requestId, entry); + + // NetworkRequests are immutable once created, and therefore can be used as stable keys. + mRequests.put(request, entry); // TODO(b/176939047): Intelligently route requests to prioritized VcnInstances (based on // Default Data Sub, or similar) @@ -86,7 +94,7 @@ public class VcnNetworkProvider extends NetworkProvider { @Override public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) { - mRequests.remove(request.requestId); + mRequests.remove(request); } private static class NetworkRequestEntry { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0542ef9b09a4..783037f6f171 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3306,9 +3306,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final ActivityStack stack = r.getRootTask(); - final Task task = stack.getDisplayArea().createStack(stack.getWindowingMode(), - stack.getActivityType(), !ON_TOP, ainfo, intent, - false /* createdByOrganizer */); + final Task task = new ActivityStack(this, stack.getDisplayArea().getNextStackId(), + stack.getActivityType(), ainfo, intent, false /* createdByOrganizer */); if (!mRecentTasks.addToBottom(task)) { // The app has too many tasks already and we can't add any more diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 4f95696dce88..e0db93a01efd 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -168,7 +168,6 @@ cc_defaults { static_libs: [ "android.hardware.broadcastradio@common-utils-1x-lib", - "libservice-connectivity-static", ], product_variables: { diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp index 43f50bfc33d5..729fa71af169 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -99,47 +99,17 @@ static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0); } -static int get_current_max_fd() { - // Not actually guaranteed to be the max, but close enough for our purposes. - int fd = open("/dev/null", O_RDONLY | O_CLOEXEC); - LOG_ALWAYS_FATAL_IF(fd == -1, "failed to open /dev/null: %s", strerror(errno)); - close(fd); - return fd; -} +static void android_server_SystemServer_fdtrackAbort(JNIEnv*, jobject) { + raise(BIONIC_SIGNAL_FDTRACK); -static const char kFdLeakEnableThresholdProperty[] = "persist.sys.debug.fdtrack_enable_threshold"; -static const char kFdLeakAbortThresholdProperty[] = "persist.sys.debug.fdtrack_abort_threshold"; -static const char kFdLeakCheckIntervalProperty[] = "persist.sys.debug.fdtrack_interval"; + // Wait for a bit to allow fdtrack to dump backtraces to logcat. + std::this_thread::sleep_for(5s); -static void android_server_SystemServer_spawnFdLeakCheckThread(JNIEnv*, jobject) { + // Abort on a different thread to avoid ART dumping runtime stacks. std::thread([]() { - pthread_setname_np(pthread_self(), "FdLeakCheckThread"); - bool loaded = false; - while (true) { - const int enable_threshold = GetIntProperty(kFdLeakEnableThresholdProperty, 1024); - const int abort_threshold = GetIntProperty(kFdLeakAbortThresholdProperty, 2048); - const int check_interval = GetIntProperty(kFdLeakCheckIntervalProperty, 120); - int max_fd = get_current_max_fd(); - if (max_fd > enable_threshold && !loaded) { - loaded = true; - ALOGE("fd count above threshold of %d, starting fd backtraces", enable_threshold); - if (dlopen("libfdtrack.so", RTLD_GLOBAL) == nullptr) { - ALOGE("failed to load libfdtrack.so: %s", dlerror()); - } - } else if (max_fd > abort_threshold) { - raise(BIONIC_SIGNAL_FDTRACK); - - // Wait for a bit to allow fdtrack to dump backtraces to logcat. - std::this_thread::sleep_for(5s); - - LOG_ALWAYS_FATAL( - "b/140703823: aborting due to fd leak: check logs for fd " - "backtraces"); - } - - std::this_thread::sleep_for(std::chrono::seconds(check_interval)); - } - }).detach(); + LOG_ALWAYS_FATAL("b/140703823: aborting due to fd leak: check logs for fd " + "backtraces"); + }).join(); } static jlong android_server_SystemServer_startIncrementalService(JNIEnv* env, jclass klass, @@ -161,8 +131,7 @@ static const JNINativeMethod gMethods[] = { {"startHidlServices", "()V", (void*)android_server_SystemServer_startHidlServices}, {"initZygoteChildHeapProfiling", "()V", (void*)android_server_SystemServer_initZygoteChildHeapProfiling}, - {"spawnFdLeakCheckThread", "()V", - (void*)android_server_SystemServer_spawnFdLeakCheckThread}, + {"fdtrackAbort", "()V", (void*)android_server_SystemServer_fdtrackAbort}, {"startIncrementalService", "()J", (void*)android_server_SystemServer_startIncrementalService}, {"setIncrementalServiceSystemReady", "(J)V", diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index 4e1a23416330..a5311f33eb36 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -261,7 +261,7 @@ public: void onDeviceAvailable(const TvInputDeviceInfo& info); void onDeviceUnavailable(int deviceId); - void onStreamConfigurationsChanged(int deviceId); + void onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus); void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded); private: @@ -519,7 +519,7 @@ void JTvInputHal::onDeviceUnavailable(int deviceId) { deviceId); } -void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { +void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) { { Mutex::Autolock autoLock(&mLock); KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); @@ -529,10 +529,8 @@ void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { connections.clear(); } JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mThiz, - gTvInputHalClassInfo.streamConfigsChanged, - deviceId); + env->CallVoidMethod(mThiz, gTvInputHalClassInfo.streamConfigsChanged, deviceId, + cableConnectionStatus); } void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) { @@ -572,7 +570,8 @@ void JTvInputHal::NotifyHandler::handleMessage(const Message& message) { mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId); } break; case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: { - mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId); + int cableConnectionStatus = static_cast<int>(mEvent.deviceInfo.cableConnectionStatus); + mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId, cableConnectionStatus); } break; default: ALOGE("Unrecognizable event"); @@ -688,9 +687,8 @@ int register_android_server_tv_TvInputHal(JNIEnv* env) { "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V"); GET_METHOD_ID( gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V"); - GET_METHOD_ID( - gTvInputHalClassInfo.streamConfigsChanged, clazz, - "streamConfigsChangedFromNative", "(I)V"); + GET_METHOD_ID(gTvInputHalClassInfo.streamConfigsChanged, clazz, + "streamConfigsChangedFromNative", "(II)V"); GET_METHOD_ID( gTvInputHalClassInfo.firstFrameCaptured, clazz, "firstFrameCapturedFromNative", "(II)V"); diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 8cb3e6d1ae73..ccf685c1abd7 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -40,8 +40,6 @@ int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_vr_VrManagerService(JNIEnv* env); int register_android_server_VibratorService(JNIEnv* env); int register_android_server_location_GnssLocationProvider(JNIEnv* env); -int register_android_server_connectivity_Vpn(JNIEnv* env); -int register_android_server_TestNetworkService(JNIEnv* env); int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*); int register_android_server_tv_TvUinputBridge(JNIEnv* env); int register_android_server_tv_TvInputHal(JNIEnv* env); @@ -93,8 +91,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_VibratorService(env); register_android_server_SystemServer(env); register_android_server_location_GnssLocationProvider(env); - register_android_server_connectivity_Vpn(env); - register_android_server_TestNetworkService(env); register_android_server_devicepolicy_CryptoTestHelper(env); register_android_server_ConsumerIrService(env); register_android_server_BatteryStatsService(env); diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp index b7d6424450f3..3690afc1bc41 100644 --- a/services/core/xsd/Android.bp +++ b/services/core/xsd/Android.bp @@ -8,11 +8,19 @@ xsd_config { xsd_config { name: "platform-compat-config", - srcs: ["platform-compat-config.xsd"], - api_dir: "platform-compat-schema", + srcs: ["platform-compat/config/platform-compat-config.xsd"], + api_dir: "platform-compat/config/schema", package_name: "com.android.server.compat.config", } +xsd_config { + name: "platform-compat-overrides", + srcs: ["platform-compat/overrides/platform-compat-overrides.xsd"], + api_dir: "platform-compat/overrides/schema", + package_name: "com.android.server.compat.overrides", + gen_writer: true, +} + xsd_config { name: "display-device-config", diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat-schema/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/services/core/xsd/platform-compat-schema/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/services/core/xsd/platform-compat-schema/OWNERS b/services/core/xsd/platform-compat/OWNERS index f8c3520e9fa8..f8c3520e9fa8 100644 --- a/services/core/xsd/platform-compat-schema/OWNERS +++ b/services/core/xsd/platform-compat/OWNERS diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat/config/platform-compat-config.xsd index 992470816068..a62e2c385766 100644 --- a/services/core/xsd/platform-compat-config.xsd +++ b/services/core/xsd/platform-compat/config/platform-compat-config.xsd @@ -31,6 +31,7 @@ <xs:attribute type="xs:int" name="enableAfterTargetSdk"/> <xs:attribute type="xs:int" name="enableSinceTargetSdk"/> <xs:attribute type="xs:string" name="description"/> + <xs:attribute type="xs:boolean" name="overridable"/> </xs:extension> </xs:simpleContent> </xs:complexType> @@ -48,7 +49,3 @@ </xs:unique> </xs:element> </xs:schema> - - - - diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat/config/schema/current.txt index e3640edd0201..fb8bbefd8374 100644 --- a/services/core/xsd/platform-compat-schema/current.txt +++ b/services/core/xsd/platform-compat/config/schema/current.txt @@ -10,6 +10,7 @@ package com.android.server.compat.config { method public long getId(); method public boolean getLoggingOnly(); method public String getName(); + method public boolean getOverridable(); method public String getValue(); method public void setDescription(String); method public void setDisabled(boolean); @@ -18,6 +19,7 @@ package com.android.server.compat.config { method public void setId(long); method public void setLoggingOnly(boolean); method public void setName(String); + method public void setOverridable(boolean); method public void setValue(String); } diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/config/schema/last_current.txt index e69de29bb2d1..e69de29bb2d1 100644 --- a/services/core/xsd/platform-compat-schema/last_current.txt +++ b/services/core/xsd/platform-compat/config/schema/last_current.txt diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/config/schema/last_removed.txt index e69de29bb2d1..e69de29bb2d1 100644 --- a/services/core/xsd/platform-compat-schema/last_removed.txt +++ b/services/core/xsd/platform-compat/config/schema/last_removed.txt diff --git a/apex/permission/framework/api/removed.txt b/services/core/xsd/platform-compat/config/schema/removed.txt index d802177e249b..d802177e249b 100644 --- a/apex/permission/framework/api/removed.txt +++ b/services/core/xsd/platform-compat/config/schema/removed.txt diff --git a/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd new file mode 100644 index 000000000000..e27e1b8ca89d --- /dev/null +++ b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + --> + +<!-- This defines the format of the XML file used to store compat config overrides in + ~ /data/misc/appcompat/compat_framework_overrides.xml +--> +<xs:schema version="2.0" elementFormDefault="qualified" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + + <xs:complexType name="override-value"> + <xs:attribute type="xs:string" name="packageName" use="required" /> + <xs:attribute type="xs:boolean" name="enabled" use="required" /> + </xs:complexType> + + <xs:complexType name="change-overrides"> + <xs:attribute type="xs:long" name="changeId" use="required"/> + <xs:element name="validated"> + <xs:complexType> + <xs:sequence> + <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" /> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="deferred"> + <xs:complexType> + <xs:sequence> + <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" /> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:complexType> + + <xs:element name="overrides"> + <xs:complexType> + <xs:sequence> + <xs:element name="change-overrides" type="change-overrides" maxOccurs="unbounded" minOccurs="0" /> + </xs:sequence> + </xs:complexType> + </xs:element> +</xs:schema> diff --git a/services/core/xsd/platform-compat/overrides/schema/current.txt b/services/core/xsd/platform-compat/overrides/schema/current.txt new file mode 100644 index 000000000000..08b82072747b --- /dev/null +++ b/services/core/xsd/platform-compat/overrides/schema/current.txt @@ -0,0 +1,51 @@ +// Signature format: 2.0 +package com.android.server.compat.overrides { + + public class ChangeOverrides { + ctor public ChangeOverrides(); + method public long getChangeId(); + method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred(); + method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated(); + method public void setChangeId(long); + method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred); + method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated); + } + + public static class ChangeOverrides.Deferred { + ctor public ChangeOverrides.Deferred(); + method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue(); + } + + public static class ChangeOverrides.Validated { + ctor public ChangeOverrides.Validated(); + method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue(); + } + + public class OverrideValue { + ctor public OverrideValue(); + method public boolean getEnabled(); + method public String getPackageName(); + method public void setEnabled(boolean); + method public void setPackageName(String); + } + + public class Overrides { + ctor public Overrides(); + method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides(); + } + + public class XmlParser { + ctor public XmlParser(); + method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + } + + public class XmlWriter implements java.io.Closeable { + ctor public XmlWriter(java.io.PrintWriter); + method public void close(); + method public static void write(com.android.server.compat.overrides.XmlWriter, com.android.server.compat.overrides.Overrides) throws java.io.IOException; + } + +} + diff --git a/services/core/xsd/platform-compat/overrides/schema/last_current.txt b/services/core/xsd/platform-compat/overrides/schema/last_current.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/platform-compat/overrides/schema/last_current.txt diff --git a/services/core/xsd/platform-compat/overrides/schema/last_removed.txt b/services/core/xsd/platform-compat/overrides/schema/last_removed.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/platform-compat/overrides/schema/last_removed.txt diff --git a/apex/permission/service/api/removed.txt b/services/core/xsd/platform-compat/overrides/schema/removed.txt index d802177e249b..d802177e249b 100644 --- a/apex/permission/service/api/removed.txt +++ b/services/core/xsd/platform-compat/overrides/schema/removed.txt diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 59b24f887f2c..203de9dbcc07 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -23,6 +23,8 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; +import static android.system.OsConstants.O_CLOEXEC; +import static android.system.OsConstants.O_RDONLY; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.utils.TimingsTraceAndSlog.SYSTEM_SERVER_TIMING_TAG; @@ -75,6 +77,8 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.server.ServerProtoEnums; import android.sysprop.VoldProperties; +import android.system.ErrnoException; +import android.system.Os; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.EventLog; @@ -93,6 +97,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.ILockSettings; import com.android.server.am.ActivityManagerService; import com.android.server.appbinding.AppBindingService; +import com.android.server.apphibernation.AppHibernationService; import com.android.server.attention.AttentionManagerService; import com.android.server.audio.AudioService; import com.android.server.biometrics.AuthService; @@ -188,6 +193,7 @@ import dalvik.system.VMRuntime; import com.google.android.startop.iorap.IorapForwardingService; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; import java.util.LinkedList; import java.util.Locale; @@ -215,12 +221,16 @@ public final class SystemServer { "com.android.server.appwidget.AppWidgetService"; private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS = "com.android.server.voiceinteraction.VoiceInteractionManagerService"; + private static final String APP_HIBERNATION_SERVICE_CLASS = + "com.android.server.apphibernation.AppHibernationService"; private static final String PRINT_MANAGER_SERVICE_CLASS = "com.android.server.print.PrintManagerService"; private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS = "com.android.server.companion.CompanionDeviceManagerService"; private static final String STATS_COMPANION_APEX_PATH = "/apex/com.android.os.statsd/javalib/service-statsd.jar"; + private static final String CONNECTIVITY_SERVICE_APEX_PATH = + "/apex/com.android.tethering/javalib/service-connectivity.jar"; private static final String STATS_COMPANION_LIFECYCLE_CLASS = "com.android.server.stats.StatsCompanion$Lifecycle"; private static final String STATS_PULL_ATOM_SERVICE_CLASS = @@ -392,11 +402,71 @@ public final class SystemServer { */ private static native void initZygoteChildHeapProfiling(); + private static final String SYSPROP_FDTRACK_ENABLE_THRESHOLD = + "persist.sys.debug.fdtrack_enable_threshold"; + private static final String SYSPROP_FDTRACK_ABORT_THRESHOLD = + "persist.sys.debug.fdtrack_abort_threshold"; + private static final String SYSPROP_FDTRACK_INTERVAL = + "persist.sys.debug.fdtrack_interval"; + + private static int getMaxFd() { + FileDescriptor fd = null; + try { + fd = Os.open("/dev/null", O_RDONLY | O_CLOEXEC, 0); + return fd.getInt$(); + } catch (ErrnoException ex) { + Slog.e("System", "Failed to get maximum fd: " + ex); + } finally { + if (fd != null) { + try { + Os.close(fd); + } catch (ErrnoException ex) { + // If Os.close threw, something went horribly wrong. + throw new RuntimeException(ex); + } + } + } + + return Integer.MAX_VALUE; + } + + private static native void fdtrackAbort(); /** * Spawn a thread that monitors for fd leaks. */ - private static native void spawnFdLeakCheckThread(); + private static void spawnFdLeakCheckThread() { + final int enableThreshold = SystemProperties.getInt(SYSPROP_FDTRACK_ENABLE_THRESHOLD, 1024); + final int abortThreshold = SystemProperties.getInt(SYSPROP_FDTRACK_ABORT_THRESHOLD, 2048); + final int checkInterval = SystemProperties.getInt(SYSPROP_FDTRACK_INTERVAL, 120); + + new Thread(() -> { + boolean enabled = false; + while (true) { + int maxFd = getMaxFd(); + if (maxFd > enableThreshold) { + // Do a manual GC to clean up fds that are hanging around as garbage. + System.gc(); + maxFd = getMaxFd(); + } + + if (maxFd > enableThreshold && !enabled) { + Slog.i("System", "fdtrack enable threshold reached, enabling"); + System.loadLibrary("fdtrack"); + enabled = true; + } else if (maxFd > abortThreshold) { + Slog.i("System", "fdtrack abort threshold reached, dumping and aborting"); + fdtrackAbort(); + } + + try { + Thread.sleep(checkInterval * 1000); + } catch (InterruptedException ex) { + continue; + } + } + }).start(); + } /** * Start native Incremental Service and get its handle. @@ -1463,15 +1533,6 @@ public final class SystemServer { } t.traceEnd(); - t.traceBegin("StartVcnManagementService"); - try { - vcnManagement = VcnManagementService.create(context); - ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement); - } catch (Throwable e) { - reportWtf("starting VCN Management Service", e); - } - t.traceEnd(); - t.traceBegin("StartTextServicesManager"); mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); t.traceEnd(); @@ -1561,14 +1622,23 @@ public final class SystemServer { // This has to be called after NetworkManagementService, NetworkStatsService // and NetworkPolicyManager because ConnectivityService needs to take these // services to initialize. - // TODO: Dynamically load service-connectivity.jar by using startServiceFromJar. - mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_CLASS); + mSystemServiceManager.startServiceFromJar(CONNECTIVITY_SERVICE_INITIALIZER_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); connectivity = IConnectivityManager.Stub.asInterface( ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); // TODO: Use ConnectivityManager instead of ConnectivityService. networkPolicy.bindConnectivityManager(connectivity); t.traceEnd(); + t.traceBegin("StartVcnManagementService"); + try { + vcnManagement = VcnManagementService.create(context); + ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement); + } catch (Throwable e) { + reportWtf("starting VCN Management Service", e); + } + t.traceEnd(); + t.traceBegin("StartNsdService"); try { serviceDiscovery = NsdService.create(context); @@ -1796,6 +1866,12 @@ public final class SystemServer { mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS); t.traceEnd(); + if (AppHibernationService.isAppHibernationEnabled()) { + t.traceBegin("StartAppHibernationService"); + mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS); + t.traceEnd(); + } + if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) { t.traceBegin("StartGestureLauncher"); mSystemServiceManager.startService(GestureLauncherService.class); @@ -2339,15 +2415,6 @@ public final class SystemServer { reportWtf("making IpSec Service ready", e); } t.traceEnd(); - t.traceBegin("MakeVcnManagementServiceReady"); - try { - if (vcnManagementF != null) { - vcnManagementF.systemReady(); - } - } catch (Throwable e) { - reportWtf("making VcnManagementService ready", e); - } - t.traceEnd(); t.traceBegin("MakeNetworkStatsServiceReady"); try { if (networkStatsF != null) { @@ -2366,6 +2433,15 @@ public final class SystemServer { reportWtf("making Connectivity Service ready", e); } t.traceEnd(); + t.traceBegin("MakeVcnManagementServiceReady"); + try { + if (vcnManagementF != null) { + vcnManagementF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making VcnManagementService ready", e); + } + t.traceEnd(); t.traceBegin("MakeNetworkPolicyServiceReady"); try { if (networkPolicyF != null) { diff --git a/services/searchui/OWNERS b/services/searchui/OWNERS new file mode 100644 index 000000000000..92835c2b0626 --- /dev/null +++ b/services/searchui/OWNERS @@ -0,0 +1,2 @@ +hyunyoungs@google.com +sfufa@google.com diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java new file mode 100644 index 000000000000..45bca6829553 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -0,0 +1,197 @@ +/* + * 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.apphibernation; + +import static org.junit.Assert.assertTrue; +import static org.mockito.AdditionalAnswers.returnsArgAt; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +import android.app.IActivityManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.ParceledListSlice; +import android.content.pm.UserInfo; +import android.net.Uri; +import android.os.RemoteException; +import android.os.UserManager; + +import androidx.test.filters.SmallTest; + +import com.android.server.SystemService; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for {@link com.android.server.apphibernation.AppHibernationService} + */ +@SmallTest +public final class AppHibernationServiceTest { + private static final String PACKAGE_SCHEME = "package"; + private static final String PACKAGE_NAME_1 = "package1"; + private static final String PACKAGE_NAME_2 = "package2"; + private static final int USER_ID_1 = 1; + private static final int USER_ID_2 = 2; + + private final List<UserInfo> mUserInfos = new ArrayList<>(); + + private AppHibernationService mAppHibernationService; + private BroadcastReceiver mBroadcastReceiver; + @Mock + private Context mContext; + @Mock + private IPackageManager mIPackageManager; + @Mock + private IActivityManager mIActivityManager; + @Mock + private UserManager mUserManager; + @Captor + private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt()); + + mAppHibernationService = new AppHibernationService(mContext, mIPackageManager, + mIActivityManager, mUserManager); + + verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any()); + mBroadcastReceiver = mReceiverCaptor.getValue(); + + doReturn(mUserInfos).when(mUserManager).getUsers(); + + doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(), + anyInt(), anyBoolean(), anyBoolean(), any(), any()); + + addUser(USER_ID_1); + mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + } + + @Test + public void testSetHibernatingForUser_packageIsHibernating() throws RemoteException { + // WHEN we hibernate a package for a user + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true); + + // THEN the package is marked hibernating for the user + assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1)); + } + + @Test + public void testSetHibernatingForUser_newPackageAdded_packageIsHibernating() + throws RemoteException { + // WHEN a new package is added and it is hibernated + Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED, + Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */)); + intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1); + mBroadcastReceiver.onReceive(mContext, intent); + + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_1, true); + + // THEN the new package is hibernated + assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_2, USER_ID_1)); + } + + @Test + public void testSetHibernatingForUser_newUserAdded_packageIsHibernating() + throws RemoteException { + // WHEN a new user is added and a package from the user is hibernated + List<PackageInfo> userPackages = new ArrayList<>(); + userPackages.add(makePackageInfo(PACKAGE_NAME_1)); + doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager) + .getInstalledPackages(anyInt(), eq(USER_ID_2)); + Intent intent = new Intent(Intent.ACTION_USER_ADDED); + intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2); + mBroadcastReceiver.onReceive(mContext, intent); + + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true); + + // THEN the new user's package is hibernated + assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_2)); + } + + @Test + public void testIsHibernatingForUser_packageReplaced_stillReturnsHibernating() + throws RemoteException { + // GIVEN a package is currently hibernated + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true); + + // WHEN the package is removed but marked as replacing + Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED, + Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */)); + intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1); + intent.putExtra(Intent.EXTRA_REPLACING, true); + mBroadcastReceiver.onReceive(mContext, intent); + + // THEN the package is still hibernating + assertTrue(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1)); + } + + @Test + public void testSetHibernatingGlobally_packageIsHibernatingGlobally() throws RemoteException { + // WHEN we hibernate a package + mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true); + + // THEN the package is marked hibernating for the user + assertTrue(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1)); + } + + /** + * Add a mock user with one package. Must be called before + * {@link AppHibernationService#onBootPhase(int)} to work properly. + */ + private void addUser(int userId) throws RemoteException { + addUser(userId, new String[]{PACKAGE_NAME_1}); + } + + /** + * Add a mock user with the packages specified. Must be called before + * {@link AppHibernationService#onBootPhase(int)} to work properly + */ + private void addUser(int userId, String[] packageNames) throws RemoteException { + mUserInfos.add(new UserInfo(userId, "user_" + userId, 0 /* flags */)); + List<PackageInfo> userPackages = new ArrayList<>(); + for (String pkgName : packageNames) { + userPackages.add(makePackageInfo(pkgName)); + } + doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager) + .getInstalledPackages(anyInt(), eq(userId)); + } + + private static PackageInfo makePackageInfo(String packageName) { + PackageInfo pkg = new PackageInfo(); + pkg.packageName = packageName; + return pkg; + } +} diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS b/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS new file mode 100644 index 000000000000..c2e27e084c8c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/apphibernation/OWNERS @@ -0,0 +1 @@ +include /core/java/android/apphibernation/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java index 4f4aa3f16f09..f00edcc85404 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -40,78 +40,83 @@ class CompatConfigBuilder { } CompatConfigBuilder addEnableAfterSdkChangeWithId(int sdk, long id) { - mChanges.add(new CompatChange(id, "", sdk, -1, false, false, "")); + mChanges.add(new CompatChange(id, "", sdk, -1, false, false, "", false)); return this; } CompatConfigBuilder addEnableAfterSdkChangeWithIdAndName(int sdk, long id, String name) { - mChanges.add(new CompatChange(id, name, sdk, -1, false, false, "")); + mChanges.add(new CompatChange(id, name, sdk, -1, false, false, "", false)); return this; } CompatConfigBuilder addEnableAfterSdkChangeWithIdDefaultDisabled(int sdk, long id) { - mChanges.add(new CompatChange(id, "", sdk, -1, true, false, "")); + mChanges.add(new CompatChange(id, "", sdk, -1, true, false, "", false)); return this; } CompatConfigBuilder addEnableAfterSdkChangeWithIdAndDescription(int sdk, long id, String description) { - mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description)); + mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description, false)); return this; } CompatConfigBuilder addEnableSinceSdkChangeWithId(int sdk, long id) { - mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "")); + mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false)); return this; } CompatConfigBuilder addEnableSinceSdkChangeWithIdAndName(int sdk, long id, String name) { - mChanges.add(new CompatChange(id, name, -1, sdk, false, false, "")); + mChanges.add(new CompatChange(id, name, -1, sdk, false, false, "", false)); return this; } CompatConfigBuilder addEnableSinceSdkChangeWithIdDefaultDisabled(int sdk, long id) { - mChanges.add(new CompatChange(id, "", -1, sdk, true, false, "")); + mChanges.add(new CompatChange(id, "", -1, sdk, true, false, "", false)); return this; } CompatConfigBuilder addEnableSinceSdkChangeWithIdAndDescription(int sdk, long id, String description) { - mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description)); + mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description, false)); return this; } CompatConfigBuilder addEnabledChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, -1, false, false, "")); + mChanges.add(new CompatChange(id, "", -1, -1, false, false, "", false)); return this; } CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) { - mChanges.add(new CompatChange(id, name, -1, -1, false, false, "")); + mChanges.add(new CompatChange(id, name, -1, -1, false, false, "", false)); return this; } CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) { - mChanges.add(new CompatChange(id, "", -1, -1, false, false, description)); + mChanges.add(new CompatChange(id, "", -1, -1, false, false, description, false)); return this; } CompatConfigBuilder addDisabledChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, -1, true, false, "")); + mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", false)); return this; } CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) { - mChanges.add(new CompatChange(id, name, -1, -1, true, false, "")); + mChanges.add(new CompatChange(id, name, -1, -1, true, false, "", false)); return this; } CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) { - mChanges.add(new CompatChange(id, "", -1, -1, true, false, description)); + mChanges.add(new CompatChange(id, "", -1, -1, true, false, description, false)); return this; } CompatConfigBuilder addLoggingOnlyChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, -1, false, true, "")); + mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", false)); + return this; + } + + CompatConfigBuilder addOverridableChangeWithId(long id) { + mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true)); return this; } diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index ac8dc341999a..a53ff9bc7fdc 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -44,6 +44,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.UUID; @RunWith(AndroidJUnit4.class) @@ -69,6 +71,10 @@ public class CompatConfigTest { os.close(); } + private String readFile(File file) throws IOException { + return new String(Files.readAllBytes(Paths.get(file.toURI()))); + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -499,4 +505,86 @@ public class CompatConfigTest { assertThat(compatConfig.isChangeEnabled(1236L, ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); } + + @Test + public void testSaveOverrides() throws Exception { + File overridesFile = new File(createTempDir(), "overrides.xml"); + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1L) + .addEnableSinceSdkChangeWithId(2, 2L) + .build(); + compatConfig.forceNonDebuggableFinalForTest(true); + compatConfig.initOverrides(overridesFile); + when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName("foo.bar") + .debuggable() + .build()); + when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt())) + .thenThrow(new NameNotFoundException()); + + compatConfig.addOverride(1L, "foo.bar", true); + compatConfig.addOverride(2L, "bar.baz", false); + + assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<overrides>\n" + + " <change-overrides changeId=\"1\">\n" + + " <validated>\n" + + " <override-value packageName=\"foo.bar\" enabled=\"true\">\n" + + " </override-value>\n" + + " </validated>\n" + + " <deferred>\n" + + " </deferred>\n" + + " </change-overrides>\n" + + " <change-overrides changeId=\"2\">\n" + + " <validated>\n" + + " </validated>\n" + + " <deferred>\n" + + " <override-value packageName=\"bar.baz\" enabled=\"false\">\n" + + " </override-value>\n" + + " </deferred>\n" + + " </change-overrides>\n" + + "</overrides>\n"); + } + + @Test + public void testLoadOverrides() throws Exception { + File tempDir = createTempDir(); + File overridesFile = new File(tempDir, "overrides.xml"); + // Change 1 is enabled for foo.bar (validated) + // Change 2 is disabled for bar.baz (deferred) + String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + + "<overrides>" + + "<change-overrides changeId=\"1\">" + + "<deferred/>" + + "<validated>" + + "<override-value packageName=\"foo.bar\" enabled=\"true\"/>" + + "</validated>" + + "</change-overrides>" + + "<change-overrides changeId=\"2\">" + + "<deferred>" + + "<override-value packageName=\"bar.baz\" enabled=\"false\"/>" + + "</deferred>" + + "<validated/>" + + "</change-overrides>" + + "</overrides>"; + writeToFile(tempDir, "overrides.xml", xmlData); + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addDisabledChangeWithId(1L) + .addEnableSinceSdkChangeWithId(2, 2L) + .build(); + compatConfig.forceNonDebuggableFinalForTest(true); + compatConfig.initOverrides(overridesFile); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("foo.bar") + .debuggable() + .build(); + when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt())) + .thenReturn(applicationInfo); + when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt())) + .thenThrow(new NameNotFoundException()); + + assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue(); + assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse(); + } } diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java index a70c51045340..a1b2dc8bd82d 100644 --- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java @@ -97,17 +97,22 @@ public class PlatformCompatTest { .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L) .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L) .addLoggingOnlyChangeWithId(7L) + .addOverridableChangeWithId(8L) .build(); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly( - new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""), - new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""), + new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false), + new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false), new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, -1, false, false, - "desc"), - new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""), - new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""), - new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, -1, false, false, ""), - new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "")); + "desc", false), + new CompatibilityChangeInfo( + 4L, "", Build.VERSION_CODES.P, -1, false, false, "", false), + new CompatibilityChangeInfo( + 5L, "", Build.VERSION_CODES.Q, -1, false, false, "", false), + new CompatibilityChangeInfo( + 6L, "", Build.VERSION_CODES.R, -1, false, false, "", false), + new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false), + new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true)); } @Test @@ -123,12 +128,12 @@ public class PlatformCompatTest { .build(); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly( - new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""), - new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""), - new CompatibilityChangeInfo(5L, "", /*enableAfter*/ -1, - /*enableSince*/ Build.VERSION_CODES.Q, false, false, ""), - new CompatibilityChangeInfo(6L, "", /*enableAfter*/ -1, - /*enableSince*/ Build.VERSION_CODES.R, false, false, "")); + new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false), + new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false), + new CompatibilityChangeInfo( + 5L, "", Build.VERSION_CODES.P, -1, false, false, "", false), + new CompatibilityChangeInfo( + 6L, "", Build.VERSION_CODES.Q, -1, false, false, "", false)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 6786f6014f2d..5d06da78fe80 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -30,6 +30,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; import static android.app.admin.DevicePolicyManager.WIPE_EUICC; import static android.app.admin.PasswordMetrics.computeForPassword; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; +import static android.net.InetAddresses.parseNumericAddress; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; @@ -65,6 +66,8 @@ import static org.mockito.Mockito.when; import static org.mockito.hamcrest.MockitoHamcrest.argThat; import static org.testng.Assert.assertThrows; +import static java.util.Collections.emptyList; + import android.Manifest.permission; import android.app.Activity; import android.app.AppOpsManager; @@ -118,6 +121,8 @@ import org.mockito.internal.util.collections.Sets; import org.mockito.stubbing.Answer; import java.io.File; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -2246,6 +2251,48 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts); } + public void testGetProxyParameters() throws Exception { + assertThat(dpm.getProxyParameters(inetAddrProxy("192.0.2.1", 1234), emptyList())) + .isEqualTo(new Pair<>("192.0.2.1:1234", "")); + assertThat(dpm.getProxyParameters(inetAddrProxy("192.0.2.1", 1234), + listOf("one.example.com ", " two.example.com "))) + .isEqualTo(new Pair<>("192.0.2.1:1234", "one.example.com,two.example.com")); + assertThat(dpm.getProxyParameters(hostnameProxy("proxy.example.com", 1234), emptyList())) + .isEqualTo(new Pair<>("proxy.example.com:1234", "")); + assertThat(dpm.getProxyParameters(hostnameProxy("proxy.example.com", 1234), + listOf("excluded.example.com"))) + .isEqualTo(new Pair<>("proxy.example.com:1234", "excluded.example.com")); + + assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters( + inetAddrProxy("192.0.2.1", 0), emptyList())); + assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters( + hostnameProxy("", 1234), emptyList())); + assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters( + hostnameProxy("", 0), emptyList())); + assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters( + hostnameProxy("invalid! hostname", 1234), emptyList())); + assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters( + hostnameProxy("proxy.example.com", 1234), listOf("invalid exclusion"))); + assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters( + hostnameProxy("proxy.example.com", -1), emptyList())); + assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters( + hostnameProxy("proxy.example.com", 0xFFFF + 1), emptyList())); + } + + private static Proxy inetAddrProxy(String inetAddr, int port) { + return new Proxy( + Proxy.Type.HTTP, new InetSocketAddress(parseNumericAddress(inetAddr), port)); + } + + private static Proxy hostnameProxy(String hostname, int port) { + return new Proxy( + Proxy.Type.HTTP, InetSocketAddress.createUnresolved(hostname, port)); + } + + private static List<String> listOf(String... args) { + return Arrays.asList(args); + } + public void testSetKeyguardDisabledFeaturesWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -5156,7 +5203,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Attempt to set to empty list (which means no listener is allowlisted) mContext.binder.callingUid = adminUid; assertFalse(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.emptyList())); + admin1, emptyList())); assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1)); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; @@ -5248,7 +5295,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Setting an empty allowlist - only system listeners allowed mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; assertTrue(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.emptyList())); + admin1, emptyList())); assertEquals(0, dpms.getPermittedCrossProfileNotificationListeners(admin1).size()); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; @@ -5312,7 +5359,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // all allowed in primary profile mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; assertTrue(dpms.setPermittedCrossProfileNotificationListeners( - admin1, Collections.emptyList())); + admin1, emptyList())); assertEquals(0, dpms.getPermittedCrossProfileNotificationListeners(admin1).size()); mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java index dd98c4b09b78..09dd3e3e50db 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java @@ -538,6 +538,15 @@ public class HdmiCecLocalDeviceAudioSystemTest { } @Test + public void setArcStatus() { + mHdmiCecLocalDeviceAudioSystem.setArcStatus(true); + assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue(); + + mHdmiCecLocalDeviceAudioSystem.setArcStatus(false); + assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse(); + } + + @Test @Ignore("b/151150320") public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() { HdmiCecMessage message = diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS deleted file mode 100644 index 28aff188dbd8..000000000000 --- a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 847766 -nfuller@google.com -include /core/java/android/app/timedetector/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java index 1581d9ac1811..691d174f55f8 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java @@ -82,6 +82,11 @@ public class LockSettingsStorageTestable extends LockSettingsStorage { } @Override + String getRebootEscrowServerBlob() { + return makeDirs(mStorageDir, super.getRebootEscrowServerBlob()).getAbsolutePath(); + } + + @Override protected File getSyntheticPasswordDirectoryForUser(int userId) { return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser( userId).getAbsolutePath()); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java index 46f43e7af596..32445fd1a47d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java @@ -19,22 +19,44 @@ package com.android.server.locksettings; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; + import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.security.GeneralSecurityException; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + /** * atest FrameworksServicesTests:RebootEscrowDataTest */ @RunWith(AndroidJUnit4.class) public class RebootEscrowDataTest { private RebootEscrowKey mKey; + private SecretKey mKeyStoreEncryptionKey; + + private SecretKey generateNewRebootEscrowEncryptionKey() throws GeneralSecurityException { + KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); + generator.init(new KeyGenParameterSpec.Builder( + "reboot_escrow_data_test_key", + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setKeySize(256) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + return generator.generateKey(); + } @Before public void generateKey() throws Exception { mKey = RebootEscrowKey.generate(); + mKeyStoreEncryptionKey = generateNewRebootEscrowEncryptionKey(); } private static byte[] getTestSp() { @@ -47,36 +69,49 @@ public class RebootEscrowDataTest { @Test(expected = NullPointerException.class) public void fromEntries_failsOnNull() throws Exception { - RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, null); + RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, null, mKeyStoreEncryptionKey); } @Test(expected = NullPointerException.class) public void fromEncryptedData_failsOnNullData() throws Exception { byte[] testSp = getTestSp(); - RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp); + RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp, + mKeyStoreEncryptionKey); RebootEscrowKey key = RebootEscrowKey.fromKeyBytes(expected.getKey().getKeyBytes()); - RebootEscrowData.fromEncryptedData(key, null); + RebootEscrowData.fromEncryptedData(key, null, mKeyStoreEncryptionKey); } @Test(expected = NullPointerException.class) public void fromEncryptedData_failsOnNullKey() throws Exception { byte[] testSp = getTestSp(); - RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp); - RebootEscrowData.fromEncryptedData(null, expected.getBlob()); + RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp, + mKeyStoreEncryptionKey); + RebootEscrowData.fromEncryptedData(null, expected.getBlob(), mKeyStoreEncryptionKey); } @Test public void fromEntries_loopback_success() throws Exception { byte[] testSp = getTestSp(); - RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp); + RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword(mKey, (byte) 2, testSp, + mKeyStoreEncryptionKey); RebootEscrowKey key = RebootEscrowKey.fromKeyBytes(expected.getKey().getKeyBytes()); - RebootEscrowData actual = RebootEscrowData.fromEncryptedData(key, expected.getBlob()); + RebootEscrowData actual = RebootEscrowData.fromEncryptedData(key, expected.getBlob(), + mKeyStoreEncryptionKey); assertThat(actual.getSpVersion(), is(expected.getSpVersion())); - assertThat(actual.getIv(), is(expected.getIv())); assertThat(actual.getKey().getKeyBytes(), is(expected.getKey().getKeyBytes())); assertThat(actual.getBlob(), is(expected.getBlob())); assertThat(actual.getSyntheticPassword(), is(expected.getSyntheticPassword())); } + + @Test + public void aesEncryptedBlob_loopback_success() throws Exception { + byte[] testSp = getTestSp(); + byte [] encrypted = AesEncryptionUtil.encrypt(mKeyStoreEncryptionKey, testSp); + byte [] decrypted = AesEncryptionUtil.decrypt(mKeyStoreEncryptionKey, encrypted); + + assertThat(decrypted, is(testSp)); + } + } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index 98d64524d87a..a4ba4c86a8fd 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; @@ -52,6 +53,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.widget.RebootEscrowListener; +import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection; import org.junit.Before; import org.junit.Test; @@ -61,6 +63,9 @@ import org.mockito.ArgumentCaptor; import java.io.File; import java.util.ArrayList; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + @SmallTest @Presubmit @RunWith(AndroidJUnit4.class) @@ -77,15 +82,26 @@ public class RebootEscrowManagerTests { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, }; + // Hex encoding of a randomly generated AES key for test. + private static final byte[] TEST_AES_KEY = new byte[] { + 0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31, + 0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61, + 0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09, + 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23, + }; + private Context mContext; private UserManager mUserManager; private RebootEscrowManager.Callbacks mCallbacks; private IRebootEscrow mRebootEscrow; + private ResumeOnRebootServiceConnection mServiceConnection; + private RebootEscrowKeyStoreManager mKeyStoreManager; LockSettingsStorageTestable mStorage; private MockableRebootEscrowInjected mInjected; private RebootEscrowManager mService; + private SecretKey mAesKey; public interface MockableRebootEscrowInjected { int getBootCount(); @@ -95,16 +111,20 @@ public class RebootEscrowManagerTests { static class MockInjector extends RebootEscrowManager.Injector { private final IRebootEscrow mRebootEscrow; + private final ResumeOnRebootServiceConnection mServiceConnection; private final RebootEscrowProviderInterface mRebootEscrowProvider; private final UserManager mUserManager; private final MockableRebootEscrowInjected mInjected; + private final RebootEscrowKeyStoreManager mKeyStoreManager; MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow, + RebootEscrowKeyStoreManager keyStoreManager, + LockSettingsStorageTestable storage, MockableRebootEscrowInjected injected) { - super(context); + super(context, storage); mRebootEscrow = rebootEscrow; - + mServiceConnection = null; RebootEscrowProviderHalImpl.Injector halInjector = new RebootEscrowProviderHalImpl.Injector() { @Override @@ -114,6 +134,23 @@ public class RebootEscrowManagerTests { }; mRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector); mUserManager = userManager; + mKeyStoreManager = keyStoreManager; + mInjected = injected; + } + + MockInjector(Context context, UserManager userManager, + ResumeOnRebootServiceConnection serviceConnection, + RebootEscrowKeyStoreManager keyStoreManager, + LockSettingsStorageTestable storage, + MockableRebootEscrowInjected injected) { + super(context, storage); + mServiceConnection = serviceConnection; + mRebootEscrow = null; + RebootEscrowProviderServerBasedImpl.Injector injector = + new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection); + mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector); + mUserManager = userManager; + mKeyStoreManager = keyStoreManager; mInjected = injected; } @@ -128,6 +165,11 @@ public class RebootEscrowManagerTests { } @Override + public RebootEscrowKeyStoreManager getKeyStoreManager() { + return mKeyStoreManager; + } + + @Override public int getBootCount() { return mInjected.getBootCount(); } @@ -144,6 +186,12 @@ public class RebootEscrowManagerTests { mUserManager = mock(UserManager.class); mCallbacks = mock(RebootEscrowManager.Callbacks.class); mRebootEscrow = mock(IRebootEscrow.class); + mServiceConnection = mock(ResumeOnRebootServiceConnection.class); + mKeyStoreManager = mock(RebootEscrowKeyStoreManager.class); + mAesKey = new SecretKeySpec(TEST_AES_KEY, "AES"); + + when(mKeyStoreManager.getKeyStoreEncryptionKey()).thenReturn(mAesKey); + when(mKeyStoreManager.generateKeyStoreEncryptionKeyIfNeeded()).thenReturn(mAesKey); mStorage = new LockSettingsStorageTestable(mContext, new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings")); @@ -160,7 +208,12 @@ public class RebootEscrowManagerTests { when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true); mInjected = mock(MockableRebootEscrowInjected.class); mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow, - mInjected), mCallbacks, mStorage); + mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage); + } + + private void setServerBasedRebootEscrowProvider() throws Exception { + mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, + mServiceConnection, mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage); } @Test @@ -176,6 +229,19 @@ public class RebootEscrowManagerTests { } @Test + public void prepareRebootEscrowServerBased_Success() throws Exception { + setServerBasedRebootEscrowProvider(); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + assertFalse(mStorage.hasRebootEscrowServerBlob()); + } + + @Test public void prepareRebootEscrow_ClearCredentials_Success() throws Exception { RebootEscrowListener mockListener = mock(RebootEscrowListener.class); mService.setRebootEscrowListener(mockListener); @@ -213,9 +279,32 @@ public class RebootEscrowManagerTests { assertNotNull( mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); verify(mRebootEscrow).storeKey(any()); + verify(mKeyStoreManager).getKeyStoreEncryptionKey(); + + assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); + assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID)); + } + + @Test + public void armServiceServerBased_Success() throws Exception { + setServerBasedRebootEscrowProvider(); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertTrue(mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID)); + assertTrue(mStorage.hasRebootEscrowServerBlob()); } @Test @@ -300,6 +389,7 @@ public class RebootEscrowManagerTests { ArgumentCaptor<byte[]> keyByteCaptor = ArgumentCaptor.forClass(byte[].class); assertTrue(mService.armRebootEscrowIfNeeded()); verify(mRebootEscrow).storeKey(keyByteCaptor.capture()); + verify(mKeyStoreManager).getKeyStoreEncryptionKey(); assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID)); @@ -314,6 +404,41 @@ public class RebootEscrowManagerTests { mService.loadRebootEscrowDataIfAvailable(); verify(mRebootEscrow).retrieveKey(); assertTrue(metricsSuccessCaptor.getValue()); + verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + } + + @Test + public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception { + setServerBasedRebootEscrowProvider(); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertTrue(mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture()); + + when(mServiceConnection.unwrap(any(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + mService.loadRebootEscrowDataIfAvailable(); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertTrue(metricsSuccessCaptor.getValue()); + verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java new file mode 100644 index 000000000000..bc1e025dd99f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java @@ -0,0 +1,145 @@ +/* + * 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.locksettings; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.stubbing.Answer; + +import java.io.File; +import java.io.IOException; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class RebootEscrowProviderServerBasedImplTests { + private SecretKey mKeyStoreEncryptionKey; + private RebootEscrowKey mRebootEscrowKey; + private ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection mServiceConnection; + private LockSettingsStorageTestable mStorage; + private RebootEscrowProviderServerBasedImpl mRebootEscrowProvider; + private Answer<byte[]> mFakeEncryption; + + private static final byte[] TEST_AES_KEY = new byte[] { + 0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31, + 0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61, + 0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09, + 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23, + }; + + @Before + public void setUp() throws Exception { + mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES"); + mRebootEscrowKey = RebootEscrowKey.generate(); + mServiceConnection = mock( + ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection.class); + + Context context = new ContextWrapper(InstrumentationRegistry.getContext()); + mStorage = new LockSettingsStorageTestable(context, + new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings")); + mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mStorage, + new RebootEscrowProviderServerBasedImpl.Injector(mServiceConnection)); + + mFakeEncryption = invocation -> { + byte[] secret = invocation.getArgument(0); + for (int i = 0; i < secret.length; i++) { + secret[i] = (byte) (secret[i] ^ 0xf); + } + return secret; + }; + } + + @Test + public void getAndClearRebootEscrowKey_loopback_success() throws Exception { + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption); + when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(mFakeEncryption); + + assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport()); + mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + + RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey( + mKeyStoreEncryptionKey); + assertThat(ks.getKeyBytes(), is(mRebootEscrowKey.getKeyBytes())); + assertFalse(mStorage.hasRebootEscrowServerBlob()); + } + + @Test + public void getAndClearRebootEscrowKey_WrongDecryptionMethod_failure() throws Exception { + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption); + when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer( + invocation -> { + byte[] secret = invocation.getArgument(0); + for (int i = 0; i < secret.length; i++) { + secret[i] = (byte) (secret[i] ^ 0xe); + } + return secret; + } + ); + + assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport()); + mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // Expect to get wrong key bytes + RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey( + mKeyStoreEncryptionKey); + assertNotEquals(ks.getKeyBytes(), mRebootEscrowKey.getKeyBytes()); + assertFalse(mStorage.hasRebootEscrowServerBlob()); + } + + @Test + public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception { + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption); + doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong()); + + assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport()); + mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // Expect to get null key bytes when the server service fails to unwrap the blob. + RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey( + mKeyStoreEncryptionKey); + assertNull(ks); + assertFalse(mStorage.hasRebootEscrowServerBlob()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java b/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java new file mode 100644 index 000000000000..b9af82b64c02 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java @@ -0,0 +1,111 @@ +/* + * 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.locksettings; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.service.resumeonreboot.ResumeOnRebootService; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@SmallTest +@RunWith(JUnit4.class) +public class ResumeOnRebootServiceProviderTests { + + @Mock + Context mMockContext; + @Mock + PackageManager mMockPackageManager; + @Mock + ResolveInfo mMockResolvedInfo; + @Mock + ServiceInfo mMockServiceInfo; + @Mock + ComponentName mMockComponentName; + @Captor + ArgumentCaptor<Intent> mIntentArgumentCaptor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mMockContext.getUserId()).thenReturn(0); + when(mMockResolvedInfo.serviceInfo).thenReturn(mMockServiceInfo); + when(mMockServiceInfo.getComponentName()).thenReturn(mMockComponentName); + } + + @Test + public void noServiceFound() throws Exception { + when(mMockPackageManager.queryIntentServices(any(), + eq(PackageManager.MATCH_SYSTEM_ONLY))).thenReturn( + null); + assertThat(new ResumeOnRebootServiceProvider(mMockContext, + mMockPackageManager).getServiceConnection()).isNull(); + } + + @Test + public void serviceNotGuardedWithPermission() throws Exception { + ArrayList<ResolveInfo> resultList = new ArrayList<>(); + when(mMockServiceInfo.permission).thenReturn(""); + resultList.add(mMockResolvedInfo); + when(mMockPackageManager.queryIntentServices(any(), any())).thenReturn( + resultList); + assertThat(new ResumeOnRebootServiceProvider(mMockContext, + mMockPackageManager).getServiceConnection()).isNull(); + } + + @Test + public void serviceResolved() throws Exception { + ArrayList<ResolveInfo> resultList = new ArrayList<>(); + resultList.add(mMockResolvedInfo); + when(mMockServiceInfo.permission).thenReturn( + Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE); + when(mMockPackageManager.queryIntentServices(any(), + eq(PackageManager.MATCH_SYSTEM_ONLY))).thenReturn( + resultList); + + assertThat(new ResumeOnRebootServiceProvider(mMockContext, + mMockPackageManager).getServiceConnection()).isNotNull(); + + verify(mMockPackageManager).queryIntentServices(mIntentArgumentCaptor.capture(), + eq(PackageManager.MATCH_SYSTEM_ONLY)); + assertThat(mIntentArgumentCaptor.getValue().getAction()).isEqualTo( + ResumeOnRebootService.SERVICE_INTERFACE); + } +} 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 fec0273383e7..df19aeb13707 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -16,13 +16,18 @@ package com.android.server.net; +import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; +import static android.Manifest.permission.NETWORK_STACK; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED; +import static android.net.INetd.FIREWALL_RULE_ALLOW; 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; +import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; @@ -34,6 +39,7 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.uidPoliciesToString; import static android.net.NetworkPolicyManager.uidRulesToString; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.TAG_ALL; @@ -74,6 +80,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -97,6 +104,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; +import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; @@ -123,6 +131,7 @@ import android.os.RemoteException; import android.os.SimpleClock; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; @@ -131,6 +140,7 @@ import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.MediumTest; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.DataUnit; import android.util.Log; import android.util.Pair; @@ -187,6 +197,7 @@ import java.util.Calendar; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -240,6 +251,7 @@ public class NetworkPolicyManagerServiceTest { private @Mock SubscriptionManager mSubscriptionManager; private @Mock CarrierConfigManager mCarrierConfigManager; private @Mock TelephonyManager mTelephonyManager; + private @Mock UserManager mUserManager; private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor = ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); @@ -351,6 +363,8 @@ public class NetworkPolicyManagerServiceTest { return mNotifManager; case Context.CONNECTIVITY_SERVICE: return mConnectivityManager; + case Context.USER_SERVICE: + return mUserManager; default: return super.getSystemService(name); } @@ -407,11 +421,14 @@ public class NetworkPolicyManagerServiceTest { when(mPackageManager.getPackagesForUid(UID_B)).thenReturn(new String[] {PKG_NAME_B}); when(mPackageManager.getPackagesForUid(UID_C)).thenReturn(new String[] {PKG_NAME_C}); when(mPackageManager.getApplicationInfo(eq(PKG_NAME_A), anyInt())) - .thenReturn(buildApplicationInfo(PKG_NAME_A)); + .thenReturn(buildApplicationInfo(PKG_NAME_A, UID_A)); when(mPackageManager.getApplicationInfo(eq(PKG_NAME_B), anyInt())) - .thenReturn(buildApplicationInfo(PKG_NAME_B)); + .thenReturn(buildApplicationInfo(PKG_NAME_B, UID_B)); when(mPackageManager.getApplicationInfo(eq(PKG_NAME_C), anyInt())) - .thenReturn(buildApplicationInfo(PKG_NAME_C)); + .thenReturn(buildApplicationInfo(PKG_NAME_C, UID_C)); + when(mPackageManager.getInstalledApplications(anyInt())).thenReturn( + buildInstalledApplicationInfoList()); + when(mUserManager.getUsers()).thenReturn(buildUserInfoList()); when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true); when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true); doNothing().when(mConnectivityManager) @@ -1874,6 +1891,66 @@ public class NetworkPolicyManagerServiceTest { } } + private void enableRestrictedMode(boolean enable) throws Exception { + mService.mRestrictedNetworkingMode = enable; + mService.updateRestrictedModeAllowlistUL(); + verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_RESTRICTED, + enable); + } + + @Test + public void testUpdateRestrictedModeAllowlist() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + expectHasUseRestrictedNetworksPermission(UID_A, true); + expectHasUseRestrictedNetworksPermission(UID_B, false); + + Map<Integer, Integer> firewallUidRules = new ArrayMap<>(); + doAnswer(arg -> { + int[] uids = arg.getArgument(1); + int[] rules = arg.getArgument(2); + assertTrue(uids.length == rules.length); + + for (int i = 0; i < uids.length; ++i) { + firewallUidRules.put(uids[i], rules[i]); + } + return null; + }).when(mNetworkManager).setFirewallUidRules(eq(FIREWALL_CHAIN_RESTRICTED), + any(int[].class), any(int[].class)); + + enableRestrictedMode(true); + assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_A).intValue()); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + assertTrue(mService.isUidNetworkingBlocked(UID_B, false)); + + enableRestrictedMode(false); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + assertFalse(mService.isUidNetworkingBlocked(UID_B, false)); + } + + @Test + public void testUpdateRestrictedModeForUid() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + expectHasUseRestrictedNetworksPermission(UID_A, true); + expectHasUseRestrictedNetworksPermission(UID_B, false); + enableRestrictedMode(true); + + // UID_D and UID_E are not part of installed applications list, so it won't have any + // firewall rules set yet + expectHasUseRestrictedNetworksPermission(UID_D, false); + mService.updateRestrictedModeForUidUL(UID_D); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, UID_D, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_D, false)); + + expectHasUseRestrictedNetworksPermission(UID_E, true); + mService.updateRestrictedModeForUidUL(UID_E); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, UID_E, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_E, false)); + } + private String formatBlockedStateError(int uid, int rule, boolean metered, boolean backgroundRestricted) { return String.format( @@ -1888,12 +1965,27 @@ public class NetworkPolicyManagerServiceTest { .build(); } - private ApplicationInfo buildApplicationInfo(String label) { + private ApplicationInfo buildApplicationInfo(String label, int uid) { final ApplicationInfo ai = new ApplicationInfo(); ai.nonLocalizedLabel = label; + ai.uid = uid; return ai; } + private List<ApplicationInfo> buildInstalledApplicationInfoList() { + final List<ApplicationInfo> installedApps = new ArrayList<>(); + installedApps.add(buildApplicationInfo(PKG_NAME_A, UID_A)); + installedApps.add(buildApplicationInfo(PKG_NAME_B, UID_B)); + installedApps.add(buildApplicationInfo(PKG_NAME_C, UID_C)); + return installedApps; + } + + private List<UserInfo> buildUserInfoList() { + final List<UserInfo> users = new ArrayList<>(); + users.add(new UserInfo(USER_ID, "user1", 0)); + return users; + } + private NetworkInfo buildNetworkInfo() { final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE, null, null); @@ -1959,6 +2051,7 @@ public class NetworkPolicyManagerServiceTest { final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); final NetworkCapabilities networkCapabilities = new NetworkCapabilities(); + networkCapabilities.setSSID(TEST_SSID); return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID); } @@ -1967,6 +2060,15 @@ public class NetworkPolicyManagerServiceTest { hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); } + private void expectHasUseRestrictedNetworksPermission(int uid, boolean hasIt) throws Exception { + when(mIpm.checkUidPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid)).thenReturn( + hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); + when(mIpm.checkUidPermission(NETWORK_STACK, uid)).thenReturn( + PackageManager.PERMISSION_DENIED); + when(mIpm.checkUidPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid)).thenReturn( + PackageManager.PERMISSION_DENIED); + } + private void expectNetworkState(boolean roaming) throws Exception { when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID))) .thenReturn(mCarrierConfig); diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java index 9c8a38219a9c..ac9316e7d908 100644 --- a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java @@ -24,6 +24,9 @@ import static org.junit.Assert.fail; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; import android.net.INetdEventCallback; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -106,6 +109,16 @@ public class NetworkWatchlistServiceTests { counter--; return true; } + + // TODO: mark @Override when aosp/1541935 automerges to master. + public void logDefaultNetworkValidity(boolean valid) { + } + + // TODO: mark @Override when aosp/1541935 automerges to master. + public void logDefaultNetworkEvent(Network defaultNetwork, int score, boolean validated, + LinkProperties lp, NetworkCapabilities nc, Network previousDefaultNetwork, + int previousScore, LinkProperties previousLp, NetworkCapabilities previousNc) { + } }; ServiceThread mHandlerThread; diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java index b07b8fa059d1..9b8a2a82c6df 100644 --- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.IntentSender; import android.content.pm.PackageManager; +import android.hardware.boot.V1_2.IBootControl; import android.os.Handler; import android.os.IPowerManager; import android.os.IRecoverySystemProgressListener; @@ -68,12 +69,13 @@ public class RecoverySystemServiceTest { private IThermalService mIThermalService; private FileWriter mUncryptUpdateFileWriter; private LockSettingsInternal mLockSettingsInternal; + private IBootControl mIBootControl; private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package"; private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package"; @Before - public void setup() { + public void setup() throws Exception { mContext = mock(Context.class); mSystemProperties = new RecoverySystemServiceTestable.FakeSystemProperties(); mUncryptSocket = mock(RecoverySystemService.UncryptSocket.class); @@ -88,8 +90,13 @@ public class RecoverySystemServiceTest { PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager, mIThermalService, new Handler(looper)); + mIBootControl = mock(IBootControl.class); + when(mIBootControl.getCurrentSlot()).thenReturn(0); + when(mIBootControl.getActiveBootSlot()).thenReturn(1); + mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties, - powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal); + powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal, + mIBootControl); } @Test @@ -332,6 +339,15 @@ public class RecoverySystemServiceTest { verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean()); } + + @Test + public void rebootWithLskf_slotMismatch_Failure() throws Exception { + assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true)); + mRecoverySystemService.onPreparedForReboot(true); + assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", false), + is(false)); + } + @Test public void rebootWithLskf_withoutPrepare_Failure() throws Exception { assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true), diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java index 131e4f321a6c..0727e5adb9ca 100644 --- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java @@ -17,6 +17,7 @@ package com.android.server.recoverysystem; import android.content.Context; +import android.hardware.boot.V1_2.IBootControl; import android.os.PowerManager; import com.android.internal.widget.LockSettingsInternal; @@ -30,16 +31,19 @@ public class RecoverySystemServiceTestable extends RecoverySystemService { private final FileWriter mUncryptPackageFileWriter; private final UncryptSocket mUncryptSocket; private final LockSettingsInternal mLockSettingsInternal; + private final IBootControl mIBootControl; MockInjector(Context context, FakeSystemProperties systemProperties, PowerManager powerManager, FileWriter uncryptPackageFileWriter, - UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) { + UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal, + IBootControl bootControl) { super(context); mSystemProperties = systemProperties; mPowerManager = powerManager; mUncryptPackageFileWriter = uncryptPackageFileWriter; mUncryptSocket = uncryptSocket; mLockSettingsInternal = lockSettingsInternal; + mIBootControl = bootControl; } @Override @@ -85,13 +89,19 @@ public class RecoverySystemServiceTestable extends RecoverySystemService { public LockSettingsInternal getLockSettingsService() { return mLockSettingsInternal; } + + @Override + public IBootControl getBootControl() { + return mIBootControl; + } } RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties, PowerManager powerManager, FileWriter uncryptPackageFileWriter, - UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) { + UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal, + IBootControl bootControl) { super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter, - uncryptSocket, lockSettingsInternal)); + uncryptSocket, lockSettingsInternal, bootControl)); } public static class FakeSystemProperties { @@ -102,6 +112,8 @@ public class RecoverySystemServiceTestable extends RecoverySystemService { || RecoverySystemService.INIT_SERVICE_SETUP_BCB.equals(key) || RecoverySystemService.INIT_SERVICE_CLEAR_BCB.equals(key)) { return null; + } else if (RecoverySystemService.AB_UPDATE.equals(key)) { + return "true"; } else { throw new IllegalArgumentException("unexpected test key: " + key); } diff --git a/services/tests/servicestests/src/com/android/server/textservices/OWNERS b/services/tests/servicestests/src/com/android/server/textservices/OWNERS new file mode 100644 index 000000000000..0471e29a25cd --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/textservices/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 816455 + +include /services/core/java/com/android/server/textservices/OWNERS diff --git a/services/tests/shortcutmanagerutils/OWNERS b/services/tests/shortcutmanagerutils/OWNERS new file mode 100644 index 000000000000..d825dfd7cf00 --- /dev/null +++ b/services/tests/shortcutmanagerutils/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/pm/OWNERS diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index 9d48955c87be..e5672081464e 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -1025,6 +1025,8 @@ public class UsageStatsDatabase { writeLocked(fos, stats, version, packagesTokenData); file.finishWrite(fos); fos = null; + } catch (Exception e) { + // Do nothing. Exception has already been handled. } finally { // When fos is null (successful write), this will no-op file.failWrite(fos); @@ -1032,7 +1034,7 @@ public class UsageStatsDatabase { } private static void writeLocked(OutputStream out, IntervalStats stats, int version, - PackagesTokenData packagesTokenData) throws RuntimeException { + PackagesTokenData packagesTokenData) throws Exception { switch (version) { case 1: case 2: @@ -1044,6 +1046,7 @@ public class UsageStatsDatabase { UsageStatsProto.write(out, stats); } catch (Exception e) { Slog.e(TAG, "Unable to write interval stats to proto.", e); + throw e; } break; case 5: @@ -1052,6 +1055,7 @@ public class UsageStatsDatabase { UsageStatsProtoV2.write(out, stats); } catch (Exception e) { Slog.e(TAG, "Unable to write interval stats to proto.", e); + throw e; } break; default: diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 724a9e477b95..335a10221d06 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -109,6 +109,20 @@ import java.util.concurrent.ConcurrentHashMap; */ public abstract class Connection extends Conferenceable { + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "STATE_", value = { + STATE_INITIALIZING, + STATE_NEW, + STATE_RINGING, + STATE_DIALING, + STATE_ACTIVE, + STATE_HOLDING, + STATE_DISCONNECTED, + STATE_PULLING_CALL + }) + public @interface ConnectionState {} + /** * The connection is initializing. This is generally the first state for a {@code Connection} * returned by a {@link ConnectionService}. @@ -2991,6 +3005,26 @@ public abstract class Connection extends Conferenceable { public void onCallAudioStateChanged(CallAudioState state) {} /** + * Inform this Connection when it will or will not be tracked by an {@link InCallService} which + * can provide an InCall UI. + * This is primarily intended for use by Connections reported by self-managed + * {@link ConnectionService} which typically maintain their own UI. + * + * @param isUsingAlternativeUi Indicates whether an InCallService that can provide InCall UI is + * currently tracking the self-managed call. + */ + public void onUsingAlternativeUi(boolean isUsingAlternativeUi) {} + + /** + * Inform this Conenection when it will or will not be tracked by an non-UI + * {@link InCallService}. + * + * @param isTracked Indicates whether an non-UI InCallService is currently tracking the + * self-managed call. + */ + public void onTrackedByNonUiService(boolean isTracked) {} + + /** * Notifies this Connection of an internal state change. This method is called after the * state is changed. * diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index b1ccb533e83d..a4ecb722bd2c 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -137,6 +137,8 @@ public abstract class ConnectionService extends Service { private static final String SESSION_HOLD = "CS.h"; private static final String SESSION_UNHOLD = "CS.u"; private static final String SESSION_CALL_AUDIO_SC = "CS.cASC"; + private static final String SESSION_USING_ALTERNATIVE_UI = "CS.uAU"; + private static final String SESSION_TRACKED_BY_NON_UI_SERVICE = "CS.tBNUS"; private static final String SESSION_PLAY_DTMF = "CS.pDT"; private static final String SESSION_STOP_DTMF = "CS.sDT"; private static final String SESSION_CONFERENCE = "CS.c"; @@ -200,6 +202,9 @@ public abstract class ConnectionService extends Service { private static final int MSG_ADD_PARTICIPANT = 39; private static final int MSG_EXPLICIT_CALL_TRANSFER = 40; private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41; + private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42; + private static final int MSG_ON_USING_ALTERNATIVE_UI = 43; + private static final int MSG_ON_TRACKED_BY_NON_UI_SERVICE = 44; private static Connection sNullConnection; @@ -584,6 +589,36 @@ public abstract class ConnectionService extends Service { } @Override + public void onUsingAlternativeUi(String callId, boolean usingAlternativeUiShowing, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_USING_ALTERNATIVE_UI); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = usingAlternativeUiShowing; + args.arg3 = Log.createSubsession(); + mHandler.obtainMessage(MSG_ON_USING_ALTERNATIVE_UI, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override + public void onTrackedByNonUiService(String callId, boolean isTracked, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_TRACKED_BY_NON_UI_SERVICE); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = isTracked; + args.arg3 = Log.createSubsession(); + mHandler.obtainMessage(MSG_ON_TRACKED_BY_NON_UI_SERVICE, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void playDtmfTone(String callId, char digit, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_PLAY_DTMF); try { @@ -1226,6 +1261,34 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_ON_USING_ALTERNATIVE_UI: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg3, + SESSION_HANDLER + SESSION_USING_ALTERNATIVE_UI); + try { + String callId = (String) args.arg1; + boolean isUsingAlternativeUi = (boolean) args.arg2; + onUsingAlternativeUi(callId, isUsingAlternativeUi); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_ON_TRACKED_BY_NON_UI_SERVICE: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg3, + SESSION_HANDLER + SESSION_TRACKED_BY_NON_UI_SERVICE); + try { + String callId = (String) args.arg1; + boolean isTracked = (boolean) args.arg2; + onTrackedByNonUiService(callId, isTracked); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } case MSG_PLAY_DTMF_TONE: { SomeArgs args = (SomeArgs) msg.obj; try { @@ -1928,10 +1991,12 @@ public abstract class ConnectionService extends Service { request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false); boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean( TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false); - Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + - "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b", - callManagerAccount, callId, request, isIncoming, isUnknown, isLegacyHandover, - isHandover); + boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean( + PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false); + Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " + + "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, " + + " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming, + isUnknown, isLegacyHandover, isHandover, addSelfManaged); Connection connection = null; if (isHandover) { @@ -2206,6 +2271,22 @@ public abstract class ConnectionService extends Service { } } + private void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi) { + Log.i(this, "onUsingAlternativeUi %s %s", callId, isUsingAlternativeUi); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "onUsingAlternativeUi") + .onUsingAlternativeUi(isUsingAlternativeUi); + } + } + + private void onTrackedByNonUiService(String callId, boolean isTracked) { + Log.i(this, "onTrackedByNonUiService %s %s", callId, isTracked); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "onTrackedByNonUiService") + .onTrackedByNonUiService(isTracked); + } + } + private void playDtmfTone(String callId, char digit) { Log.i(this, "playDtmfTone %s %c", callId, digit); if (mConnectionById.containsKey(callId)) { diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 835ecaa8c90d..579b33e9c283 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -188,6 +188,15 @@ public final class PhoneAccount implements Parcelable { "android.telecom.extra.SKIP_CALL_FILTERING"; /** + * Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which + * indicates whether a Self-managed {@link PhoneAccount} want to expose its calls to all + * {@link InCallService} which declares the metadata + * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS}. + */ + public static final String EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE = + "android.telecom.extra.ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE"; + + /** * Flag indicating that this {@code PhoneAccount} can act as a connection manager for * other connections. The {@link ConnectionService} associated with this {@code PhoneAccount} * will be allowed to manage phone calls including using its own proprietary phone-call diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index fb5417994b57..d5555474f248 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -136,4 +136,9 @@ oneway interface IConnectionService { int error, in Session.Info sessionInfo); void handoverComplete(String callId, in Session.Info sessionInfo); + + void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi, + in Session.Info sessionInfo); + + void onTrackedByNonUiService(String callId, boolean isTracked, in Session.Info sessionInfo); } diff --git a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java new file mode 100644 index 000000000000..c7e7cd5ec64e --- /dev/null +++ b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java @@ -0,0 +1,238 @@ +/* + * 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 com.android.internal.telephony; + +import android.net.Uri; +import android.util.Log; +import android.util.Pair; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Utility methods for parsing parts of {@link android.telephony.ims.SipMessage}s. + * See RFC 3261 for more information. + * @hide + */ +// Note: This is lightweight in order to avoid a full SIP stack import in frameworks/base. +public class SipMessageParsingUtils { + private static final String TAG = "SipMessageParsingUtils"; + // "Method" in request-line + // Request-Line = Method SP Request-URI SP SIP-Version CRLF + private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS", + "BYE", "CANCEL", "REGISTER", "PRACK", "SUBSCRIBE", "NOTIFY", "PUBLISH", "INFO", "REFER", + "MESSAGE", "UPDATE"}; + + // SIP Version 2.0 (corresponding to RCS 3261), set in "SIP-Version" of Status-Line and + // Request-Line + // + // Request-Line = Method SP Request-URI SP SIP-Version CRLF + // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF + private static final String SIP_VERSION_2 = "SIP/2.0"; + + // headers are formatted Key:Value + private static final String HEADER_KEY_VALUE_SEPARATOR = ":"; + // Multiple of the same header can be concatenated and put into one header Key:Value pair, for + // example "v: XX1;branch=YY1,XX2;branch=YY2". This needs to be treated as two "v:" headers. + private static final String SUBHEADER_VALUE_SEPARATOR = ","; + + // SIP header parameters have the format ";paramName=paramValue" + private static final String PARAM_SEPARATOR = ";"; + // parameters are formatted paramName=ParamValue + private static final String PARAM_KEY_VALUE_SEPARATOR = "="; + + // The via branch parameter definition + private static final String BRANCH_PARAM_KEY = "branch"; + + // via header key + private static final String VIA_SIP_HEADER_KEY = "via"; + // compact form of the via header key + private static final String VIA_SIP_HEADER_KEY_COMPACT = "v"; + + /** + * @return true if the SIP message start line is considered a request (based on known request + * methods). + */ + public static boolean isSipRequest(String startLine) { + String[] splitLine = splitStartLineAndVerify(startLine); + if (splitLine == null) return false; + return verifySipRequest(splitLine); + } + + /** + * Return the via branch parameter, which is used to identify the transaction ID (request and + * response pair) in a SIP transaction. + * @param headerString The string containing the headers of the SIP message. + */ + public static String getTransactionId(String headerString) { + // search for Via: or v: parameter, we only care about the first one. + List<Pair<String, String>> headers = parseHeaders(headerString, true, + VIA_SIP_HEADER_KEY, VIA_SIP_HEADER_KEY_COMPACT); + for (Pair<String, String> header : headers) { + // Headers can also be concatenated together using a "," between each header value. + // format becomes v: XX1;branch=YY1,XX2;branch=YY2. Need to extract only the first ID's + // branch param YY1. + String[] subHeaders = header.second.split(SUBHEADER_VALUE_SEPARATOR); + for (String subHeader : subHeaders) { + // Search for ;branch=z9hG4bKXXXXXX and return parameter value + String[] params = subHeader.split(PARAM_SEPARATOR); + if (params.length < 2) { + // This param doesn't include a branch param, move to next param. + Log.w(TAG, "getTransactionId: via detected without branch param:" + + subHeader); + continue; + } + // by spec, each param can only appear once in a header. + for (String param : params) { + String[] pair = param.split(PARAM_KEY_VALUE_SEPARATOR); + if (pair.length < 2) { + // ignore info before the first parameter + continue; + } + if (pair.length > 2) { + Log.w(TAG, + "getTransactionId: unexpected parameter" + Arrays.toString(pair)); + } + // Trim whitespace in parameter + pair[0] = pair[0].trim(); + pair[1] = pair[1].trim(); + if (BRANCH_PARAM_KEY.equalsIgnoreCase(pair[0])) { + // There can be multiple "Via" headers in the SIP message, however we want + // to return the first once found, as this corresponds with the transaction + // that is relevant here. + return pair[1]; + } + } + } + } + return null; + } + + private static String[] splitStartLineAndVerify(String startLine) { + String[] splitLine = startLine.split(" "); + if (isStartLineMalformed(splitLine)) return null; + return splitLine; + } + + private static boolean isStartLineMalformed(String[] startLine) { + if (startLine == null || startLine.length == 0) { + return true; + } + // start lines contain three segments separated by spaces (SP): + // Request-Line = Method SP Request-URI SP SIP-Version CRLF + // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF + return (startLine.length != 3); + } + + private static boolean verifySipRequest(String[] request) { + // Request-Line = Method SP Request-URI SP SIP-Version CRLF + boolean verified = request[2].contains(SIP_VERSION_2); + verified &= (Uri.parse(request[1]).getScheme() != null); + verified &= Arrays.stream(SIP_REQUEST_METHODS).anyMatch(s -> request[0].contains(s)); + return verified; + } + + private static boolean verifySipResponse(String[] response) { + // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF + boolean verified = response[0].contains(SIP_VERSION_2); + int statusCode = Integer.parseInt(response[1]); + verified &= (statusCode >= 100 && statusCode < 700); + return verified; + } + + /** + * Parse a String representation of the Header portion of the SIP Message and re-structure it + * into a List of key->value pairs representing each header in the order that they appeared in + * the message. + * + * @param headerString The raw string containing all headers + * @param stopAtFirstMatch Return early when the first match is found from matching header keys. + * @param matchingHeaderKeys An optional list of Strings containing header keys that should be + * returned if they exist. If none exist, all keys will be returned. + * (This is internally an equalsIgnoreMatch comparison). + * @return the matched header keys and values. + */ + private static List<Pair<String, String>> parseHeaders(String headerString, + boolean stopAtFirstMatch, String... matchingHeaderKeys) { + // Ensure there is no leading whitespace + headerString = removeLeadingWhitespace(headerString); + + List<Pair<String, String>> result = new ArrayList<>(); + // Split the string line-by-line. + String[] headerLines = headerString.split("\\r?\\n"); + if (headerLines.length == 0) { + return Collections.emptyList(); + } + + String headerKey = null; + StringBuilder headerValueSegment = new StringBuilder(); + // loop through each line, either parsing a "key: value" pair or appending values that span + // multiple lines. + for (String line : headerLines) { + // This line is a continuation of the last line if it starts with whitespace or tab + if (line.startsWith("\t") || line.startsWith(" ")) { + headerValueSegment.append(removeLeadingWhitespace(line)); + continue; + } + // This line is the start of a new key, If headerKey/value is already populated from a + // previous key/value pair, add it to list of parsed header pairs. + if (headerKey != null) { + final String key = headerKey; + if (matchingHeaderKeys == null || matchingHeaderKeys.length == 0 + || Arrays.stream(matchingHeaderKeys).anyMatch( + (s) -> s.equalsIgnoreCase(key))) { + result.add(new Pair<>(key, headerValueSegment.toString())); + if (stopAtFirstMatch) { + return result; + } + } + headerKey = null; + headerValueSegment = new StringBuilder(); + } + + // Format is "Key:Value", ignore any ":" after the first. + String[] pair = line.split(HEADER_KEY_VALUE_SEPARATOR, 2); + if (pair.length < 2) { + // malformed line, skip + Log.w(TAG, "parseHeaders - received malformed line: " + line); + continue; + } + + headerKey = pair[0].trim(); + for (int i = 1; i < pair.length; i++) { + headerValueSegment.append(removeLeadingWhitespace(pair[i])); + } + } + // Pick up the last pending header being parsed, if it exists. + if (headerKey != null) { + final String key = headerKey; + if (matchingHeaderKeys == null || matchingHeaderKeys.length == 0 + || Arrays.stream(matchingHeaderKeys).anyMatch( + (s) -> s.equalsIgnoreCase(key))) { + result.add(new Pair<>(key, headerValueSegment.toString())); + } + } + + return result; + } + + private static String removeLeadingWhitespace(String line) { + return line.replaceFirst("^\\s*", ""); + } +} diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index d01297147fdb..f6d18fcd9ab3 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -18,10 +18,7 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.SystemApi; -import android.hardware.radio.V1_1.GeranBands; import android.hardware.radio.V1_5.AccessNetwork; -import android.hardware.radio.V1_5.EutranBands; -import android.hardware.radio.V1_5.UtranBands; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -117,52 +114,120 @@ public final class AccessNetworkConstants { * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf */ public static final class GeranBand { - public static final int BAND_T380 = GeranBands.BAND_T380; - public static final int BAND_T410 = GeranBands.BAND_T410; - public static final int BAND_450 = GeranBands.BAND_450; - public static final int BAND_480 = GeranBands.BAND_480; - public static final int BAND_710 = GeranBands.BAND_710; - public static final int BAND_750 = GeranBands.BAND_750; - public static final int BAND_T810 = GeranBands.BAND_T810; - public static final int BAND_850 = GeranBands.BAND_850; - public static final int BAND_P900 = GeranBands.BAND_P900; - public static final int BAND_E900 = GeranBands.BAND_E900; - public static final int BAND_R900 = GeranBands.BAND_R900; - public static final int BAND_DCS1800 = GeranBands.BAND_DCS1800; - public static final int BAND_PCS1900 = GeranBands.BAND_PCS1900; - public static final int BAND_ER900 = GeranBands.BAND_ER900; + public static final int BAND_T380 = android.hardware.radio.V1_1.GeranBands.BAND_T380; + public static final int BAND_T410 = android.hardware.radio.V1_1.GeranBands.BAND_T410; + public static final int BAND_450 = android.hardware.radio.V1_1.GeranBands.BAND_450; + public static final int BAND_480 = android.hardware.radio.V1_1.GeranBands.BAND_480; + public static final int BAND_710 = android.hardware.radio.V1_1.GeranBands.BAND_710; + public static final int BAND_750 = android.hardware.radio.V1_1.GeranBands.BAND_750; + public static final int BAND_T810 = android.hardware.radio.V1_1.GeranBands.BAND_T810; + public static final int BAND_850 = android.hardware.radio.V1_1.GeranBands.BAND_850; + public static final int BAND_P900 = android.hardware.radio.V1_1.GeranBands.BAND_P900; + public static final int BAND_E900 = android.hardware.radio.V1_1.GeranBands.BAND_E900; + public static final int BAND_R900 = android.hardware.radio.V1_1.GeranBands.BAND_R900; + public static final int BAND_DCS1800 = android.hardware.radio.V1_1.GeranBands.BAND_DCS1800; + public static final int BAND_PCS1900 = android.hardware.radio.V1_1.GeranBands.BAND_PCS1900; + public static final int BAND_ER900 = android.hardware.radio.V1_1.GeranBands.BAND_ER900; + + /** + * GeranBand + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_T380, + BAND_T410, + BAND_450, + BAND_480, + BAND_710, + BAND_750, + BAND_T810, + BAND_850, + BAND_P900, + BAND_E900, + BAND_R900, + BAND_DCS1800, + BAND_PCS1900, + BAND_ER900}) + + public @interface GeranBands {} /** @hide */ private GeranBand() {} } /** + * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN. + * 3GPP TS 45.005 Table 2-2 Fixed designation of ARFCN. + * @hide + */ + enum GeranBandArfcnFrequency { + + // Dynamically mapped ARFCN +// GERAN_ARFCN_FREQUENCY_BAND_T380(GeranBand.BAND_T380, 380.2, 0), +// GERAN_ARFCN_FREQUENCY_BAND_T410(GeranBand.BAND_T410, 410.2, 0), +// GERAN_ARFCN_FREQUENCY_BAND_710(GeranBand.BAND_710, 698, 0), +// GERAN_ARFCN_FREQUENCY_BAND_750(GeranBand.BAND_750, 747, 438, 30), +// GERAN_ARFCN_FREQUENCY_BAND_T810(GeranBand.BAND_T810, 806, 350), + // Fixed designation of ARFCN + GERAN_ARFCN_FREQUENCY_BAND_450(GeranBand.BAND_450, 450600, 259, 259, 293, 10), + GERAN_ARFCN_FREQUENCY_BAND_480(GeranBand.BAND_480, 479000, 306, 306, 340, 10), + GERAN_ARFCN_FREQUENCY_BAND_850(GeranBand.BAND_850, 824200, 128, 128, 251, 45), + GERAN_ARFCN_FREQUENCY_BAND_DCS1800(GeranBand.BAND_DCS1800, 1710200, 512, 512, 885, 95), + GERAN_ARFCN_FREQUENCY_BAND_PCS1900(GeranBand.BAND_PCS1900, 1850200, 512, 512, 810, 80), + GERAN_ARFCN_FREQUENCY_BAND_E900_1(GeranBand.BAND_E900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_E900_2(GeranBand.BAND_E900, 890000, 1024, 975, 1023, 45), + GERAN_ARFCN_FREQUENCY_BAND_R900_1(GeranBand.BAND_R900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_R900_2(GeranBand.BAND_R900, 890000, 1024, 955, 1023, 45), + GERAN_ARFCN_FREQUENCY_BAND_P900(GeranBand.BAND_P900, 890000, 0, 1, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_ER900_1(GeranBand.BAND_ER900, 890000, 0, 0, 124, 45), + GERAN_ARFCN_FREQUENCY_BAND_ER900_2(GeranBand.BAND_ER900, 890000, 1024, 940, 1023, 1024); + + GeranBandArfcnFrequency(int band, int uplinkFrequencyFirstKhz, int arfcnOffset, + int arfcnRangeFirst, int arfcnRangeLast, int downlinkOffset) { + this.band = band; + this.uplinkFrequencyFirst = uplinkFrequencyFirstKhz; + this.arfcnOffset = arfcnOffset; + this.arfcnRangeFirst = arfcnRangeFirst; + this.arfcnRangeLast = arfcnRangeLast; + this.downlinkOffset = downlinkOffset; + } + + int band; + int uplinkFrequencyFirst; + int arfcnOffset; + int arfcnRangeFirst; + int arfcnRangeLast; + int downlinkOffset; + } + + /** * Frequency bands for UTRAN. * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf */ public static final class UtranBand { - public static final int BAND_1 = UtranBands.BAND_1; - public static final int BAND_2 = UtranBands.BAND_2; - public static final int BAND_3 = UtranBands.BAND_3; - public static final int BAND_4 = UtranBands.BAND_4; - public static final int BAND_5 = UtranBands.BAND_5; - public static final int BAND_6 = UtranBands.BAND_6; - public static final int BAND_7 = UtranBands.BAND_7; - public static final int BAND_8 = UtranBands.BAND_8; - public static final int BAND_9 = UtranBands.BAND_9; - public static final int BAND_10 = UtranBands.BAND_10; - public static final int BAND_11 = UtranBands.BAND_11; - public static final int BAND_12 = UtranBands.BAND_12; - public static final int BAND_13 = UtranBands.BAND_13; - public static final int BAND_14 = UtranBands.BAND_14; + public static final int BAND_1 = android.hardware.radio.V1_5.UtranBands.BAND_1; + public static final int BAND_2 = android.hardware.radio.V1_5.UtranBands.BAND_2; + public static final int BAND_3 = android.hardware.radio.V1_5.UtranBands.BAND_3; + public static final int BAND_4 = android.hardware.radio.V1_5.UtranBands.BAND_4; + public static final int BAND_5 = android.hardware.radio.V1_5.UtranBands.BAND_5; + public static final int BAND_6 = android.hardware.radio.V1_5.UtranBands.BAND_6; + public static final int BAND_7 = android.hardware.radio.V1_5.UtranBands.BAND_7; + public static final int BAND_8 = android.hardware.radio.V1_5.UtranBands.BAND_8; + public static final int BAND_9 = android.hardware.radio.V1_5.UtranBands.BAND_9; + public static final int BAND_10 = android.hardware.radio.V1_5.UtranBands.BAND_10; + public static final int BAND_11 = android.hardware.radio.V1_5.UtranBands.BAND_11; + public static final int BAND_12 = android.hardware.radio.V1_5.UtranBands.BAND_12; + public static final int BAND_13 = android.hardware.radio.V1_5.UtranBands.BAND_13; + public static final int BAND_14 = android.hardware.radio.V1_5.UtranBands.BAND_14; // band 15, 16, 17, 18 are reserved - public static final int BAND_19 = UtranBands.BAND_19; - public static final int BAND_20 = UtranBands.BAND_20; - public static final int BAND_21 = UtranBands.BAND_21; - public static final int BAND_22 = UtranBands.BAND_22; + public static final int BAND_19 = android.hardware.radio.V1_5.UtranBands.BAND_19; + public static final int BAND_20 = android.hardware.radio.V1_5.UtranBands.BAND_20; + public static final int BAND_21 = android.hardware.radio.V1_5.UtranBands.BAND_21; + public static final int BAND_22 = android.hardware.radio.V1_5.UtranBands.BAND_22; // band 23, 24 are reserved - public static final int BAND_25 = UtranBands.BAND_25; - public static final int BAND_26 = UtranBands.BAND_26; + public static final int BAND_25 = android.hardware.radio.V1_5.UtranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_5.UtranBands.BAND_26; // Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2. @@ -171,115 +236,423 @@ public final class AccessNetworkConstants { * 1900 - 1920 MHz: Uplink and downlink transmission * 2010 - 2025 MHz: Uplink and downlink transmission */ - public static final int BAND_A = UtranBands.BAND_A; + public static final int BAND_A = android.hardware.radio.V1_5.UtranBands.BAND_A; /** * Band B * 1850 - 1910 MHz: Uplink and downlink transmission * 1930 - 1990 MHz: Uplink and downlink transmission */ - public static final int BAND_B = UtranBands.BAND_B; + public static final int BAND_B = android.hardware.radio.V1_5.UtranBands.BAND_B; /** * Band C * 1910 - 1930 MHz: Uplink and downlink transmission */ - public static final int BAND_C = UtranBands.BAND_C; + public static final int BAND_C = android.hardware.radio.V1_5.UtranBands.BAND_C; /** * Band D * 2570 - 2620 MHz: Uplink and downlink transmission */ - public static final int BAND_D = UtranBands.BAND_D; + public static final int BAND_D = android.hardware.radio.V1_5.UtranBands.BAND_D; /** * Band E * 2300—2400 MHz: Uplink and downlink transmission */ - public static final int BAND_E = UtranBands.BAND_E; + public static final int BAND_E = android.hardware.radio.V1_5.UtranBands.BAND_E; /** * Band F * 1880 - 1920 MHz: Uplink and downlink transmission */ - public static final int BAND_F = UtranBands.BAND_F; + public static final int BAND_F = android.hardware.radio.V1_5.UtranBands.BAND_F; + + /** + * UtranBand + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_1, + BAND_2, + BAND_3, + BAND_4, + BAND_5, + BAND_6, + BAND_7, + BAND_8, + BAND_9, + BAND_10, + BAND_11, + BAND_12, + BAND_13, + BAND_14, + BAND_19, + BAND_20, + BAND_21, + BAND_22, + BAND_25, + BAND_26, + BAND_A, + BAND_B, + BAND_C, + BAND_D, + BAND_E, + BAND_F}) + + public @interface UtranBands {} /** @hide */ private UtranBand() {} } /** + * 3GPP TS 25.101, Table 5.1 UARFCN definition (general) + * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option. + * + * @hide + */ + enum UtranBandArfcnFrequency { + + UTRAN_ARFCN_FREQUENCY_BAND_1(UtranBand.BAND_1, 0, 10562, 10838, 0, 9612, 9888), + UTRAN_ARFCN_FREQUENCY_BAND_2(UtranBand.BAND_2, 0, 9662, 9938, 0, 9262, 9538), + UTRAN_ARFCN_FREQUENCY_BAND_3(UtranBand.BAND_3, 1575000, 1162, 1513, 1525000, 937, 1288), + UTRAN_ARFCN_FREQUENCY_BAND_4(UtranBand.BAND_4, 1805000, 1537, 1738, 1450000, 1312, 1513), + UTRAN_ARFCN_FREQUENCY_BAND_5(UtranBand.BAND_5, 0, 4357, 4458, 0, 4132, 4233), + UTRAN_ARFCN_FREQUENCY_BAND_6(UtranBand.BAND_6, 0, 4387, 4413, 0, 4162, 4188), + UTRAN_ARFCN_FREQUENCY_BAND_7(UtranBand.BAND_7, 2175000, 2237, 2563, 2100000, 2012, 2338), + UTRAN_ARFCN_FREQUENCY_BAND_8(UtranBand.BAND_8, 340000, 2937, 3088, 340000, 2712, 2863), + UTRAN_ARFCN_FREQUENCY_BAND_9(UtranBand.BAND_9, 0, 9327, 9837, 0, 8762, 8912), + UTRAN_ARFCN_FREQUENCY_BAND_10(UtranBand.BAND_10, 1490000, 3112, 3388, 1135000, 2887, 3163), + UTRAN_ARFCN_FREQUENCY_BAND_11(UtranBand.BAND_11, 736000, 3712, 3787, 733000, 3487, 3562), + UTRAN_ARFCN_FREQUENCY_BAND_12(UtranBand.BAND_12, -37000, 3842, 3903, -22000, 3617, 3678), + UTRAN_ARFCN_FREQUENCY_BAND_13(UtranBand.BAND_13, -55000, 4017, 4043, 21000, 3792, 3818), + UTRAN_ARFCN_FREQUENCY_BAND_14(UtranBand.BAND_14, -63000, 4117, 4143, 12000, 3892, 3918), + UTRAN_ARFCN_FREQUENCY_BAND_19(UtranBand.BAND_19, 735000, 712, 763, 770000, 312, 363), + UTRAN_ARFCN_FREQUENCY_BAND_20(UtranBand.BAND_20, -109000, 4512, 4638, -23000, 4287, 4413), + UTRAN_ARFCN_FREQUENCY_BAND_21(UtranBand.BAND_21, 1326000, 862, 912, 1358000, 462, 512), + UTRAN_ARFCN_FREQUENCY_BAND_22(UtranBand.BAND_22, 2580000, 4662, 5038, 2525000, 4437, 4813), + UTRAN_ARFCN_FREQUENCY_BAND_25(UtranBand.BAND_25, 910000, 5112, 5413, 875000, 4887, 5188), + UTRAN_ARFCN_FREQUENCY_BAND_A(UtranBand.BAND_A, 0, 10054, 10121, 0, 9504, 9596), + UTRAN_ARFCN_FREQUENCY_BAND_B(UtranBand.BAND_B, 0, 9654, 9946, 0, 9254, 9546), + UTRAN_ARFCN_FREQUENCY_BAND_C(UtranBand.BAND_C, 0, 0, 0, 0, 9554, 9646), + UTRAN_ARFCN_FREQUENCY_BAND_D(UtranBand.BAND_D, 0, 0, 0, 0, 12854, 13096), + UTRAN_ARFCN_FREQUENCY_BAND_E(UtranBand.BAND_E, 0, 0, 0, 0, 11504, 11996), + UTRAN_ARFCN_FREQUENCY_BAND_F(UtranBand.BAND_F, 0, 0, 0, 0, 9404, 9596); + + UtranBandArfcnFrequency(int band, int downlinkOffsetKhz, int downlinkRangeFirst, + int downlinkRangeLast, int uplinkOffsetKhz, int uplinkRangeFirst, + int uplinkRangeLast) { + this.band = band; + this.downlinkOffset = downlinkOffsetKhz; + this.downlinkRangeFirst = downlinkRangeFirst; + this.downlinkRangeLast = downlinkRangeLast; + this.uplinkOffset = uplinkOffsetKhz; + this.uplinkRangeFirst = uplinkRangeFirst; + this.uplinkRangeLast = uplinkRangeLast; + } + + int band; + int downlinkOffset; + int downlinkRangeFirst; + int downlinkRangeLast; + int uplinkOffset; + int uplinkRangeFirst; + int uplinkRangeLast; + } + + /** * Frequency bands for EUTRAN. * 3GPP TS 36.101, Version 16.4.0, Table 5.5: Operating bands * https://www.etsi.org/deliver/etsi_ts/136100_136199/136101/15.09.00_60/ts_136101v150900p.pdf */ public static final class EutranBand { - public static final int BAND_1 = EutranBands.BAND_1; - public static final int BAND_2 = EutranBands.BAND_2; - public static final int BAND_3 = EutranBands.BAND_3; - public static final int BAND_4 = EutranBands.BAND_4; - public static final int BAND_5 = EutranBands.BAND_5; - public static final int BAND_6 = EutranBands.BAND_6; - public static final int BAND_7 = EutranBands.BAND_7; - public static final int BAND_8 = EutranBands.BAND_8; - public static final int BAND_9 = EutranBands.BAND_9; - public static final int BAND_10 = EutranBands.BAND_10; - public static final int BAND_11 = EutranBands.BAND_11; - public static final int BAND_12 = EutranBands.BAND_12; - public static final int BAND_13 = EutranBands.BAND_13; - public static final int BAND_14 = EutranBands.BAND_14; - public static final int BAND_17 = EutranBands.BAND_17; - public static final int BAND_18 = EutranBands.BAND_18; - public static final int BAND_19 = EutranBands.BAND_19; - public static final int BAND_20 = EutranBands.BAND_20; - public static final int BAND_21 = EutranBands.BAND_21; - public static final int BAND_22 = EutranBands.BAND_22; - public static final int BAND_23 = EutranBands.BAND_23; - public static final int BAND_24 = EutranBands.BAND_24; - public static final int BAND_25 = EutranBands.BAND_25; - public static final int BAND_26 = EutranBands.BAND_26; - public static final int BAND_27 = EutranBands.BAND_27; - public static final int BAND_28 = EutranBands.BAND_28; - public static final int BAND_30 = EutranBands.BAND_30; - public static final int BAND_31 = EutranBands.BAND_31; - public static final int BAND_33 = EutranBands.BAND_33; - public static final int BAND_34 = EutranBands.BAND_34; - public static final int BAND_35 = EutranBands.BAND_35; - public static final int BAND_36 = EutranBands.BAND_36; - public static final int BAND_37 = EutranBands.BAND_37; - public static final int BAND_38 = EutranBands.BAND_38; - public static final int BAND_39 = EutranBands.BAND_39; - public static final int BAND_40 = EutranBands.BAND_40; - public static final int BAND_41 = EutranBands.BAND_41; - public static final int BAND_42 = EutranBands.BAND_42; - public static final int BAND_43 = EutranBands.BAND_43; - public static final int BAND_44 = EutranBands.BAND_44; - public static final int BAND_45 = EutranBands.BAND_45; - public static final int BAND_46 = EutranBands.BAND_46; - public static final int BAND_47 = EutranBands.BAND_47; - public static final int BAND_48 = EutranBands.BAND_48; - public static final int BAND_49 = EutranBands.BAND_49; - public static final int BAND_50 = EutranBands.BAND_50; - public static final int BAND_51 = EutranBands.BAND_51; - public static final int BAND_52 = EutranBands.BAND_52; - public static final int BAND_53 = EutranBands.BAND_53; - public static final int BAND_65 = EutranBands.BAND_65; - public static final int BAND_66 = EutranBands.BAND_66; - public static final int BAND_68 = EutranBands.BAND_68; - public static final int BAND_70 = EutranBands.BAND_70; - public static final int BAND_71 = EutranBands.BAND_71; - public static final int BAND_72 = EutranBands.BAND_72; - public static final int BAND_73 = EutranBands.BAND_73; - public static final int BAND_74 = EutranBands.BAND_74; - public static final int BAND_85 = EutranBands.BAND_85; - public static final int BAND_87 = EutranBands.BAND_87; - public static final int BAND_88 = EutranBands.BAND_88; + public static final int BAND_1 = android.hardware.radio.V1_5.EutranBands.BAND_1; + public static final int BAND_2 = android.hardware.radio.V1_5.EutranBands.BAND_2; + public static final int BAND_3 = android.hardware.radio.V1_5.EutranBands.BAND_3; + public static final int BAND_4 = android.hardware.radio.V1_5.EutranBands.BAND_4; + public static final int BAND_5 = android.hardware.radio.V1_5.EutranBands.BAND_5; + public static final int BAND_6 = android.hardware.radio.V1_5.EutranBands.BAND_6; + public static final int BAND_7 = android.hardware.radio.V1_5.EutranBands.BAND_7; + public static final int BAND_8 = android.hardware.radio.V1_5.EutranBands.BAND_8; + public static final int BAND_9 = android.hardware.radio.V1_5.EutranBands.BAND_9; + public static final int BAND_10 = android.hardware.radio.V1_5.EutranBands.BAND_10; + public static final int BAND_11 = android.hardware.radio.V1_5.EutranBands.BAND_11; + public static final int BAND_12 = android.hardware.radio.V1_5.EutranBands.BAND_12; + public static final int BAND_13 = android.hardware.radio.V1_5.EutranBands.BAND_13; + public static final int BAND_14 = android.hardware.radio.V1_5.EutranBands.BAND_14; + public static final int BAND_17 = android.hardware.radio.V1_5.EutranBands.BAND_17; + public static final int BAND_18 = android.hardware.radio.V1_5.EutranBands.BAND_18; + public static final int BAND_19 = android.hardware.radio.V1_5.EutranBands.BAND_19; + public static final int BAND_20 = android.hardware.radio.V1_5.EutranBands.BAND_20; + public static final int BAND_21 = android.hardware.radio.V1_5.EutranBands.BAND_21; + public static final int BAND_22 = android.hardware.radio.V1_5.EutranBands.BAND_22; + public static final int BAND_23 = android.hardware.radio.V1_5.EutranBands.BAND_23; + public static final int BAND_24 = android.hardware.radio.V1_5.EutranBands.BAND_24; + public static final int BAND_25 = android.hardware.radio.V1_5.EutranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_5.EutranBands.BAND_26; + public static final int BAND_27 = android.hardware.radio.V1_5.EutranBands.BAND_27; + public static final int BAND_28 = android.hardware.radio.V1_5.EutranBands.BAND_28; + public static final int BAND_30 = android.hardware.radio.V1_5.EutranBands.BAND_30; + public static final int BAND_31 = android.hardware.radio.V1_5.EutranBands.BAND_31; + public static final int BAND_33 = android.hardware.radio.V1_5.EutranBands.BAND_33; + public static final int BAND_34 = android.hardware.radio.V1_5.EutranBands.BAND_34; + public static final int BAND_35 = android.hardware.radio.V1_5.EutranBands.BAND_35; + public static final int BAND_36 = android.hardware.radio.V1_5.EutranBands.BAND_36; + public static final int BAND_37 = android.hardware.radio.V1_5.EutranBands.BAND_37; + public static final int BAND_38 = android.hardware.radio.V1_5.EutranBands.BAND_38; + public static final int BAND_39 = android.hardware.radio.V1_5.EutranBands.BAND_39; + public static final int BAND_40 = android.hardware.radio.V1_5.EutranBands.BAND_40; + public static final int BAND_41 = android.hardware.radio.V1_5.EutranBands.BAND_41; + public static final int BAND_42 = android.hardware.radio.V1_5.EutranBands.BAND_42; + public static final int BAND_43 = android.hardware.radio.V1_5.EutranBands.BAND_43; + public static final int BAND_44 = android.hardware.radio.V1_5.EutranBands.BAND_44; + public static final int BAND_45 = android.hardware.radio.V1_5.EutranBands.BAND_45; + public static final int BAND_46 = android.hardware.radio.V1_5.EutranBands.BAND_46; + public static final int BAND_47 = android.hardware.radio.V1_5.EutranBands.BAND_47; + public static final int BAND_48 = android.hardware.radio.V1_5.EutranBands.BAND_48; + public static final int BAND_49 = android.hardware.radio.V1_5.EutranBands.BAND_49; + public static final int BAND_50 = android.hardware.radio.V1_5.EutranBands.BAND_50; + public static final int BAND_51 = android.hardware.radio.V1_5.EutranBands.BAND_51; + public static final int BAND_52 = android.hardware.radio.V1_5.EutranBands.BAND_52; + public static final int BAND_53 = android.hardware.radio.V1_5.EutranBands.BAND_53; + public static final int BAND_65 = android.hardware.radio.V1_5.EutranBands.BAND_65; + public static final int BAND_66 = android.hardware.radio.V1_5.EutranBands.BAND_66; + public static final int BAND_68 = android.hardware.radio.V1_5.EutranBands.BAND_68; + public static final int BAND_70 = android.hardware.radio.V1_5.EutranBands.BAND_70; + public static final int BAND_71 = android.hardware.radio.V1_5.EutranBands.BAND_71; + public static final int BAND_72 = android.hardware.radio.V1_5.EutranBands.BAND_72; + public static final int BAND_73 = android.hardware.radio.V1_5.EutranBands.BAND_73; + public static final int BAND_74 = android.hardware.radio.V1_5.EutranBands.BAND_74; + public static final int BAND_85 = android.hardware.radio.V1_5.EutranBands.BAND_85; + public static final int BAND_87 = android.hardware.radio.V1_5.EutranBands.BAND_87; + public static final int BAND_88 = android.hardware.radio.V1_5.EutranBands.BAND_88; + + /** + * EutranBands + * + * @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"BAND_"}, + value = {BAND_1, + BAND_2, + BAND_3, + BAND_4, + BAND_5, + BAND_6, + BAND_7, + BAND_8, + BAND_9, + BAND_10, + BAND_11, + BAND_12, + BAND_13, + BAND_14, + BAND_17, + BAND_18, + BAND_19, + BAND_20, + BAND_21, + BAND_22, + BAND_23, + BAND_24, + BAND_25, + BAND_26, + BAND_27, + BAND_28, + BAND_30, + BAND_31, + BAND_33, + BAND_34, + BAND_35, + BAND_36, + BAND_37, + BAND_38, + BAND_39, + BAND_40, + BAND_41, + BAND_42, + BAND_43, + BAND_44, + BAND_45, + BAND_46, + BAND_47, + BAND_48, + BAND_49, + BAND_50, + BAND_51, + BAND_52, + BAND_53, + BAND_65, + BAND_66, + BAND_68, + BAND_70, + BAND_71, + BAND_72, + BAND_73, + BAND_74, + BAND_85, + BAND_87, + BAND_88}) + + public @interface EutranBands {} /** @hide */ private EutranBand() {}; } /** + * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers. + * + * @hide + */ + enum EutranBandArfcnFrequency { + + EUTRAN_ARFCN_FREQUENCY_BAND_1( + EutranBand.BAND_1, 2110000, 0, 599, 1920000, 18800, 18599), + EUTRAN_ARFCN_FREQUENCY_BAND_2( + EutranBand.BAND_2, 1930000, 600, 1199, 1850000, 18600, 19199), + EUTRAN_ARFCN_FREQUENCY_BAND_3( + EutranBand.BAND_3, 1805000, 1200, 1949, 1710000, 19200, 19949), + EUTRAN_ARFCN_FREQUENCY_BAND_4( + EutranBand.BAND_4, 2110000, 1950, 2399, 1710000, 19950, 20399), + EUTRAN_ARFCN_FREQUENCY_BAND_5( + EutranBand.BAND_5, 869000, 2400, 2649, 824000, 20400, 20649), + EUTRAN_ARFCN_FREQUENCY_BAND_6( + EutranBand.BAND_6, 875000, 2650, 2749, 830000, 20650, 20749), + EUTRAN_ARFCN_FREQUENCY_BAND_7( + EutranBand.BAND_7, 2620000, 2750, 3449, 2500000, 20750, 21449), + EUTRAN_ARFCN_FREQUENCY_BAND_8( + EutranBand.BAND_8, 925000, 3450, 3799, 880000, 21450, 21799), + EUTRAN_ARFCN_FREQUENCY_BAND_9( + EutranBand.BAND_9, 1844900, 3800, 4149, 1749900, 21800, 22149), + EUTRAN_ARFCN_FREQUENCY_BAND_10( + EutranBand.BAND_10, 2110000, 4150, 4749, 1710000, 22150, 22749), + EUTRAN_ARFCN_FREQUENCY_BAND_11( + EutranBand.BAND_11, 1475900, 4750, 4949, 1427900, 22750, 22949), + EUTRAN_ARFCN_FREQUENCY_BAND_12( + EutranBand.BAND_12, 729000, 5010, 5179, 699000, 23010, 23179), + EUTRAN_ARFCN_FREQUENCY_BAND_13( + EutranBand.BAND_13, 746000, 5180, 5279, 777000, 23180, 23279), + EUTRAN_ARFCN_FREQUENCY_BAND_14( + EutranBand.BAND_14, 758000, 5280, 5379, 788000, 23230, 23379), + EUTRAN_ARFCN_FREQUENCY_BAND_17( + EutranBand.BAND_17, 734000, 5730, 5849, 704000, 23730, 23849), + EUTRAN_ARFCN_FREQUENCY_BAND_18( + EutranBand.BAND_18, 860000, 5850, 5999, 815000, 23850, 23999), + EUTRAN_ARFCN_FREQUENCY_BAND_19( + EutranBand.BAND_19, 875000, 6000, 6149, 830000, 24000, 24149), + EUTRAN_ARFCN_FREQUENCY_BAND_20( + EutranBand.BAND_20, 791000, 6150, 6449, 832000, 24150, 24449), + EUTRAN_ARFCN_FREQUENCY_BAND_21( + EutranBand.BAND_21, 1495900, 6450, 6599, 1447900, 24450, 24599), + EUTRAN_ARFCN_FREQUENCY_BAND_22( + EutranBand.BAND_22, 3510000, 6600, 7399, 3410000, 24600, 25399), + EUTRAN_ARFCN_FREQUENCY_BAND_23( + EutranBand.BAND_23, 2180000, 7500, 7699, 2000000, 25500, 25699), + EUTRAN_ARFCN_FREQUENCY_BAND_24( + EutranBand.BAND_24, 1525000, 7700, 8039, 1626500, 25700, 26039), + EUTRAN_ARFCN_FREQUENCY_BAND_25( + EutranBand.BAND_25, 1930000, 8040, 8689, 1850000, 26040, 26689), + EUTRAN_ARFCN_FREQUENCY_BAND_26( + EutranBand.BAND_26, 859000, 8690, 9039, 814000, 26690, 27039), + EUTRAN_ARFCN_FREQUENCY_BAND_27( + EutranBand.BAND_27, 852000, 9040, 9209, 807000, 27040, 27209), + EUTRAN_ARFCN_FREQUENCY_BAND_28( + EutranBand.BAND_28, 758000, 9210, 9659, 703000, 27210, 27659), + EUTRAN_ARFCN_FREQUENCY_BAND_30( + EutranBand.BAND_30, 2350000, 9770, 9869, 2305000, 27660, 27759), + EUTRAN_ARFCN_FREQUENCY_BAND_31( + EutranBand.BAND_31, 462500, 9870, 9919, 452500, 27760, 27809), + EUTRAN_ARFCN_FREQUENCY_BAND_33( + EutranBand.BAND_33, 1900000, 36000, 36199, 1900000, 36000, 36199), + EUTRAN_ARFCN_FREQUENCY_BAND_34( + EutranBand.BAND_34, 2010000, 36200, 36349, 2010000, 36200, 36349), + EUTRAN_ARFCN_FREQUENCY_BAND_35( + EutranBand.BAND_35, 1850000, 36350, 36949, 1850000, 36350, 36949), + EUTRAN_ARFCN_FREQUENCY_BAND_36( + EutranBand.BAND_36, 1930000, 36950, 37549, 1930000, 36950, 37549), + EUTRAN_ARFCN_FREQUENCY_BAND_37( + EutranBand.BAND_37, 1910000, 37550, 37749, 1910000, 37550, 37749), + EUTRAN_ARFCN_FREQUENCY_BAND_38( + EutranBand.BAND_38, 2570000, 37750, 38249, 2570000, 37750, 38249), + EUTRAN_ARFCN_FREQUENCY_BAND_39( + EutranBand.BAND_39, 1880000, 38250, 38649, 1880000, 38250, 38649), + EUTRAN_ARFCN_FREQUENCY_BAND_40( + EutranBand.BAND_40, 2300000, 38650, 39649, 2300000, 38650, 39649), + EUTRAN_ARFCN_FREQUENCY_BAND_41( + EutranBand.BAND_41, 2496000, 39650, 41589, 2496000, 39650, 41589), + EUTRAN_ARFCN_FREQUENCY_BAND_42( + EutranBand.BAND_42, 3400000, 41950, 43589, 3400000, 41950, 43589), + EUTRAN_ARFCN_FREQUENCY_BAND_43( + EutranBand.BAND_43, 3600000, 43950, 45589, 3600000, 43950, 45589), + EUTRAN_ARFCN_FREQUENCY_BAND_44( + EutranBand.BAND_44, 703000, 45590, 46589, 703000, 45590, 46589), + EUTRAN_ARFCN_FREQUENCY_BAND_45( + EutranBand.BAND_45, 1447000, 46590, 46789, 1447000, 46590, 46789), + EUTRAN_ARFCN_FREQUENCY_BAND_46( + EutranBand.BAND_46, 5150000, 46790, 54539, 5150000, 46790, 54539), + EUTRAN_ARFCN_FREQUENCY_BAND_47( + EutranBand.BAND_47, 5855000, 54540, 55239, 5855000, 54540, 55239), + EUTRAN_ARFCN_FREQUENCY_BAND_48( + EutranBand.BAND_48, 3550000, 55240, 56739, 3550000, 55240, 56739), + EUTRAN_ARFCN_FREQUENCY_BAND_49( + EutranBand.BAND_49, 3550000, 56740, 58239, 3550000, 56740, 58239), + EUTRAN_ARFCN_FREQUENCY_BAND_50( + EutranBand.BAND_50, 1432000, 58240, 59089, 1432000, 58240, 59089), + EUTRAN_ARFCN_FREQUENCY_BAND_51( + EutranBand.BAND_51, 1427000, 59090, 59139, 1427000, 59090, 59139), + EUTRAN_ARFCN_FREQUENCY_BAND_52( + EutranBand.BAND_52, 3300000, 59140, 60139, 3300000, 59140, 60139), + EUTRAN_ARFCN_FREQUENCY_BAND_53( + EutranBand.BAND_53, 2483500, 60140, 60254, 2483500, 60140, 60254), + EUTRAN_ARFCN_FREQUENCY_BAND_65( + EutranBand.BAND_65, 2110000, 65536, 66435, 1920000, 131072, 131971), + EUTRAN_ARFCN_FREQUENCY_BAND_66( + EutranBand.BAND_66, 2110000, 66436, 67335, 1710000, 131972, 132671), + EUTRAN_ARFCN_FREQUENCY_BAND_68( + EutranBand.BAND_68, 753000, 67536, 67835, 698000, 132672, 132971), + EUTRAN_ARFCN_FREQUENCY_BAND_70( + EutranBand.BAND_70, 1995000, 68336, 68585, 1695000, 132972, 133121), + EUTRAN_ARFCN_FREQUENCY_BAND_71( + EutranBand.BAND_71, 617000, 68586, 68935, 663000, 133122, 133471), + EUTRAN_ARFCN_FREQUENCY_BAND_72( + EutranBand.BAND_72, 461000, 68936, 68985, 451000, 133472, 133521), + EUTRAN_ARFCN_FREQUENCY_BAND_73( + EutranBand.BAND_73, 460000, 68986, 69035, 450000, 133522, 133571), + EUTRAN_ARFCN_FREQUENCY_BAND_74( + EutranBand.BAND_74, 1475000, 69036, 69465, 1427000, 133572, 134001), + EUTRAN_ARFCN_FREQUENCY_BAND_85( + EutranBand.BAND_85, 728000, 70366, 70545, 698000, 134002, 134181), + EUTRAN_ARFCN_FREQUENCY_BAND_87( + EutranBand.BAND_87, 420000, 70546, 70595, 410000, 134182, 134231), + EUTRAN_ARFCN_FREQUENCY_BAND_88( + EutranBand.BAND_88, 422000, 70596, 70645, 412000, 134231, 134280); + + EutranBandArfcnFrequency(int band, int downlinkLowKhz, int downlinkOffset, + int downlinkRange, int uplinkLowKhz, int uplinkOffset, + int uplinkRange) { + this.band = band; + this.downlinkLowKhz = downlinkLowKhz; + this.downlinkOffset = downlinkOffset; + this.uplinkLowKhz = uplinkLowKhz; + this.uplinkOffset = uplinkOffset; + this.downlinkRange = downlinkRange; + this.uplinkRange = uplinkRange; + } + + int band; + int downlinkLowKhz; + int downlinkOffset; + int uplinkLowKhz; + int uplinkOffset; + int downlinkRange; + int uplinkRange; + } + + /** * Frequency bands for CDMA2000. * http://www.3gpp2.org/Public_html/Specs/C.S0057-E_v1.0_Bandclass_Specification.pdf * @hide @@ -320,7 +693,7 @@ public final class AccessNetworkConstants { * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810102/15.08.00_60/ts_13810102v150800p.pdf */ public static final class NgranBands { - /** 3GPP TS 38.101-1, Version 16.2.0, Table 5.2-1: FR1 bands */ + /** 3GPP TS 38.101-1, Version 16.5.0, Table 5.2-1: FR1 bands */ public static final int BAND_1 = android.hardware.radio.V1_5.NgranBands.BAND_1; public static final int BAND_2 = android.hardware.radio.V1_5.NgranBands.BAND_2; public static final int BAND_3 = android.hardware.radio.V1_5.NgranBands.BAND_3; @@ -332,6 +705,7 @@ public final class AccessNetworkConstants { public static final int BAND_18 = android.hardware.radio.V1_5.NgranBands.BAND_18; public static final int BAND_20 = android.hardware.radio.V1_5.NgranBands.BAND_20; public static final int BAND_25 = android.hardware.radio.V1_5.NgranBands.BAND_25; + public static final int BAND_26 = android.hardware.radio.V1_6.NgranBands.BAND_26; public static final int BAND_28 = android.hardware.radio.V1_5.NgranBands.BAND_28; public static final int BAND_29 = android.hardware.radio.V1_5.NgranBands.BAND_29; public static final int BAND_30 = android.hardware.radio.V1_5.NgranBands.BAND_30; @@ -340,9 +714,11 @@ public final class AccessNetworkConstants { public static final int BAND_39 = android.hardware.radio.V1_5.NgranBands.BAND_39; public static final int BAND_40 = android.hardware.radio.V1_5.NgranBands.BAND_40; public static final int BAND_41 = android.hardware.radio.V1_5.NgranBands.BAND_41; + public static final int BAND_46 = android.hardware.radio.V1_6.NgranBands.BAND_46; public static final int BAND_48 = android.hardware.radio.V1_5.NgranBands.BAND_48; public static final int BAND_50 = android.hardware.radio.V1_5.NgranBands.BAND_50; public static final int BAND_51 = android.hardware.radio.V1_5.NgranBands.BAND_51; + public static final int BAND_53 = android.hardware.radio.V1_6.NgranBands.BAND_53; public static final int BAND_65 = android.hardware.radio.V1_5.NgranBands.BAND_65; public static final int BAND_66 = android.hardware.radio.V1_5.NgranBands.BAND_66; public static final int BAND_70 = android.hardware.radio.V1_5.NgranBands.BAND_70; @@ -366,6 +742,7 @@ public final class AccessNetworkConstants { public static final int BAND_93 = android.hardware.radio.V1_5.NgranBands.BAND_93; public static final int BAND_94 = android.hardware.radio.V1_5.NgranBands.BAND_94; public static final int BAND_95 = android.hardware.radio.V1_5.NgranBands.BAND_95; + public static final int BAND_96 = android.hardware.radio.V1_6.NgranBands.BAND_96; /** 3GPP TS 38.101-2, Version 16.2.0, Table 5.2-1: FR2 bands */ public static final int BAND_257 = android.hardware.radio.V1_5.NgranBands.BAND_257; @@ -390,6 +767,7 @@ public final class AccessNetworkConstants { BAND_18, BAND_20, BAND_25, + BAND_26, BAND_28, BAND_29, BAND_30, @@ -398,9 +776,11 @@ public final class AccessNetworkConstants { BAND_39, BAND_40, BAND_41, + BAND_46, BAND_48, BAND_50, BAND_51, + BAND_53, BAND_65, BAND_66, BAND_70, @@ -424,6 +804,7 @@ public final class AccessNetworkConstants { BAND_93, BAND_94, BAND_95, + BAND_96, BAND_257, BAND_258, BAND_260, @@ -464,7 +845,8 @@ public final class AccessNetworkConstants { value = { FREQUENCY_RANGE_GROUP_UNKNOWN, FREQUENCY_RANGE_GROUP_1, - FREQUENCY_RANGE_GROUP_2}) + FREQUENCY_RANGE_GROUP_2 + }) public @interface FrequencyRangeGroup {} /** @@ -489,6 +871,7 @@ public final class AccessNetworkConstants { case BAND_18: case BAND_20: case BAND_25: + case BAND_26: case BAND_28: case BAND_29: case BAND_30: @@ -497,9 +880,11 @@ public final class AccessNetworkConstants { case BAND_39: case BAND_40: case BAND_41: + case BAND_46: case BAND_48: case BAND_50: case BAND_51: + case BAND_53: case BAND_65: case BAND_66: case BAND_70: @@ -523,6 +908,7 @@ public final class AccessNetworkConstants { case BAND_93: case BAND_94: case BAND_95: + case BAND_96: return FREQUENCY_RANGE_GROUP_1; case BAND_257: case BAND_258: @@ -538,6 +924,33 @@ public final class AccessNetworkConstants { private NgranBands() {} } + /** + * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster. + * + * @hide + */ + enum NgranArfcnFrequency { + + NGRAN_ARFCN_FREQUENCY_RANGE_1(5, 0, 0, 0, 599999), + NGRAN_ARFCN_FREQUENCY_RANGE_2(15, 3000000, 600000, 600000, 2016666), + NGRAN_ARFCN_FREQUENCY_RANGE_3(60, 24250080, 2016667, 2016667, 3279165); + + NgranArfcnFrequency(int globalKhz, int rangeOffset, int arfcnOffset, + int rangeFirst, int rangeLast) { + this.globalKhz = globalKhz; + this.rangeOffset = rangeOffset; + this.arfcnOffset = arfcnOffset; + this.rangeFirst = rangeFirst; + this.rangeLast = rangeLast; + } + + int globalKhz; + int rangeOffset; + int arfcnOffset; + int rangeFirst; + int rangeLast; + } + /** @hide */ private AccessNetworkConstants() {}; } diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java index 7661a32f6d5b..f29f3bd352be 100644 --- a/telephony/java/android/telephony/AccessNetworkUtils.java +++ b/telephony/java/android/telephony/AccessNetworkUtils.java @@ -4,12 +4,20 @@ import static android.telephony.ServiceState.DUPLEX_MODE_FDD; import static android.telephony.ServiceState.DUPLEX_MODE_TDD; import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN; +import android.telephony.AccessNetworkConstants.EutranBandArfcnFrequency; import android.telephony.AccessNetworkConstants.EutranBand; import android.telephony.AccessNetworkConstants.GeranBand; +import android.telephony.AccessNetworkConstants.GeranBandArfcnFrequency; +import android.telephony.AccessNetworkConstants.NgranArfcnFrequency; +import android.telephony.AccessNetworkConstants.NgranBands; import android.telephony.AccessNetworkConstants.UtranBand; +import android.telephony.AccessNetworkConstants.UtranBandArfcnFrequency; import android.telephony.ServiceState.DuplexMode; +import android.util.Log; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * Utilities to map between radio constants. @@ -22,9 +30,27 @@ public class AccessNetworkUtils { private AccessNetworkUtils() {} public static final int INVALID_BAND = -1; + public static final int INVALID_FREQUENCY = -1; /** ISO country code of Japan. */ private static final String JAPAN_ISO_COUNTRY_CODE = "jp"; + private static final String TAG = "AccessNetworkUtils"; + + private static final int FREQUENCY_KHZ = 1000; + private static final int FREQUENCY_RANGE_LOW_KHZ = 1000000; + private static final int FREQUENCY_RANGE_MID_KHZ = 3000000; + private static final int FREQUENCY_RANGE_HIGH_KHZ = 6000000; + + private static final Set<Integer> UARFCN_NOT_GENERAL_BAND; + static { + UARFCN_NOT_GENERAL_BAND = new HashSet<Integer>(); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_A); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_B); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_C); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_D); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_E); + UARFCN_NOT_GENERAL_BAND.add(UtranBand.BAND_F); + } /** * Gets the duplex mode for the given EUTRAN operating band. @@ -325,4 +351,403 @@ public class AccessNetworkUtils { } return INVALID_BAND; } + + /** + * Get geran bands from {@link PhysicalChannelConfig#getBand()} + */ + public static int getFrequencyRangeGroupFromGeranBand(@GeranBand.GeranBands int band) { + switch (band) { + case GeranBand.BAND_T380: + case GeranBand.BAND_T410: + case GeranBand.BAND_450: + case GeranBand.BAND_480: + case GeranBand.BAND_710: + case GeranBand.BAND_750: + case GeranBand.BAND_T810: + case GeranBand.BAND_850: + case GeranBand.BAND_P900: + case GeranBand.BAND_E900: + case GeranBand.BAND_R900: + case GeranBand.BAND_ER900: + return ServiceState.FREQUENCY_RANGE_LOW; + case GeranBand.BAND_DCS1800: + case GeranBand.BAND_PCS1900: + return ServiceState.FREQUENCY_RANGE_MID; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get utran bands from {@link PhysicalChannelConfig#getBand()} + */ + public static int getFrequencyRangeGroupFromUtranBand(@UtranBand.UtranBands int band) { + switch (band) { + case UtranBand.BAND_5: + case UtranBand.BAND_6: + case UtranBand.BAND_8: + case UtranBand.BAND_12: + case UtranBand.BAND_13: + case UtranBand.BAND_14: + case UtranBand.BAND_19: + case UtranBand.BAND_20: + case UtranBand.BAND_26: + return ServiceState.FREQUENCY_RANGE_LOW; + case UtranBand.BAND_1: + case UtranBand.BAND_2: + case UtranBand.BAND_3: + case UtranBand.BAND_4: + case UtranBand.BAND_7: + case UtranBand.BAND_9: + case UtranBand.BAND_10: + case UtranBand.BAND_11: + case UtranBand.BAND_21: + case UtranBand.BAND_25: + case UtranBand.BAND_A: + case UtranBand.BAND_B: + case UtranBand.BAND_C: + case UtranBand.BAND_D: + case UtranBand.BAND_E: + case UtranBand.BAND_F: + return ServiceState.FREQUENCY_RANGE_MID; + case UtranBand.BAND_22: + return ServiceState.FREQUENCY_RANGE_HIGH; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get eutran bands from {@link PhysicalChannelConfig#getBand()} + * 3GPP TS 36.101 Table 5.5 EUTRA operating bands + */ + public static int getFrequencyRangeGroupFromEutranBand(@EutranBand.EutranBands int band) { + switch (band) { + case EutranBand.BAND_5: + case EutranBand.BAND_6: + case EutranBand.BAND_8: + case EutranBand.BAND_12: + case EutranBand.BAND_13: + case EutranBand.BAND_14: + case EutranBand.BAND_17: + case EutranBand.BAND_18: + case EutranBand.BAND_19: + case EutranBand.BAND_20: + case EutranBand.BAND_26: + case EutranBand.BAND_27: + case EutranBand.BAND_28: + case EutranBand.BAND_31: + case EutranBand.BAND_44: + case EutranBand.BAND_50: + case EutranBand.BAND_51: + case EutranBand.BAND_68: + case EutranBand.BAND_71: + case EutranBand.BAND_72: + case EutranBand.BAND_73: + case EutranBand.BAND_85: + case EutranBand.BAND_87: + case EutranBand.BAND_88: + return ServiceState.FREQUENCY_RANGE_LOW; + case EutranBand.BAND_1: + case EutranBand.BAND_2: + case EutranBand.BAND_3: + case EutranBand.BAND_4: + case EutranBand.BAND_7: + case EutranBand.BAND_9: + case EutranBand.BAND_10: + case EutranBand.BAND_11: + case EutranBand.BAND_21: + case EutranBand.BAND_23: + case EutranBand.BAND_24: + case EutranBand.BAND_25: + case EutranBand.BAND_30: + case EutranBand.BAND_33: + case EutranBand.BAND_34: + case EutranBand.BAND_35: + case EutranBand.BAND_36: + case EutranBand.BAND_37: + case EutranBand.BAND_38: + case EutranBand.BAND_39: + case EutranBand.BAND_40: + case EutranBand.BAND_41: + case EutranBand.BAND_45: + case EutranBand.BAND_53: + case EutranBand.BAND_65: + case EutranBand.BAND_66: + case EutranBand.BAND_70: + case EutranBand.BAND_74: + return ServiceState.FREQUENCY_RANGE_MID; + case EutranBand.BAND_22: + case EutranBand.BAND_42: + case EutranBand.BAND_43: + case EutranBand.BAND_46: + case EutranBand.BAND_47: + case EutranBand.BAND_48: + case EutranBand.BAND_49: + case EutranBand.BAND_52: + return ServiceState.FREQUENCY_RANGE_HIGH; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * Get ngran band from {@link PhysicalChannelConfig#getBand()} + * 3GPP TS 38.104 Table 5.2-1 NR operating bands in FR1 + * 3GPP TS 38.104 Table 5.2-2 NR operating bands in FR2 + */ + public static int getFrequencyRangeGroupFromNrBand(@NgranBands.NgranBand int band) { + switch (band) { + case NgranBands.BAND_5: + case NgranBands.BAND_8: + case NgranBands.BAND_12: + case NgranBands.BAND_14: + case NgranBands.BAND_18: + case NgranBands.BAND_20: + case NgranBands.BAND_26: + case NgranBands.BAND_28: + case NgranBands.BAND_29: + case NgranBands.BAND_71: + case NgranBands.BAND_81: + case NgranBands.BAND_82: + case NgranBands.BAND_83: + case NgranBands.BAND_89: + return ServiceState.FREQUENCY_RANGE_LOW; + case NgranBands.BAND_1: + case NgranBands.BAND_2: + case NgranBands.BAND_3: + case NgranBands.BAND_7: + case NgranBands.BAND_25: + case NgranBands.BAND_30: + case NgranBands.BAND_34: + case NgranBands.BAND_38: + case NgranBands.BAND_39: + case NgranBands.BAND_40: + case NgranBands.BAND_41: + case NgranBands.BAND_50: + case NgranBands.BAND_51: + case NgranBands.BAND_53: + case NgranBands.BAND_65: + case NgranBands.BAND_66: + case NgranBands.BAND_70: + case NgranBands.BAND_74: + case NgranBands.BAND_75: + case NgranBands.BAND_76: + case NgranBands.BAND_80: + case NgranBands.BAND_84: + case NgranBands.BAND_86: + case NgranBands.BAND_90: + case NgranBands.BAND_91: + case NgranBands.BAND_92: + case NgranBands.BAND_93: + case NgranBands.BAND_94: + case NgranBands.BAND_95: + return ServiceState.FREQUENCY_RANGE_MID; + case NgranBands.BAND_46: + case NgranBands.BAND_48: + case NgranBands.BAND_77: + case NgranBands.BAND_78: + case NgranBands.BAND_79: + return ServiceState.FREQUENCY_RANGE_HIGH; + case NgranBands.BAND_96: + case NgranBands.BAND_257: + case NgranBands.BAND_258: + case NgranBands.BAND_260: + case NgranBands.BAND_261: + return ServiceState.FREQUENCY_RANGE_MMWAVE; + default: + return ServiceState.FREQUENCY_RANGE_UNKNOWN; + } + } + + /** + * 3GPP TS 38.104 Table 5.4.2.1-1 NR-ARFCN parameters for the global frequency raster. + * Formula of NR-ARFCN convert to actual frequency: + * Actual frequency(kHz) = (RANGE_OFFSET + GLOBAL_KHZ * (ARFCN - ARFCN_OFFSET)) + */ + public static int getFrequencyFromNrArfcn(int nrArfcn) { + + int globalKhz = 0; + int rangeOffset = 0; + int arfcnOffset = 0; + for (NgranArfcnFrequency nrArfcnFrequency : AccessNetworkConstants. + NgranArfcnFrequency.values()) { + if (nrArfcn >= nrArfcnFrequency.rangeFirst + && nrArfcn <= nrArfcnFrequency.rangeLast) { + globalKhz = nrArfcnFrequency.globalKhz; + rangeOffset = nrArfcnFrequency.rangeOffset; + arfcnOffset = nrArfcnFrequency.arfcnOffset; + break; + } + } + return rangeOffset + globalKhz * (nrArfcn - arfcnOffset); + } + + /** + * Get actual frequency from E-UTRA ARFCN. + */ + public static int getFrequencyFromEarfcn(int band, int earfcn, boolean isUplink) { + + int low = 0; + int offset = 0; + for (EutranBandArfcnFrequency earfcnFrequency : EutranBandArfcnFrequency.values()) { + if (band == earfcnFrequency.band) { + if (isInEarfcnRange(earfcn, earfcnFrequency, isUplink)) { + low = isUplink ? earfcnFrequency.uplinkLowKhz : earfcnFrequency.downlinkLowKhz; + offset = isUplink ? earfcnFrequency.uplinkOffset + : earfcnFrequency.downlinkOffset; + break; + } else { + Log.e(TAG, "Band and the range of EARFCN are not consistent."); + return INVALID_FREQUENCY; + } + } + } + return convertEarfcnToFrequency(low, earfcn, offset); + } + + /** + * 3GPP TS 36.101 Table 5.7.3-1 E-UTRA channel numbers. + * Formula of E-UTRA ARFCN convert to actual frequency: + * Actual frequency(kHz) = (DOWNLINK_LOW + 0.1 * (ARFCN - DOWNLINK_OFFSET)) * FREQUENCY_KHZ + * Actual frequency(kHz) = (UPLINK_LOW + 0.1 * (ARFCN - UPLINK_OFFSET)) * FREQUENCY_KHZ + */ + private static int convertEarfcnToFrequency(int low, int earfcn, int offset) { + return low + 100 * (earfcn - offset); + } + + private static boolean isInEarfcnRange(int earfcn, EutranBandArfcnFrequency earfcnFrequency, + boolean isUplink) { + if (isUplink) { + return earfcn >= earfcnFrequency.uplinkOffset && earfcn <= earfcnFrequency.uplinkRange; + } else { + return earfcn >= earfcnFrequency.downlinkOffset + && earfcn <= earfcnFrequency.downlinkRange; + } + } + + /** + * Get actual frequency from UTRA ARFCN. + */ + public static int getFrequencyFromUarfcn(int band, int uarfcn, boolean isUplink) { + + int offsetKhz = 0; + for (UtranBandArfcnFrequency uarfcnFrequency : AccessNetworkConstants. + UtranBandArfcnFrequency.values()) { + if (band == uarfcnFrequency.band) { + if (isInUarfcnRange(uarfcn, uarfcnFrequency, isUplink)) { + offsetKhz = isUplink ? uarfcnFrequency.uplinkOffset + : uarfcnFrequency.downlinkOffset; + break; + } else { + Log.e(TAG, "Band and the range of UARFCN are not consistent."); + return INVALID_FREQUENCY; + } + } + } + + if (!UARFCN_NOT_GENERAL_BAND.contains(band)) { + return convertUarfcnToFrequency(offsetKhz, uarfcn); + } else { + return convertUarfcnTddToFrequency(band, uarfcn); + } + } + + /** + * 3GPP TS 25.101, Table 5.1 UARFCN definition (general). + * Formula of UTRA ARFCN convert to actual frequency: + * For general bands: + * Downlink actual frequency(kHz) = (DOWNLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ + * Uplink actual frequency(kHz) = (UPLINK_OFFSET + 0.2 * ARFCN) * FREQUENCY_KHZ + */ + private static int convertUarfcnToFrequency(int offsetKhz, int uarfcn) { + return offsetKhz + (200 * uarfcn); + } + + /** + * 3GPP TS 25.102, Table 5.2 UTRA Absolute Radio Frequency Channel Number 1.28 Mcps TDD Option. + * For FDD bands A, B, C, E, F: + * Actual frequency(kHz) = 5 * ARFCN * FREQUENCY_KHZ + * For TDD bands D: + * Actual frequency(kHz) = (5 * (ARFCN - 2150.1MHz)) * FREQUENCY_KHZ + */ + private static int convertUarfcnTddToFrequency(int band, int uarfcn) { + if (band != UtranBand.BAND_D) { + return 5 * uarfcn * FREQUENCY_KHZ; + } else { + return 5 * ((FREQUENCY_KHZ * uarfcn) - 2150100); + } + } + + private static boolean isInUarfcnRange(int uarfcn, UtranBandArfcnFrequency uarfcnFrequency, + boolean isUplink) { + if (isUplink) { + return uarfcn >= uarfcnFrequency.uplinkRangeFirst + && uarfcn <= uarfcnFrequency.uplinkRangeLast; + } else { + if (uarfcnFrequency.downlinkRangeFirst != 0 && uarfcnFrequency.downlinkRangeLast != 0) { + return uarfcn >= uarfcnFrequency.downlinkRangeFirst + && uarfcn <= uarfcnFrequency.downlinkRangeLast; + } else { + // BAND_C, BAND_D, BAND_E and BAND_F do not have the downlink range. + return true; + } + } + } + + /** + * Get actual frequency from GERAN ARFCN. + */ + public static int getFrequencyFromArfcn(int band, int arfcn, boolean isUplink) { + + int uplinkFrequencyFirst = 0; + int arfcnOffset = 0; + int downlinkOffset = 0; + int frequency = 0; + for (GeranBandArfcnFrequency arfcnFrequency : AccessNetworkConstants. + GeranBandArfcnFrequency.values()) { + if (band == arfcnFrequency.band) { + if (arfcn >= arfcnFrequency.arfcnRangeFirst + && arfcn <= arfcnFrequency.arfcnRangeLast) { + uplinkFrequencyFirst = arfcnFrequency.uplinkFrequencyFirst; + downlinkOffset = arfcnFrequency.downlinkOffset; + arfcnOffset = arfcnFrequency.arfcnOffset; + frequency = convertArfcnToFrequency(arfcn, uplinkFrequencyFirst, + arfcnOffset); + break; + } else { + Log.e(TAG, "Band and the range of ARFCN are not consistent."); + return INVALID_FREQUENCY; + } + } + } + + return isUplink ? frequency : frequency + downlinkOffset; + } + + /** + * 3GPP TS 45.005 Table 2-1 Dynamically mapped ARFCN + * Formula of Geran ARFCN convert to actual frequency: + * Uplink actual frequency(kHz) = + * (UPLINK_FREQUENCY_FIRST + 0.2 * (ARFCN - ARFCN_RANGE_FIRST)) * FREQUENCY_KHZ + * Downlink actual frequency(kHz) = Uplink actual frequency + 10 + */ + private static int convertArfcnToFrequency(int arfcn, int uplinkFrequencyFirstKhz, + int arfcnOffset) { + return uplinkFrequencyFirstKhz + 200 * (arfcn - arfcnOffset); + } + + public static int getFrequencyRangeFromArfcn(int frequency) { + if (frequency < FREQUENCY_RANGE_LOW_KHZ) { + return ServiceState.FREQUENCY_RANGE_LOW; + } else if (frequency < FREQUENCY_RANGE_MID_KHZ + && frequency >= FREQUENCY_RANGE_LOW_KHZ) { + return ServiceState.FREQUENCY_RANGE_MID; + } else if (frequency < FREQUENCY_RANGE_HIGH_KHZ + && frequency >= FREQUENCY_RANGE_MID_KHZ) { + return ServiceState.FREQUENCY_RANGE_HIGH; + } else { + return ServiceState.FREQUENCY_RANGE_MMWAVE; + } + } } diff --git a/telephony/java/android/telephony/CarrierBandwidth.java b/telephony/java/android/telephony/CarrierBandwidth.java index 17747a3919ee..b153fefce6e3 100644 --- a/telephony/java/android/telephony/CarrierBandwidth.java +++ b/telephony/java/android/telephony/CarrierBandwidth.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -102,7 +103,7 @@ public final class CarrierBandwidth implements Parcelable { /** * Retrieves the upstream bandwidth for the primary network in Kbps. This always only refers to * the estimated first hop transport bandwidth. - * This will be INVALID if the network is not connected + * This will be {@link #INVALID} if the network is not connected * * @return The estimated first hop upstream (device to network) bandwidth. */ @@ -113,7 +114,7 @@ public final class CarrierBandwidth implements Parcelable { /** * Retrieves the downstream bandwidth for the primary network in Kbps. This always only refers * to the estimated first hop transport bandwidth. - * This will be INVALID if the network is not connected + * This will be {@link #INVALID} if the network is not connected * * @return The estimated first hop downstream (network to device) bandwidth. */ @@ -124,10 +125,19 @@ public final class CarrierBandwidth implements Parcelable { /** * Retrieves the upstream bandwidth for the secondary network in Kbps. This always only refers * to the estimated first hop transport bandwidth. - * This will be INVALID if the network is not connected + * <p/> + * This will be {@link #INVALID} if either are the case: + * <ol> + * <li>The network is not connected</li> + * <li>The device does not support + * {@link android.telephony.TelephonyManager#CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE}.</li> + * </ol> * * @return The estimated first hop upstream (device to network) bandwidth. */ + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE) public int getSecondaryDownlinkCapacityKbps() { return mSecondaryDownlinkCapacityKbps; } @@ -135,10 +145,18 @@ public final class CarrierBandwidth implements Parcelable { /** * Retrieves the downstream bandwidth for the secondary network in Kbps. This always only * refers to the estimated first hop transport bandwidth. - * This will be INVALID if the network is not connected - * + * <p/> + * This will be {@link #INVALID} if either are the case: + * <ol> + * <li>The network is not connected</li> + * <li>The device does not support + * {@link android.telephony.TelephonyManager#CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE}.</li> + * </ol> * @return The estimated first hop downstream (network to device) bandwidth. */ + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE) public int getSecondaryUplinkCapacityKbps() { return mSecondaryUplinkCapacityKbps; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 74b2aad5293e..3a9896a5a91d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2774,6 +2774,30 @@ public class CarrierConfigManager { public static final String IMSI_KEY_DOWNLOAD_URL_STRING = "imsi_key_download_url_string"; /** + * String representation of a carrier's public key used for IMSI encryption for ePDG. If this + * is provided, the device will use it as a fallback when no key exists on device, but the key + * download will still initiate. + * Example string: + * "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234 + * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----" + * @hide + */ + public static final String IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING = + "imsi_carrier_public_key_epdg_string"; + + /** + * String representation of a carrier's public key used for IMSI encryption for WLAN. If this + * is provided, the device will use it as a fallback when no key exists on device, but the key + * download will still initiate. + * Example string: + * "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234 + * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----" + * @hide + */ + public static final String IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING = + "imsi_carrier_public_key_wlan_string"; + + /** * Identifies if the key is available for WLAN or EPDG or both. The value is a bitmask. * 0 indicates that neither EPDG or WLAN is enabled. * 1 indicates that key type TelephonyManager#KEY_TYPE_EPDG is enabled. @@ -4077,6 +4101,24 @@ public class CarrierConfigManager { "use_lower_mtu_value_if_both_received"; /** + * Determines the default RTT mode. + * + * Upon first boot, when the user has not yet set a value for their preferred RTT mode, + * the value of this config will be sent to the IMS stack. Valid values are the same as for + * {@link Settings.Secure#RTT_CALLING_MODE}. + * + * @hide + */ + public static final String KEY_DEFAULT_RTT_MODE_INT = + "default_rtt_mode_int"; + + /** + * Indicates whether RTT is supported while roaming. + */ + public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = + "rtt_supported_while_roaming_bool"; + + /** * Indicates if auto-configuration server is used for the RCS config * Reference: GSMA RCC.14 */ @@ -4433,6 +4475,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false); sDefaults.putInt(IMSI_KEY_AVAILABILITY_INT, 0); sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null); + sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING, null); + sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING, null); sDefaults.putBoolean(KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL, false); sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null); @@ -4441,6 +4485,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false); sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true); sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false); + sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false); sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true); sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null); @@ -4628,6 +4673,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false); + sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0); } /** diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index d502da9fb9ec..99a77ae5d133 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -915,6 +915,8 @@ public final class DataFailCause { public static final int IPV6_PREFIX_UNAVAILABLE = 0x8CA; /** System preference change back to SRAT during handoff */ public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB; + /** Data call fail due to the slice not being allowed for the data call. */ + public static final int SLICE_REJECTED = 0x8CC; //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2). @@ -985,7 +987,7 @@ public final class DataFailCause { * the authentication failed. */ public static final int IWLAN_IKEV2_AUTH_FAILURE = 0x4001; - /** IKE message timeout, tunnel setup failed due to no response from EPDG */ + /** IKE message timeout, tunnel setup failed due to no response from EPDG */ public static final int IWLAN_IKEV2_MSG_TIMEOUT = 0x4002; /** IKE Certification validation failure */ public static final int IWLAN_IKEV2_CERT_INVALID = 0x4003; @@ -1419,6 +1421,7 @@ public final class DataFailCause { sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED"); sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE"); sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED"); + sFailCauseMap.put(SLICE_REJECTED, "SLICE_REJECTED"); sFailCauseMap.put(IWLAN_PDN_CONNECTION_REJECTION, "IWLAN_PDN_CONNECTION_REJECTION"); sFailCauseMap.put(IWLAN_MAX_CONNECTION_REACHED, "IWLAN_MAX_CONNECTION_REACHED"); sFailCauseMap.put(IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION, diff --git a/telephony/java/android/telephony/ImsiEncryptionInfo.java b/telephony/java/android/telephony/ImsiEncryptionInfo.java index 75a79d62d2aa..4978692d3964 100644 --- a/telephony/java/android/telephony/ImsiEncryptionInfo.java +++ b/telephony/java/android/telephony/ImsiEncryptionInfo.java @@ -163,8 +163,8 @@ public final class ImsiEncryptionInfo implements Parcelable { public String toString(){ return "[ImsiEncryptionInfo " + "mcc=" + mcc - + "mnc=" + mnc - + "publicKey=" + publicKey + + " mnc=" + mnc + + " publicKey=" + publicKey + ", keyIdentifier=" + keyIdentifier + ", keyType=" + keyType + ", expirationTime=" + expirationTime diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index b785037e51c1..6571858fc4ae 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -28,8 +28,6 @@ import java.util.Objects; /** * Define capability of a modem group. That is, the capabilities * are shared between those modems defined by list of modem IDs. - * - * @hide */ public final class PhoneCapability implements Parcelable { // Hardcoded default DSDS capability. diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index ed09d538a3b1..1273aa3abbc9 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -478,7 +478,9 @@ public class PhoneNumberUtils { /** * Compare phone numbers a and b, return true if they're identical enough for caller ID purposes. + * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead */ + @Deprecated public static boolean compare(String a, String b) { // We've used loose comparation at least Eclair, which may change in the future. @@ -489,7 +491,9 @@ public class PhoneNumberUtils { * Compare phone numbers a and b, and return true if they're identical * enough for caller ID purposes. Checks a resource to determine whether * to use a strict or loose comparison algorithm. + * @deprecated use {@link #areSamePhoneNumber(String, String, String)} instead */ + @Deprecated public static boolean compare(Context context, String a, String b) { boolean useStrict = context.getResources().getBoolean( com.android.internal.R.bool.config_use_strict_phone_number_comparation); @@ -3218,7 +3222,7 @@ public class PhoneNumberUtils { } // The conversion map is not defined (this is default). Skip conversion. - if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0 ) { + if (sConvertToEmergencyMap == null || sConvertToEmergencyMap.length == 0) { return number; } @@ -3254,4 +3258,47 @@ public class PhoneNumberUtils { } return number; } + + /** + * Determines if two phone numbers are the same. + * <p> + * Matching is based on <a href="https://github.com/google/libphonenumber>libphonenumber</a>. + * Unlike {@link #compare(String, String)}, matching takes into account national + * dialing plans rather than simply matching the last 7 digits of the two phone numbers. As a + * result, it is expected that some numbers which would match using the previous method will no + * longer match using this new approach. + * + * @param number1 + * @param number2 + * @param defaultCountryIso The lowercase two letter ISO 3166-1 country code. Used when parsing + * the phone numbers where it is not possible to determine the country + * associated with a phone number based on the number alone. It + * is recommended to pass in + * {@link TelephonyManager#getNetworkCountryIso()}. + * @return True if the two given phone number are same. + */ + public static boolean areSamePhoneNumber(@NonNull String number1, + @NonNull String number2, @NonNull String defaultCountryIso) { + PhoneNumberUtil util = PhoneNumberUtil.getInstance(); + PhoneNumber n1; + PhoneNumber n2; + defaultCountryIso = defaultCountryIso.toUpperCase(); + try { + n1 = util.parseAndKeepRawInput(number1, defaultCountryIso); + n2 = util.parseAndKeepRawInput(number2, defaultCountryIso); + } catch (NumberParseException e) { + return false; + } + + PhoneNumberUtil.MatchType matchType = util.isNumberMatch(n1, n2); + if (matchType == PhoneNumberUtil.MatchType.EXACT_MATCH + || matchType == PhoneNumberUtil.MatchType.NSN_MATCH) { + return true; + } else if (matchType == PhoneNumberUtil.MatchType.SHORT_NSN_MATCH) { + return (n1.getNationalNumber() == n2.getNationalNumber() + && n1.getCountryCode() == n2.getCountryCode()); + } else { + return false; + } + } } diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index af62ba4b93a1..9fb098ea8758 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -17,6 +17,8 @@ package android.telephony; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.telephony.Annotation.NetworkType; @@ -26,12 +28,10 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; -/** - * @hide - */ public final class PhysicalChannelConfig implements Parcelable { // TODO(b/72993578) consolidate these enums in a central location. + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({CONNECTION_PRIMARY_SERVING, CONNECTION_SECONDARY_SERVING, CONNECTION_UNKNOWN}) public @interface ConnectionStatus {} @@ -47,7 +47,25 @@ public final class PhysicalChannelConfig implements Parcelable { public static final int CONNECTION_SECONDARY_SERVING = 2; /** Connection status is unknown. */ - public static final int CONNECTION_UNKNOWN = Integer.MAX_VALUE; + public static final int CONNECTION_UNKNOWN = -1; + + /** Channel number is unknown. */ + public static final int CHANNEL_NUMBER_UNKNOWN = -1; + + /** Physical Cell Id is unknown. */ + public static final int PHYSICAL_CELL_ID_UNKNOWN = -1; + + /** Physical Cell Id's maximum value is 1007. */ + public static final int PHYSICAL_CELL_ID_MAXIMUM_VALUE = 1007; + + /** Cell bandwidth is unknown. */ + public static final int CELL_BANDWIDTH_UNKNOWN = 0; + + /** The frequency is unknown. */ + public static final int FREQUENCY_UNKNOWN = -1; + + /** The band is unknown. */ + public static final int BAND_UNKNOWN = 0; /** * Connection status of the cell. @@ -58,15 +76,20 @@ public final class PhysicalChannelConfig implements Parcelable { private int mCellConnectionStatus; /** - * Cell bandwidth, in kHz. + * Downlink cell bandwidth, in kHz. */ private int mCellBandwidthDownlinkKhz; /** + * Uplink cell bandwidth, in kHz. + */ + private int mCellBandwidthUplinkKhz; + + /** * The radio technology for this physical channel. */ @NetworkType - private int mRat; + private int mNetworkType; /** * The rough frequency range for this physical channel. @@ -75,9 +98,24 @@ public final class PhysicalChannelConfig implements Parcelable { private int mFrequencyRange; /** - * The absolute radio frequency channel number, {@link Integer#MAX_VALUE} if unknown. + * The frequency of Downlink. */ - private int mChannelNumber; + private int mDownlinkFrequency; + + /** + * The frequency of Uplink. + */ + private int mUplinkFrequency; + + /** + * Downlink Absolute Radio Frequency Channel Number + */ + private int mDownlinkChannelNumber; + + /** + * Uplink Absolute Radio Frequency Channel Number + */ + private int mUplinkChannelNumber; /** * A list of data calls mapped to this physical channel. An empty list means the physical @@ -86,51 +124,81 @@ public final class PhysicalChannelConfig implements Parcelable { private int[] mContextIds; /** - * The physical cell identifier for this cell - PCI, PSC, {@link Integer#MAX_VALUE} if known. + * The physical cell identifier for this cell - PCI, PSC, {@link #PHYSICAL_CELL_ID_UNKNOWN} */ private int mPhysicalCellId; + /** + * This is the band which is being used. + */ + private int mBand; + @Override public int describeContents() { return 0; } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mCellConnectionStatus); dest.writeInt(mCellBandwidthDownlinkKhz); - dest.writeInt(mRat); - dest.writeInt(mChannelNumber); + dest.writeInt(mCellBandwidthUplinkKhz); + dest.writeInt(mNetworkType); + dest.writeInt(mDownlinkChannelNumber); + dest.writeInt(mUplinkChannelNumber); dest.writeInt(mFrequencyRange); dest.writeIntArray(mContextIds); dest.writeInt(mPhysicalCellId); + dest.writeInt(mBand); } /** - * @return Cell bandwidth, in kHz + * @return Downlink cell bandwidth in kHz, {@link #CELL_BANDWIDTH_UNKNOWN} if unknown. */ - public int getCellBandwidthDownlink() { + @IntRange(from = 1) + public int getCellBandwidthDownlinkKhz() { return mCellBandwidthDownlinkKhz; } /** + * @return Uplink cell bandwidth in kHz, {@link #CELL_BANDWIDTH_UNKNOWN} if unknown. + */ + @IntRange(from = 1) + public int getCellBandwidthUplinkKhz() { + return mCellBandwidthUplinkKhz; + } + + /** * Get the list of data call ids mapped to this physical channel. This list is sorted into * ascending numerical order. Each id in this list must match the id in * {@link com.android.internal.telephony.dataconnection.DataConnection}. An empty list means the * physical channel has no data call mapped to it. * - * @return an integer list indicates the data call ids. + * @return an integer list indicates the data call ids, + * @hide */ public int[] getContextIds() { return mContextIds; } /** - * @return the rough frequency range for this physical channel. + * @return the absolute radio frequency channel number for this physical channel, + * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + * @deprecated Use {@link #getDownlinkChannelNumber()} to get the channel number. + */ + @Deprecated + public int getChannelNumber() { + return getDownlinkChannelNumber(); + } + + /** + * @return the rough frequency range for this physical channel, + * {@link ServiceState#FREQUENCY_RANGE_UNKNOWN} if unknown. * @see {@link ServiceState#FREQUENCY_RANGE_LOW} * @see {@link ServiceState#FREQUENCY_RANGE_MID} * @see {@link ServiceState#FREQUENCY_RANGE_HIGH} * @see {@link ServiceState#FREQUENCY_RANGE_MMWAVE} + * @hide */ @ServiceState.FrequencyRange public int getFrequencyRange() { @@ -138,11 +206,48 @@ public final class PhysicalChannelConfig implements Parcelable { } /** - * @return the absolute radio frequency channel number for this physical channel, - * {@link Integer#MAX_VALUE} if unknown. + * @return Downlink Absolute Radio Frequency Channel Number, + * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. */ - public int getChannelNumber() { - return mChannelNumber; + @IntRange(from = 0) + public int getDownlinkChannelNumber() { + return mDownlinkChannelNumber; + } + + /** + * @return Uplink Absolute Radio Frequency Channel Number, + * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getUplinkChannelNumber() { + return mUplinkChannelNumber; + } + + /** + * The valid bands are {@link AccessNetworkConstants.GeranBand}, + * {@link AccessNetworkConstants.UtranBand}, {@link AccessNetworkConstants.EutranBand} and + * {@link AccessNetworkConstants.NgranBands}. + * + * @return the frequency band, {@link #BAND_UNKNOWN} if unknown. */ + @IntRange(from = 1, to = 261) + public int getBand() { + return mBand; + } + + /** + * @return The downlink frequency in kHz, {@link #FREQUENCY_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getDownlinkFrequencyKhz() { + return mDownlinkFrequency; + } + + /** + * @return The uplink frequency in kHz, {@link #FREQUENCY_UNKNOWN} if unknown. + */ + @IntRange(from = 0) + public int getUplinkFrequencyKhz() { + return mUplinkFrequency; } /** @@ -152,19 +257,24 @@ public final class PhysicalChannelConfig implements Parcelable { * In EUTRAN, this value is physical layer cell identity. The range is [0, 503]. * Reference: 3GPP TS 36.211 section 6.11. * - * In 5G RAN, this value is physical layer cell identity. The range is [0, 1008]. + * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007]. * Reference: 3GPP TS 38.211 section 7.4.2.1. * - * @return the physical cell identifier for this cell, {@link Integer#MAX_VALUE} if unknown. + * @return the physical cell identifier for this cell, {@link #PHYSICAL_CELL_ID_UNKNOWN} + * if {@link android.telephony.CellInfo#UNAVAILABLE}. */ + @IntRange(from = 0, to = 1007) public int getPhysicalCellId() { return mPhysicalCellId; } - /**The radio technology for this physical channel. */ + /** + * @return The network type for this physical channel, + * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if unknown. + */ @NetworkType - public int getRat() { - return mRat; + public int getNetworkType() { + return mNetworkType; } /** @@ -174,14 +284,17 @@ public final class PhysicalChannelConfig implements Parcelable { * @see #CONNECTION_SECONDARY_SERVING * @see #CONNECTION_UNKNOWN * - * @return Connection status of the cell + * @return Connection status of the cell, {@link #CONNECTION_UNKNOWN} if unknown. */ @ConnectionStatus public int getConnectionStatus() { return mCellConnectionStatus; } - /** @return String representation of the connection status */ + /** + * @return String representation of the connection status + * @hide + */ private String getConnectionStatusString() { switch(mCellConnectionStatus) { case CONNECTION_PRIMARY_SERVING: @@ -195,6 +308,97 @@ public final class PhysicalChannelConfig implements Parcelable { } } + private void setDownlinkFrequency() { + switch (mNetworkType) { + case TelephonyManager.NETWORK_TYPE_NR: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromNrArfcn( + mDownlinkChannelNumber); + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn( + mBand, mDownlinkChannelNumber, false); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromUarfcn( + mBand, mDownlinkChannelNumber, false); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mDownlinkFrequency = AccessNetworkUtils.getFrequencyFromArfcn( + mBand, mDownlinkChannelNumber, false); + break; + } + } + + private void setUplinkFrequency() { + switch (mNetworkType){ + case TelephonyManager.NETWORK_TYPE_NR: + mUplinkFrequency = mDownlinkFrequency; + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn( + mBand, mUplinkChannelNumber, true); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromUarfcn( + mBand, mUplinkChannelNumber, true); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mUplinkFrequency = AccessNetworkUtils.getFrequencyFromArfcn( + mBand, mUplinkChannelNumber, true); + break; + } + } + + private void setFrequencyRange() { + if (mFrequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) { + return; + } + + switch (mNetworkType) { + case TelephonyManager.NETWORK_TYPE_NR: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromNrBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_LTE: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromEutranBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromUtranBand(mBand); + break; + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_GSM: + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeGroupFromGeranBand(mBand); + break; + default: + mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; + break; + } + + if (mFrequencyRange == ServiceState.FREQUENCY_RANGE_UNKNOWN) { + mFrequencyRange = AccessNetworkUtils.getFrequencyRangeFromArfcn( + mDownlinkFrequency); + } + } + @Override public boolean equals(Object o) { if (this == o) { @@ -208,30 +412,37 @@ public final class PhysicalChannelConfig implements Parcelable { PhysicalChannelConfig config = (PhysicalChannelConfig) o; return mCellConnectionStatus == config.mCellConnectionStatus && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz - && mRat == config.mRat + && mCellBandwidthUplinkKhz == config.mCellBandwidthUplinkKhz + && mNetworkType == config.mNetworkType && mFrequencyRange == config.mFrequencyRange - && mChannelNumber == config.mChannelNumber + && mDownlinkChannelNumber == config.mDownlinkChannelNumber + && mUplinkChannelNumber == config.mUplinkChannelNumber && mPhysicalCellId == config.mPhysicalCellId - && Arrays.equals(mContextIds, config.mContextIds); + && Arrays.equals(mContextIds, config.mContextIds) + && mBand == config.mBand + && mDownlinkFrequency == config.mDownlinkFrequency + && mUplinkFrequency == config.mUplinkFrequency; } @Override public int hashCode() { return Objects.hash( - mCellConnectionStatus, mCellBandwidthDownlinkKhz, mRat, mFrequencyRange, - mChannelNumber, mPhysicalCellId, mContextIds); + mCellConnectionStatus, mCellBandwidthDownlinkKhz, mCellBandwidthUplinkKhz, + mNetworkType, mFrequencyRange, mDownlinkChannelNumber, mUplinkChannelNumber, + mContextIds, mPhysicalCellId, mBand, mDownlinkFrequency, mUplinkFrequency); } - public static final @android.annotation.NonNull Parcelable.Creator<PhysicalChannelConfig> CREATOR = - new Parcelable.Creator<PhysicalChannelConfig>() { - public PhysicalChannelConfig createFromParcel(Parcel in) { - return new PhysicalChannelConfig(in); - } + public static final + @android.annotation.NonNull Parcelable.Creator<PhysicalChannelConfig> CREATOR = + new Parcelable.Creator<PhysicalChannelConfig>() { + public PhysicalChannelConfig createFromParcel(Parcel in) { + return new PhysicalChannelConfig(in); + } - public PhysicalChannelConfig[] newArray(int size) { - return new PhysicalChannelConfig[size]; - } - }; + public PhysicalChannelConfig[] newArray(int size) { + return new PhysicalChannelConfig[size]; + } + }; @Override public String toString() { @@ -240,16 +451,26 @@ public final class PhysicalChannelConfig implements Parcelable { .append(getConnectionStatusString()) .append(",mCellBandwidthDownlinkKhz=") .append(mCellBandwidthDownlinkKhz) - .append(",mRat=") - .append(TelephonyManager.getNetworkTypeName(mRat)) + .append(",mCellBandwidthUplinkKhz=") + .append(mCellBandwidthUplinkKhz) + .append(",mNetworkType=") + .append(TelephonyManager.getNetworkTypeName(mNetworkType)) .append(",mFrequencyRange=") .append(ServiceState.frequencyRangeToString(mFrequencyRange)) - .append(",mChannelNumber=") - .append(mChannelNumber) + .append(",mDownlinkChannelNumber=") + .append(mDownlinkChannelNumber) + .append(",mUplinkChannelNumber=") + .append(mUplinkChannelNumber) .append(",mContextIds=") .append(Arrays.toString(mContextIds)) .append(",mPhysicalCellId=") .append(mPhysicalCellId) + .append(",mBand=") + .append(mBand) + .append(",mDownlinkFrequency=") + .append(mDownlinkFrequency) + .append(",mUplinkFrequency=") + .append(mUplinkFrequency) .append("}") .toString(); } @@ -257,89 +478,143 @@ public final class PhysicalChannelConfig implements Parcelable { private PhysicalChannelConfig(Parcel in) { mCellConnectionStatus = in.readInt(); mCellBandwidthDownlinkKhz = in.readInt(); - mRat = in.readInt(); - mChannelNumber = in.readInt(); + mCellBandwidthUplinkKhz = in.readInt(); + mNetworkType = in.readInt(); + mDownlinkChannelNumber = in.readInt(); + mUplinkChannelNumber = in.readInt(); mFrequencyRange = in.readInt(); mContextIds = in.createIntArray(); mPhysicalCellId = in.readInt(); + mBand = in.readInt(); + if (mBand > BAND_UNKNOWN) { + setDownlinkFrequency(); + setUplinkFrequency(); + setFrequencyRange(); + } } private PhysicalChannelConfig(Builder builder) { mCellConnectionStatus = builder.mCellConnectionStatus; mCellBandwidthDownlinkKhz = builder.mCellBandwidthDownlinkKhz; - mRat = builder.mRat; - mChannelNumber = builder.mChannelNumber; + mCellBandwidthUplinkKhz = builder.mCellBandwidthUplinkKhz; + mNetworkType = builder.mNetworkType; + mDownlinkChannelNumber = builder.mDownlinkChannelNumber; + mUplinkChannelNumber = builder.mUplinkChannelNumber; mFrequencyRange = builder.mFrequencyRange; mContextIds = builder.mContextIds; mPhysicalCellId = builder.mPhysicalCellId; + mBand = builder.mBand; + if (mBand > BAND_UNKNOWN) { + setDownlinkFrequency(); + setUplinkFrequency(); + setFrequencyRange(); + } } - /** The builder of {@code PhysicalChannelConfig}. */ + /** + * The builder of {@code PhysicalChannelConfig}. + * @hide + */ public static final class Builder { - private int mRat; + private int mNetworkType; private int mFrequencyRange; - private int mChannelNumber; + private int mDownlinkChannelNumber; + private int mUplinkChannelNumber; private int mCellBandwidthDownlinkKhz; + private int mCellBandwidthUplinkKhz; private int mCellConnectionStatus; private int[] mContextIds; private int mPhysicalCellId; + private int mBand; - /** @hide */ public Builder() { - mRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN; + mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; - mChannelNumber = Integer.MAX_VALUE; - mCellBandwidthDownlinkKhz = 0; + mDownlinkChannelNumber = CHANNEL_NUMBER_UNKNOWN; + mUplinkChannelNumber = CHANNEL_NUMBER_UNKNOWN; + mCellBandwidthDownlinkKhz = CELL_BANDWIDTH_UNKNOWN; + mCellBandwidthUplinkKhz = CELL_BANDWIDTH_UNKNOWN; mCellConnectionStatus = CONNECTION_UNKNOWN; mContextIds = new int[0]; - mPhysicalCellId = Integer.MAX_VALUE; + mPhysicalCellId = PHYSICAL_CELL_ID_UNKNOWN; + mBand = BAND_UNKNOWN; } - /** @hide */ public PhysicalChannelConfig build() { return new PhysicalChannelConfig(this); } - /** @hide */ - public Builder setRat(int rat) { - this.mRat = rat; + public @NonNull Builder setNetworkType(@NetworkType int networkType) { + if (!TelephonyManager.isNetworkTypeValid(networkType)) { + throw new IllegalArgumentException("Network type: " + networkType + " is invalid."); + } + mNetworkType = networkType; + return this; + } + + public @NonNull Builder setFrequencyRange(int frequencyRange) { + if (!ServiceState.isFrequencyRangeValid(frequencyRange)) { + throw new IllegalArgumentException("Frequency range: " + frequencyRange + + " is invalid."); + } + mFrequencyRange = frequencyRange; + return this; + } + + public @NonNull Builder setDownlinkChannelNumber(int downlinkChannelNumber) { + mDownlinkChannelNumber = downlinkChannelNumber; return this; } - /** @hide */ - public Builder setFrequencyRange(int frequencyRange) { - this.mFrequencyRange = frequencyRange; + public @NonNull Builder setUplinkChannelNumber(int uplinkChannelNumber) { + mUplinkChannelNumber = uplinkChannelNumber; return this; } - /** @hide */ - public Builder setChannelNumber(int channelNumber) { - this.mChannelNumber = channelNumber; + public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { + if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) { + throw new IllegalArgumentException("Cell downlink bandwidth(kHz): " + + cellBandwidthDownlinkKhz + " is invalid."); + } + mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; return this; } - /** @hide */ - public Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { - this.mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; + public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) { + if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) { + throw new IllegalArgumentException("Cell uplink bandwidth(kHz): "+ + cellBandwidthUplinkKhz +" is invalid."); + } + mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; return this; } - /** @hide */ - public Builder setCellConnectionStatus(int connectionStatus) { - this.mCellConnectionStatus = connectionStatus; + public @NonNull Builder setCellConnectionStatus(int connectionStatus) { + mCellConnectionStatus = connectionStatus; return this; } - /** @hide */ - public Builder setContextIds(int[] contextIds) { + public @NonNull Builder setContextIds(int[] contextIds) { if (contextIds != null) Arrays.sort(contextIds); - this.mContextIds = contextIds; + mContextIds = contextIds; return this; } - /** @hide */ - public Builder setPhysicalCellId(int physicalCellId) { - this.mPhysicalCellId = physicalCellId; + public @NonNull Builder setPhysicalCellId(int physicalCellId) { + if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) { + throw new IllegalArgumentException("Physical cell Id: " + physicalCellId + + " is over limit."); + } + mPhysicalCellId = physicalCellId; + return this; + } + + public @NonNull Builder setBand(int band) { + if (band <= BAND_UNKNOWN) { + throw new IllegalArgumentException("Band: " + band + + " is invalid."); + } + mBand = band; return this; } } diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java index 250d9e8b212e..3b4cf75e7919 100644 --- a/telephony/java/android/telephony/PreciseDisconnectCause.java +++ b/telephony/java/android/telephony/PreciseDisconnectCause.java @@ -121,7 +121,7 @@ public final class PreciseDisconnectCause { public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57; /** The requested bearer capability is not available at this time. */ public static final int BEARER_NOT_AVAIL = 58; - /** The service option is not availble at this time. */ + /** The service option is not available at this time. */ public static final int SERVICE_OPTION_NOT_AVAILABLE = 63; /** The equipment sending this cause does not support the bearer capability requested. */ public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65; diff --git a/telephony/java/android/telephony/RadioInterfaceCapabilities.java b/telephony/java/android/telephony/RadioInterfaceCapabilities.java new file mode 100644 index 000000000000..7c7eb9fbbeb2 --- /dev/null +++ b/telephony/java/android/telephony/RadioInterfaceCapabilities.java @@ -0,0 +1,53 @@ +/* + * 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.telephony; + +import android.util.ArraySet; + +/** + * Contains the set of supported capabilities that the Radio Interface supports on this device. + * + * @hide + */ +public class RadioInterfaceCapabilities { + + private final ArraySet<String> mSupportedCapabilities; + + + public RadioInterfaceCapabilities() { + mSupportedCapabilities = new ArraySet<>(); + } + + /** + * Marks a capability as supported + * + * @param capabilityName the name of the capability + */ + public void addSupportedCapability( + @TelephonyManager.RadioInterfaceCapability String capabilityName) { + mSupportedCapabilities.add(capabilityName); + } + + /** + * Whether the capability is supported + * + * @param capabilityName the name of the capability + */ + public boolean isSupported(String capabilityName) { + return mSupportedCapabilities.contains(capabilityName); + } +} diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index dedb1afa2495..f110daecd952 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -2111,4 +2111,23 @@ public class ServiceState implements Parcelable { } return false; } + + /** + * The frequency range is valid or not. + * + * @param frequencyRange The frequency range {@link FrequencyRange}. + * @return {@code true} if valid, {@code false} otherwise. + * + * @hide + */ + public static boolean isFrequencyRangeValid(int frequencyRange) { + if (frequencyRange == FREQUENCY_RANGE_LOW + || frequencyRange == FREQUENCY_RANGE_MID + || frequencyRange == FREQUENCY_RANGE_HIGH + || frequencyRange == FREQUENCY_RANGE_MMWAVE) { + return true; + } else { + return false; + } + } } diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl b/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl new file mode 100644 index 000000000000..a45de2e58dd1 --- /dev/null +++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl @@ -0,0 +1,20 @@ +/* +** +** 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.telephony; + +parcelable SignalStrengthUpdateRequest; diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java new file mode 100644 index 000000000000..af67ed279fab --- /dev/null +++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java @@ -0,0 +1,272 @@ +/* + * 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.telephony; + +import android.annotation.NonNull; +import android.os.Binder; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * Request used to register {@link SignalThresholdInfo} to be notified when the signal strength + * breach the specified thresholds. + */ +public final class SignalStrengthUpdateRequest implements Parcelable { + /** + * List of SignalThresholdInfo for the request. + */ + private final List<SignalThresholdInfo> mSignalThresholdInfos; + + /** + * Whether the reporting is required for thresholds in the request while device is idle. + */ + private final boolean mIsReportingRequestedWhileIdle; + + /** + * Whether the reporting requested for system thresholds while device is idle. + * + * System signal thresholds are loaded from carrier config items and mainly used for UI + * displaying. By default, they are ignored when device is idle. When setting the value to true, + * modem will continue reporting signal strength changes over the system signal thresholds even + * device is idle. + * + * This should only set to true by the system caller. + */ + private final boolean mIsSystemThresholdReportingRequestedWhileIdle; + + /** + * A IBinder object as a token for server side to check if the request client is still living. + */ + private final IBinder mLiveToken; + + private SignalStrengthUpdateRequest( + @NonNull List<SignalThresholdInfo> signalThresholdInfos, + boolean isReportingRequestedWhileIdle, + boolean isSystemThresholdReportingRequestedWhileIdle) { + validate(signalThresholdInfos); + + mSignalThresholdInfos = signalThresholdInfos; + mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; + mIsSystemThresholdReportingRequestedWhileIdle = + isSystemThresholdReportingRequestedWhileIdle; + mLiveToken = new Binder(); + } + + /** + * Builder class to create {@link SignalStrengthUpdateRequest} object. + */ + public static final class Builder { + private List<SignalThresholdInfo> mSignalThresholdInfos = null; + private boolean mIsReportingRequestedWhileIdle = false; + private boolean mIsSystemThresholdReportingRequestedWhileIdle = false; + + /** + * Set the collection of SignalThresholdInfo for the builder object + * + * @param signalThresholdInfos the collection of SignalThresholdInfo + * + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setSignalThresholdInfos( + @NonNull Collection<SignalThresholdInfo> signalThresholdInfos) { + Objects.requireNonNull(signalThresholdInfos, + "SignalThresholdInfo collection must not be null"); + for (SignalThresholdInfo info : signalThresholdInfos) { + Objects.requireNonNull(info, + "SignalThresholdInfo in the collection must not be null"); + } + + mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos); + // Sort the collection with RAN ascending order, make the ordering not matter for equals + mSignalThresholdInfos.sort( + Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType)); + return this; + } + + /** + * Set the builder object if require reporting on thresholds in this request when device is + * idle. + * + * @param isReportingRequestedWhileIdle true if request reporting when device is idle + * + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setReportingRequestedWhileIdle( + boolean isReportingRequestedWhileIdle) { + mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; + return this; + } + + /** + * Set the builder object if require reporting on the system thresholds when device is idle. + * + * This can only used by the system caller. + * + * @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the + * system thresholds when device is idle + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle( + boolean isSystemThresholdReportingRequestedWhileIdle) { + mIsSystemThresholdReportingRequestedWhileIdle = + isSystemThresholdReportingRequestedWhileIdle; + return this; + } + + /** + * Build a {@link SignalStrengthUpdateRequest} object. + * + * @return the SignalStrengthUpdateRequest object + * + * @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the + * radio access network type in the collection is not unique + */ + public @NonNull SignalStrengthUpdateRequest build() { + return new SignalStrengthUpdateRequest(mSignalThresholdInfos, + mIsReportingRequestedWhileIdle, mIsSystemThresholdReportingRequestedWhileIdle); + } + } + + private SignalStrengthUpdateRequest(Parcel in) { + mSignalThresholdInfos = in.createTypedArrayList(SignalThresholdInfo.CREATOR); + mIsReportingRequestedWhileIdle = in.readBoolean(); + mIsSystemThresholdReportingRequestedWhileIdle = in.readBoolean(); + mLiveToken = in.readStrongBinder(); + } + + /** + * Get the collection of SignalThresholdInfo in the request. + * + * @return the collection of SignalThresholdInfo + */ + @NonNull + public Collection<SignalThresholdInfo> getSignalThresholdInfos() { + return Collections.unmodifiableList(mSignalThresholdInfos); + } + + /** + * Get whether reporting is requested for the threshold in the request while device is idle. + * + * @return true if reporting requested while device is idle + */ + public boolean isReportingRequestedWhileIdle() { + return mIsReportingRequestedWhileIdle; + } + + /** + * @return true if reporting requested for system thresholds while device is idle + * + * @hide + */ + public boolean isSystemThresholdReportingRequestedWhileIdle() { + return mIsSystemThresholdReportingRequestedWhileIdle; + } + + /* + * @return the live token of the request + * + * @hide + */ + public @NonNull IBinder getLiveToken() { + return mLiveToken; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mSignalThresholdInfos); + dest.writeBoolean(mIsReportingRequestedWhileIdle); + dest.writeBoolean(mIsSystemThresholdReportingRequestedWhileIdle); + dest.writeStrongBinder(mLiveToken); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + + if (!(other instanceof SignalStrengthUpdateRequest)) { + return false; + } + + SignalStrengthUpdateRequest request = (SignalStrengthUpdateRequest) other; + return mSignalThresholdInfos.equals(request.mSignalThresholdInfos) + && mIsReportingRequestedWhileIdle == request.mIsReportingRequestedWhileIdle + && mIsSystemThresholdReportingRequestedWhileIdle + == request.mIsSystemThresholdReportingRequestedWhileIdle; + } + + @Override + public int hashCode() { + return Objects.hash(mSignalThresholdInfos, mIsReportingRequestedWhileIdle, + mIsSystemThresholdReportingRequestedWhileIdle); + } + + public static final @NonNull Parcelable.Creator<SignalStrengthUpdateRequest> CREATOR = + new Parcelable.Creator<SignalStrengthUpdateRequest>() { + @Override + public SignalStrengthUpdateRequest createFromParcel(Parcel source) { + return new SignalStrengthUpdateRequest(source); + } + + @Override + public SignalStrengthUpdateRequest[] newArray(int size) { + return new SignalStrengthUpdateRequest[size]; + } + }; + + @Override + public String toString() { + return new StringBuilder("SignalStrengthUpdateRequest{") + .append("mSignalThresholdInfos=") + .append(mSignalThresholdInfos) + .append(" mIsReportingRequestedWhileIdle=") + .append(mIsReportingRequestedWhileIdle) + .append(" mIsSystemThresholdReportingRequestedWhileIdle=") + .append(mIsSystemThresholdReportingRequestedWhileIdle) + .append(" mLiveToken") + .append(mLiveToken) + .append("}").toString(); + } + + /** + * Throw IAE when the RAN in the collection is not unique. + */ + private static void validate(Collection<SignalThresholdInfo> infos) { + Set<Integer> uniqueRan = new HashSet<>(infos.size()); + for (SignalThresholdInfo info : infos) { + final int ran = info.getRadioAccessNetworkType(); + if (!uniqueRan.add(ran)) { + throw new IllegalArgumentException("RAN: " + ran + " is not unique"); + } + } + } +} diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java index f6f6d75c37c6..0059ad6c2426 100644 --- a/telephony/java/android/telephony/SignalThresholdInfo.java +++ b/telephony/java/android/telephony/SignalThresholdInfo.java @@ -28,101 +28,109 @@ import java.util.Objects; /** * Defines the threshold value of the signal strength. - * @hide */ -public class SignalThresholdInfo implements Parcelable { +public final class SignalThresholdInfo implements Parcelable { + + /** + * Unknown signal measurement type. + */ + public static final int SIGNAL_MEASUREMENT_TYPE_UNKNOWN = 0; + /** * Received Signal Strength Indication. * Range: -113 dBm and -51 dBm - * Used RAN: GERAN, CDMA2000 + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#GERAN}, + * {@link AccessNetworkConstants.AccessNetworkType#CDMA2000} * Reference: 3GPP TS 27.007 section 8.5. */ - public static final int SIGNAL_RSSI = 1; + public static final int SIGNAL_MEASUREMENT_TYPE_RSSI = 1; /** * Received Signal Code Power. * Range: -120 dBm to -25 dBm; - * Used RAN: UTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN} * Reference: 3GPP TS 25.123, section 9.1.1.1 */ - public static final int SIGNAL_RSCP = 2; + public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2; /** * Reference Signal Received Power. * Range: -140 dBm to -44 dBm; - * Used RAN: EUTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} * Reference: 3GPP TS 36.133 9.1.4 */ - public static final int SIGNAL_RSRP = 3; + public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3; /** * Reference Signal Received Quality - * Range: -20 dB to -3 dB; - * Used RAN: EUTRAN + * Range: -34 dB to 3 dB; + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} * Reference: 3GPP TS 36.133 9.1.7 */ - public static final int SIGNAL_RSRQ = 4; + public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4; /** * Reference Signal Signal to Noise Ratio * Range: -20 dB to 30 dB; - * Used RAN: EUTRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} */ - public static final int SIGNAL_RSSNR = 5; + public static final int SIGNAL_MEASUREMENT_TYPE_RSSNR = 5; /** * 5G SS reference signal received power. * Range: -140 dBm to -44 dBm. - * Used RAN: NGRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} * Reference: 3GPP TS 38.215. */ - public static final int SIGNAL_SSRSRP = 6; + public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRP = 6; /** * 5G SS reference signal received quality. - * Range: -20 dB to -3 dB. - * Used RAN: NGRAN - * Reference: 3GPP TS 38.215. + * Range: -43 dB to 20 dB. + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} + * Reference: 3GPP TS 38.133 section 10.1.11.1. */ - public static final int SIGNAL_SSRSRQ = 7; + public static final int SIGNAL_MEASUREMENT_TYPE_SSRSRQ = 7; /** * 5G SS signal-to-noise and interference ratio. * Range: -23 dB to 40 dB - * Used RAN: NGRAN + * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#NGRAN} * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1. */ - public static final int SIGNAL_SSSINR = 8; + public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8; /** @hide */ - @IntDef(prefix = { "SIGNAL_" }, value = { - SIGNAL_RSSI, - SIGNAL_RSCP, - SIGNAL_RSRP, - SIGNAL_RSRQ, - SIGNAL_RSSNR, - SIGNAL_SSRSRP, - SIGNAL_SSRSRQ, - SIGNAL_SSSINR + @IntDef(prefix = {"SIGNAL_MEASUREMENT_TYPE_"}, value = { + SIGNAL_MEASUREMENT_TYPE_UNKNOWN, + SIGNAL_MEASUREMENT_TYPE_RSSI, + SIGNAL_MEASUREMENT_TYPE_RSCP, + SIGNAL_MEASUREMENT_TYPE_RSRP, + SIGNAL_MEASUREMENT_TYPE_RSRQ, + SIGNAL_MEASUREMENT_TYPE_RSSNR, + SIGNAL_MEASUREMENT_TYPE_SSRSRP, + SIGNAL_MEASUREMENT_TYPE_SSRSRQ, + SIGNAL_MEASUREMENT_TYPE_SSSINR }) @Retention(RetentionPolicy.SOURCE) - public @interface SignalMeasurementType {} + public @interface SignalMeasurementType { + } @SignalMeasurementType - private int mSignalMeasurement; + private final int mSignalMeasurementType; /** * A hysteresis time in milliseconds to prevent flapping. * A value of 0 disables hysteresis */ - private int mHysteresisMs; + private final int mHysteresisMs; /** * An interval in dB defining the required magnitude change between reports. * hysteresisDb must be smaller than the smallest threshold delta. * An interval value of 0 disables hysteresis. */ - private int mHysteresisDb; + private final int mHysteresisDb; /** * List of threshold values. @@ -130,60 +138,399 @@ public class SignalThresholdInfo implements Parcelable { * The threshold values for which to apply criteria. * A vector size of 0 disables the use of thresholds for reporting. */ - private int[] mThresholds = null; + private final int[] mThresholds; /** * {@code true} means modem must trigger the report based on the criteria; * {@code false} means modem must not trigger the report based on the criteria. */ - private boolean mIsEnabled = true; + private final boolean mIsEnabled; + + /** + * The radio access network type associated with the signal thresholds. + */ + @AccessNetworkConstants.RadioAccessNetworkType + private final int mRan; /** * Indicates the hysteresisMs is disabled. + * + * @hide */ public static final int HYSTERESIS_MS_DISABLED = 0; /** * Indicates the hysteresisDb is disabled. + * + * @hide */ public static final int HYSTERESIS_DB_DISABLED = 0; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}. + * + * @hide + */ + public static final int SIGNAL_RSSI_MIN_VALUE = -113; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}. + * + * @hide + */ + public static final int SIGNAL_RSSI_MAX_VALUE = -51; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}. + * + * @hide + */ + public static final int SIGNAL_RSCP_MIN_VALUE = -120; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSCP}. + * + * @hide + */ + public static final int SIGNAL_RSCP_MAX_VALUE = -25; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}. + * + * @hide + */ + public static final int SIGNAL_RSRP_MIN_VALUE = -140; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRP}. + * + * @hide + */ + public static final int SIGNAL_RSRP_MAX_VALUE = -44; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}. + * + * @hide + */ + public static final int SIGNAL_RSRQ_MIN_VALUE = -34; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSRQ}. + * + * @hide + */ + public static final int SIGNAL_RSRQ_MAX_VALUE = 3; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}. + * + * @hide + */ + public static final int SIGNAL_RSSNR_MIN_VALUE = -20; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSNR}. + * + * @hide + */ + public static final int SIGNAL_RSSNR_MAX_VALUE = 30; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}. + * + * @hide + */ + public static final int SIGNAL_SSRSRP_MIN_VALUE = -140; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRP}. + * + * @hide + */ + public static final int SIGNAL_SSRSRP_MAX_VALUE = -44; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}. + * + * @hide + */ + public static final int SIGNAL_SSRSRQ_MIN_VALUE = -43; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSRSRQ}. + * + * @hide + */ + public static final int SIGNAL_SSRSRQ_MAX_VALUE = 20; + + /** + * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}. + * + * @hide + */ + public static final int SIGNAL_SSSINR_MIN_VALUE = -23; + + /** + * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_SSSINR}. + * + * @hide + */ + public static final int SIGNAL_SSSINR_MAX_VALUE = 40; + + /** + * The minimum number of thresholds allowed in each SignalThresholdInfo. + * + * @hide + */ + public static final int MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 1; + + /** + * The maximum number of thresholds allowed in each SignalThresholdInfo. + * + * @hide + */ + public static final int MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED = 4; + /** * Constructor * - * @param signalMeasurement Signal Measurement Type - * @param hysteresisMs hysteresisMs - * @param hysteresisDb hysteresisDb - * @param thresholds threshold value - * @param isEnabled isEnabled - */ - public SignalThresholdInfo(@SignalMeasurementType int signalMeasurement, - int hysteresisMs, int hysteresisDb, @NonNull int [] thresholds, boolean isEnabled) { - mSignalMeasurement = signalMeasurement; + * @param ran Radio Access Network type + * @param signalMeasurementType Signal Measurement Type + * @param hysteresisMs hysteresisMs + * @param hysteresisDb hysteresisDb + * @param thresholds threshold value + * @param isEnabled isEnabled + */ + private SignalThresholdInfo(@AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int signalMeasurementType, int hysteresisMs, int hysteresisDb, + @NonNull int[] thresholds, boolean isEnabled) { + Objects.requireNonNull(thresholds, "thresholds must not be null"); + validateRanWithMeasurementType(ran, signalMeasurementType); + validateThresholdRange(signalMeasurementType, thresholds); + + mRan = ran; + mSignalMeasurementType = signalMeasurementType; mHysteresisMs = hysteresisMs < 0 ? HYSTERESIS_MS_DISABLED : hysteresisMs; mHysteresisDb = hysteresisDb < 0 ? HYSTERESIS_DB_DISABLED : hysteresisDb; - mThresholds = thresholds == null ? null : thresholds.clone(); + mThresholds = thresholds; mIsEnabled = isEnabled; } - public @SignalMeasurementType int getSignalMeasurement() { - return mSignalMeasurement; + /** + * Builder class to create {@link SignalThresholdInfo} objects. + */ + public static final class Builder { + private int mRan = AccessNetworkConstants.AccessNetworkType.UNKNOWN; + private int mSignalMeasurementType = SIGNAL_MEASUREMENT_TYPE_UNKNOWN; + private int mHysteresisMs = HYSTERESIS_MS_DISABLED; + private int mHysteresisDb = HYSTERESIS_DB_DISABLED; + private int[] mThresholds = null; + private boolean mIsEnabled = false; + + /** + * Set the radio access network type for the builder instance. + * + * @param ran The radio access network type + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setRadioAccessNetworkType( + @AccessNetworkConstants.RadioAccessNetworkType int ran) { + mRan = ran; + return this; + } + + /** + * Set the signal measurement type for the builder instance. + * + * @param signalMeasurementType The signal measurement type + * @return the builder to facilitate the chaining + */ + public @NonNull Builder setSignalMeasurementType( + @SignalMeasurementType int signalMeasurementType) { + mSignalMeasurementType = signalMeasurementType; + return this; + } + + /** + * Set the hysteresis time in milliseconds to prevent flapping. A value of 0 disables + * hysteresis. + * + * @param hysteresisMs the hysteresis time in milliseconds + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setHysteresisMs(int hysteresisMs) { + mHysteresisMs = hysteresisMs; + return this; + } + + /** + * Set the interval in dB defining the required magnitude change between reports. A value of + * zero disabled dB-based hysteresis restrictions. + * + * @param hysteresisDb the interval in dB + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setHysteresisDb(int hysteresisDb) { + mHysteresisDb = hysteresisDb; + return this; + } + + /** + * Set the signal strength thresholds of the corresponding signal measurement type. + * + * The range and unit must reference specific SignalMeasurementType. The length of the + * thresholds should between the numbers return from + * {@link #getMinimumNumberOfThresholdsAllowed()} and + * {@link #getMaximumNumberOfThresholdsAllowed()}. An IllegalArgumentException will throw + * otherwise. + * + * @param thresholds array of integer as the signal threshold values + * @return the builder to facilitate the chaining + * + * @see #SIGNAL_MEASUREMENT_TYPE_RSSI + * @see #SIGNAL_MEASUREMENT_TYPE_RSCP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR + * @see #getThresholds() for more details on signal strength thresholds + */ + public @NonNull Builder setThresholds(@NonNull int[] thresholds) { + Objects.requireNonNull(thresholds, "thresholds must not be null"); + if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) { + throw new IllegalArgumentException( + "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED + + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED); + } + mThresholds = thresholds.clone(); + Arrays.sort(mThresholds); + return this; + } + + /** + * Set the signal strength thresholds for the corresponding signal measurement type without + * the length limitation. + * + * @param thresholds array of integer as the signal threshold values + * @return the builder to facilitate the chaining + * + * @hide + */ + public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) { + Objects.requireNonNull(thresholds, "thresholds must not be null"); + mThresholds = thresholds.clone(); + Arrays.sort(mThresholds); + return this; + } + + + /** + * Set if the modem should trigger the report based on the criteria. + * + * @param isEnabled true if the modem should trigger the report based on the criteria + * @return the builder to facilitate the chaining + * @hide + */ + public @NonNull Builder setIsEnabled(boolean isEnabled) { + mIsEnabled = isEnabled; + return this; + } + + /** + * Build {@link SignalThresholdInfo} object. + * + * @return the SignalThresholdInfo object build out + * + * @throws IllegalArgumentException if the signal measurement type is invalid, any value in + * the thresholds is out of range, or the RAN is not allowed to set with the signal + * measurement type + */ + public @NonNull SignalThresholdInfo build() { + return new SignalThresholdInfo(mRan, mSignalMeasurementType, mHysteresisMs, + mHysteresisDb, mThresholds, mIsEnabled); + } + } + + /** + * Get the radio access network type. + * + * @return radio access network type + */ + public @AccessNetworkConstants.RadioAccessNetworkType int getRadioAccessNetworkType() { + return mRan; + } + + /** + * Get the signal measurement type. + * + * @return the SignalMeasurementType value + */ + public @SignalMeasurementType int getSignalMeasurementType() { + return mSignalMeasurementType; } + /** @hide */ public int getHysteresisMs() { return mHysteresisMs; } + /** @hide */ public int getHysteresisDb() { return mHysteresisDb; } + /** @hide */ public boolean isEnabled() { return mIsEnabled; } - public int[] getThresholds() { - return mThresholds == null ? null : mThresholds.clone(); + /** + * Get the signal strength thresholds. + * + * Signal strength thresholds are a list of integer used for suggesting signal level and signal + * reporting criteria. The range and unit must reference specific SignalMeasurementType. + * + * Please refer to https://source.android.com/devices/tech/connect/signal-strength on how signal + * strength thresholds are used for signal strength reporting. + * + * @return array of integer of the signal thresholds + * + * @see #SIGNAL_MEASUREMENT_TYPE_RSSI + * @see #SIGNAL_MEASUREMENT_TYPE_RSCP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRP + * @see #SIGNAL_MEASUREMENT_TYPE_RSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_RSSNR + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP + * @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ + * @see #SIGNAL_MEASUREMENT_TYPE_SSSINR + */ + public @NonNull int[] getThresholds() { + return mThresholds.clone(); + } + + /** + * Get the minimum number of thresholds allowed in each SignalThresholdInfo. + * + * @return the minimum number of thresholds allowed + */ + public static int getMinimumNumberOfThresholdsAllowed() { + return MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED; + } + + /** + * Get the maximum number of threshold allowed in each SignalThresholdInfo. + * + * @return the maximum number of thresholds allowed + */ + public static int getMaximumNumberOfThresholdsAllowed() { + return MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED; } @Override @@ -192,8 +539,9 @@ public class SignalThresholdInfo implements Parcelable { } @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mSignalMeasurement); + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeInt(mRan); + out.writeInt(mSignalMeasurementType); out.writeInt(mHysteresisMs); out.writeInt(mHysteresisDb); out.writeIntArray(mThresholds); @@ -201,7 +549,8 @@ public class SignalThresholdInfo implements Parcelable { } private SignalThresholdInfo(Parcel in) { - mSignalMeasurement = in.readInt(); + mRan = in.readInt(); + mSignalMeasurementType = in.readInt(); mHysteresisMs = in.readInt(); mHysteresisDb = in.readInt(); mThresholds = in.createIntArray(); @@ -217,7 +566,8 @@ public class SignalThresholdInfo implements Parcelable { } SignalThresholdInfo other = (SignalThresholdInfo) o; - return mSignalMeasurement == other.mSignalMeasurement + return mRan == other.mRan + && mSignalMeasurementType == other.mSignalMeasurementType && mHysteresisMs == other.mHysteresisMs && mHysteresisDb == other.mHysteresisDb && Arrays.equals(mThresholds, other.mThresholds) @@ -226,8 +576,8 @@ public class SignalThresholdInfo implements Parcelable { @Override public int hashCode() { - return Objects.hash( - mSignalMeasurement, mHysteresisMs, mHysteresisDb, mThresholds, mIsEnabled); + return Objects.hash(mRan, mSignalMeasurementType, mHysteresisMs, mHysteresisDb, mThresholds, + mIsEnabled); } public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR = @@ -246,11 +596,83 @@ public class SignalThresholdInfo implements Parcelable { @Override public String toString() { return new StringBuilder("SignalThresholdInfo{") - .append("mSignalMeasurement=").append(mSignalMeasurement) - .append("mHysteresisMs=").append(mSignalMeasurement) - .append("mHysteresisDb=").append(mHysteresisDb) - .append("mThresholds=").append(Arrays.toString(mThresholds)) - .append("mIsEnabled=").append(mIsEnabled) - .append("}").toString(); + .append("mRan=").append(mRan) + .append(" mSignalMeasurementType=").append(mSignalMeasurementType) + .append(" mHysteresisMs=").append(mHysteresisMs) + .append(" mHysteresisDb=").append(mHysteresisDb) + .append(" mThresholds=").append(Arrays.toString(mThresholds)) + .append(" mIsEnabled=").append(mIsEnabled) + .append("}").toString(); + } + + /** + * Return true if signal measurement type is valid and the threshold value is in range. + */ + private static boolean isValidThreshold(@SignalMeasurementType int type, int threshold) { + switch (type) { + case SIGNAL_MEASUREMENT_TYPE_RSSI: + return threshold >= SIGNAL_RSSI_MIN_VALUE && threshold <= SIGNAL_RSSI_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSCP: + return threshold >= SIGNAL_RSCP_MIN_VALUE && threshold <= SIGNAL_RSCP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSRP: + return threshold >= SIGNAL_RSRP_MIN_VALUE && threshold <= SIGNAL_RSRP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSRQ: + return threshold >= SIGNAL_RSRQ_MIN_VALUE && threshold <= SIGNAL_RSRQ_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_RSSNR: + return threshold >= SIGNAL_RSSNR_MIN_VALUE && threshold <= SIGNAL_RSSNR_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSRSRP: + return threshold >= SIGNAL_SSRSRP_MIN_VALUE && threshold <= SIGNAL_SSRSRP_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSRSRQ: + return threshold >= SIGNAL_SSRSRQ_MIN_VALUE && threshold <= SIGNAL_SSRSRQ_MAX_VALUE; + case SIGNAL_MEASUREMENT_TYPE_SSSINR: + return threshold >= SIGNAL_SSSINR_MIN_VALUE && threshold <= SIGNAL_SSSINR_MAX_VALUE; + default: + return false; + } + } + + /** + * Return true if the radio access type is allowed to set with the measurement type. + */ + private static boolean isValidRanWithMeasurementType( + @AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int type) { + switch (type) { + case SIGNAL_MEASUREMENT_TYPE_RSSI: + return ran == AccessNetworkConstants.AccessNetworkType.GERAN + || ran == AccessNetworkConstants.AccessNetworkType.CDMA2000; + case SIGNAL_MEASUREMENT_TYPE_RSCP: + return ran == AccessNetworkConstants.AccessNetworkType.UTRAN; + case SIGNAL_MEASUREMENT_TYPE_RSRP: + case SIGNAL_MEASUREMENT_TYPE_RSRQ: + case SIGNAL_MEASUREMENT_TYPE_RSSNR: + return ran == AccessNetworkConstants.AccessNetworkType.EUTRAN; + case SIGNAL_MEASUREMENT_TYPE_SSRSRP: + case SIGNAL_MEASUREMENT_TYPE_SSRSRQ: + case SIGNAL_MEASUREMENT_TYPE_SSSINR: + return ran == AccessNetworkConstants.AccessNetworkType.NGRAN; + default: + return false; + } + } + + private void validateRanWithMeasurementType( + @AccessNetworkConstants.RadioAccessNetworkType int ran, + @SignalMeasurementType int signalMeasurement) { + if (!isValidRanWithMeasurementType(ran, signalMeasurement)) { + throw new IllegalArgumentException( + "invalid RAN: " + ran + " with signal measurement type: " + signalMeasurement); + } + } + + private void validateThresholdRange(@SignalMeasurementType int signalMeasurement, + int[] thresholds) { + for (int threshold : thresholds) { + if (!isValidThreshold(signalMeasurement, threshold)) { + throw new IllegalArgumentException( + "invalid signal measurement type: " + signalMeasurement + + " with threshold: " + threshold); + } + } } } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index bcc2c67bd8e7..b958bff6d00b 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -345,7 +345,6 @@ public final class SmsManager { * where this operation may fail. * </p> * - * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC @@ -358,7 +357,6 @@ public final class SmsManager { * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -473,7 +471,6 @@ public final class SmsManager { * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -862,22 +859,20 @@ public final class SmsManager { * where this operation may fail. * </p> * - * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use - * the current default SMSC + * the current default SMSC * @param parts an <code>ArrayList</code> of strings that, in order, - * comprise the original message + * comprise the original message * @param sentIntents if not null, an <code>ArrayList</code> of - * <code>PendingIntent</code>s (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be <code>Activity.RESULT_OK</code> for success, - * or one of these errors:<br> + * <code>PendingIntent</code>s (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be <code>Activity.RESULT_OK</code> for success, + * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -933,14 +928,14 @@ public final class SmsManager { * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> - * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. + * The per-application based SMS control checks sentIntent. If sentIntent + * is NULL the caller will be checked against all unknown applications, + * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an <code>ArrayList</code> of - * <code>PendingIntent</code>s (one for each message part) that is - * broadcast when the corresponding message part has been delivered - * to the recipient. The raw pdu of the status report is in the - * extended data ("pdu"). + * <code>PendingIntent</code>s (one for each message part) that is + * broadcast when the corresponding message part has been delivered + * to the recipient. The raw pdu of the status report is in the + * extended data ("pdu"). * * @throws IllegalArgumentException if destinationAddress or data are empty */ @@ -1123,22 +1118,21 @@ public final class SmsManager { * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions * where this operation may fail. * </p> - + * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use - * the current default SMSC + * the current default SMSC * @param parts an <code>ArrayList</code> of strings that, in order, - * comprise the original message + * comprise the original message * @param sentIntents if not null, an <code>ArrayList</code> of - * <code>PendingIntent</code>s (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be <code>Activity.RESULT_OK</code> for success, - * or one of these errors:<br> + * <code>PendingIntent</code>s (one for each message part) that is + * broadcast when the corresponding message part has been sent. + * The result code will be <code>Activity.RESULT_OK</code> for success, + * or one of these errors:<br> * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> @@ -1194,14 +1188,14 @@ public final class SmsManager { * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors, * the sentIntent may include the extra "errorCode" containing a radio technology specific * value, generally only useful for troubleshooting.<br> - * The per-application based SMS control checks sentIntent. If sentIntent - * is NULL the caller will be checked against all unknown applications, - * which cause smaller number of SMS to be sent in checking period. + * The per-application based SMS control checks sentIntent. If sentIntent + * is NULL the caller will be checked against all unknown applications, + * which cause smaller number of SMS to be sent in checking period. * @param deliveryIntents if not null, an <code>ArrayList</code> of - * <code>PendingIntent</code>s (one for each message part) that is - * broadcast when the corresponding message part has been delivered - * to the recipient. The raw pdu of the status report is in the - * extended data ("pdu"). + * <code>PendingIntent</code>s (one for each message part) that is + * broadcast when the corresponding message part has been delivered + * to the recipient. The raw pdu of the status report is in the + * extended data ("pdu"). * @param priority Priority level of the message * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 * --------------------------------- @@ -1340,7 +1334,6 @@ public final class SmsManager { * <code>RESULT_ERROR_RADIO_OFF</code><br> * <code>RESULT_ERROR_NULL_PDU</code><br> * <code>RESULT_ERROR_NO_SERVICE</code><br> - * <code>RESULT_ERROR_NO_SERVICE</code><br> * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br> * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br> * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br> diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java index 3d5c6aad1042..1fcb504e7895 100644 --- a/telephony/java/android/telephony/TelephonyDisplayInfo.java +++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java @@ -29,6 +29,10 @@ import java.util.Objects; * information is provided in accordance with carrier policy and branding preferences; it is not * necessarily a precise or accurate representation of the current state and should be treated * accordingly. + * To be notified of changes in TelephonyDisplayInfo, use + * {@link TelephonyManager#registerPhoneStateListener} with a {@link PhoneStateListener} + * that implements {@link PhoneStateListener.DisplayInfoChangedListener}. + * Override the onDisplayInfoChanged() method to handle the broadcast. */ public final class TelephonyDisplayInfo implements Parcelable { /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index c05e90b28fa8..e8ace34793db 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -31,6 +31,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.StringDef; import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -120,10 +121,12 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -155,6 +158,7 @@ import java.util.function.Consumer; public class TelephonyManager { private static final String TAG = "TelephonyManager"; + private TelephonyRegistryManager mTelephonyRegistryMgr; /** * To expand the error codes for {@link TelephonyManager#updateAvailableNetworks} and * {@link TelephonyManager#setPreferredOpportunisticDataSubscription}. @@ -5569,8 +5573,7 @@ public class TelephonyManager { // /** - * Registers a listener object to receive notification of changes - * in specified telephony states. + * Registers a listener object to receive notification of changes in specified telephony states. * <p> * To register a listener, pass a {@link PhoneStateListener} and specify at least one telephony * state of interest in the events argument. @@ -5580,13 +5583,15 @@ public class TelephonyManager { * values. * <p> * To un-register a listener, pass the listener object and set the events argument to - * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0). + * {@link PhoneStateListener#LISTEN_NONE} (0). * * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, * applies to the given subId. Otherwise, applies to - * {@link SubscriptionManager#getDefaultSubscriptionId()}. To listen events for multiple subIds, + * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}. To listen events for multiple subIds, * pass a separate listener object to each TelephonyManager object created with - * {@link #createForSubscriptionId}. + * {@link #createForSubscriptionId}. Only {@link PhoneStateListener#LISTEN_CALL_STATE} event can + * be used to receive changes for all subIds through + * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}. * * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A @@ -5596,23 +5601,28 @@ public class TelephonyManager { * instability. If a process has registered too many listeners without unregistering them, it * may encounter an {@link IllegalStateException} when trying to register more listeners. * - * @param listener The {@link PhoneStateListener} object to register - * (or unregister) - * @param events The telephony state(s) of interest to the listener, - * as a bitwise-OR combination of {@link PhoneStateListener} - * LISTEN_ flags. + * @param listener The {@link PhoneStateListener} object to register (or unregister) + * @param events The telephony state(s) of interest to the listener, as a bitwise-OR combination + * of {@link PhoneStateListener} LISTEN_ flags. + * @deprecated Use {@link #registerPhoneStateListener(Executor, PhoneStateListener)}. */ + @Deprecated public void listen(PhoneStateListener listener, int events) { - if (mContext == null) return; - boolean notifyNow = (getITelephony() != null); - TelephonyRegistryManager telephonyRegistry = - (TelephonyRegistryManager) - mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); - if (telephonyRegistry != null) { - telephonyRegistry.listenForSubscriber(mSubId, getOpPackageName(), getAttributionTag(), - listener, events, notifyNow); + if (!listener.isExecutorSet()) { + throw new IllegalStateException("PhoneStateListener should be created on a thread " + + "with Looper.myLooper() != null"); + } + boolean notifyNow = getITelephony() != null; + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr != null) { + if (events != PhoneStateListener.LISTEN_NONE) { + mTelephonyRegistryMgr.registerPhoneStateListenerWithEvents(mSubId, + getOpPackageName(), getAttributionTag(), listener, events, notifyNow); + } else { + unregisterPhoneStateListener(listener); + } } else { - Rlog.w(TAG, "telephony registry not ready."); + throw new IllegalStateException("telephony service is null."); } } @@ -7448,18 +7458,23 @@ public class TelephonyManager { } /** - * Set IMS registration state + * Set IMS registration state on all active subscriptions. + * <p/> + * Use {@link android.telephony.ims.stub.ImsRegistrationImplBase#onRegistered} and + * {@link android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered} to set Ims + * registration state instead. + * + * @param registered whether ims is registered * - * @param Registration state * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void setImsRegistrationState(boolean registered) { + public void setImsRegistrationState(final boolean registered) { try { - ITelephony telephony = getITelephony(); + final ITelephony telephony = getITelephony(); if (telephony != null) telephony.setImsRegistrationState(registered); - } catch (RemoteException e) { + } catch (final RemoteException e) { } } @@ -8689,11 +8704,18 @@ public class TelephonyManager { */ public static final int CALL_COMPOSER_STATUS_ON = 1; + /** + * Call composer status indicating that sending/receiving pictures is disabled. + * All other attachments are still enabled in this state. + */ + public static final int CALL_COMPOSER_STATUS_ON_NO_PICTURES = 2; + /** @hide */ @IntDef(prefix = {"CALL_COMPOSER_STATUS_"}, value = { CALL_COMPOSER_STATUS_ON, CALL_COMPOSER_STATUS_OFF, + CALL_COMPOSER_STATUS_ON_NO_PICTURES, }) public @interface CallComposerStatus {} @@ -8701,8 +8723,9 @@ public class TelephonyManager { * Set the user-set status for enriched calling with call composer. * * @param status user-set status for enriched calling with call composer; - * it must be a value of either {@link #CALL_COMPOSER_STATUS_ON} - * or {@link #CALL_COMPOSER_STATUS_OFF}. + * it must be any of {@link #CALL_COMPOSER_STATUS_ON} + * {@link #CALL_COMPOSER_STATUS_OFF}, + * or {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES} * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} @@ -8712,7 +8735,8 @@ public class TelephonyManager { */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallComposerStatus(@CallComposerStatus int status) { - if (status != CALL_COMPOSER_STATUS_ON && status != CALL_COMPOSER_STATUS_OFF) { + if (status > CALL_COMPOSER_STATUS_ON_NO_PICTURES + || status < CALL_COMPOSER_STATUS_OFF) { throw new IllegalArgumentException("requested status is invalid"); } try { @@ -8734,8 +8758,9 @@ public class TelephonyManager { * * @throws SecurityException if the caller does not have the permission. * - * @return the user-set status for enriched calling with call composer either - * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}. + * @return the user-set status for enriched calling with call composer, any of + * {@link #CALL_COMPOSER_STATUS_ON}, {@link #CALL_COMPOSER_STATUS_OFF}, or + * {@link #CALL_COMPOSER_STATUS_ON_NO_PICTURES}. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @CallComposerStatus int getCallComposerStatus() { @@ -9438,9 +9463,16 @@ public class TelephonyManager { * @return true if mobile data is enabled. */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, - android.Manifest.permission.MODIFY_PHONE_STATE}) + android.Manifest.permission.MODIFY_PHONE_STATE, + android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled() { - return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); + try { + return isDataEnabledForReason(DATA_ENABLED_REASON_USER); + } catch (IllegalStateException ise) { + // TODO(b/176163590): Remove this catch once TelephonyManager is booting safely. + Log.e(TAG, "Error calling #isDataEnabled, returning default (false).", ise); + return false; + } } /** @@ -9685,7 +9717,7 @@ public class TelephonyManager { @SystemApi public boolean getDataEnabled(int subId) { try { - return isDataEnabledForReason(DATA_ENABLED_REASON_USER); + return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER); } catch (RuntimeException e) { Log.e(TAG, "Error calling isDataEnabledForReason e:" + e); } @@ -14314,6 +14346,54 @@ public class TelephonyManager { } /** + * Indicates whether {@link CarrierBandwidth#getSecondaryDownlinkCapacityKbps()} and + * {@link CarrierBandwidth#getSecondaryUplinkCapacityKbps()} are visible. See comments + * on respective methods for more information. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = + "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "CAPABILITY_", value = { + CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE, + }) + public @interface RadioInterfaceCapability {} + + /** + * Whether the device supports a given capability on the radio interface. + * + * If the capability is not in the set of radio interface capabilities, false is returned. + * + * @param capability the name of the capability to check for + * @return the availability of the capability + * + * @hide + */ + @SystemApi + public boolean isRadioInterfaceCapabilitySupported( + @NonNull @RadioInterfaceCapability String capability) { + try { + if (capability == null) return false; + + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isRadioInterfaceCapabilitySupported(capability); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + return false; + } + + /** * Indicates that the thermal mitigation request was completed successfully. * * @hide @@ -14583,4 +14663,165 @@ public class TelephonyManager { e.execute(() -> callback.onAuthenticationFailure(GBA_FAILURE_REASON_FEATURE_NOT_READY)); } } + + /** + * Registers a listener object to receive notification of changes in specified telephony states. + * <p> + * To register a listener, pass a {@link PhoneStateListener} which implements + * interfaces of events. For example, + * FakeServiceStateChangedListener extends {@link PhoneStateListener} implements + * {@link PhoneStateListener.ServiceStateChangedListener}. + * + * At registration, and when a specified telephony state changes, the telephony manager invokes + * the appropriate callback method on the listener object and passes the current (updated) + * values. + * <p> + * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}. To listen events for multiple subIds, + * pass a separate listener object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. Only {@link PhoneStateListener.CallStateChangedListener} + * can be used to receive changes for all subIds through + * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}. + * + * Note: if you call this method while in the middle of a binder transaction, you <b>must</b> + * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A + * {@link SecurityException} will be thrown otherwise. + * + * This API should be used sparingly -- large numbers of listeners will cause system + * instability. If a process has registered too many listeners without unregistering them, it + * may encounter an {@link IllegalStateException} when trying to register more listeners. + * + * @param executor The executor of where the callback will execute. + * @param listener The {@link PhoneStateListener} object to register. + */ + public void registerPhoneStateListener(@NonNull @CallbackExecutor Executor executor, + @NonNull PhoneStateListener listener) { + if (executor == null || listener == null) { + throw new IllegalArgumentException("PhoneStateListener and executor must be non-null"); + } + mTelephonyRegistryMgr = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (mTelephonyRegistryMgr != null) { + mTelephonyRegistryMgr.registerPhoneStateListener(executor, mSubId, + getOpPackageName(), getAttributionTag(), listener, getITelephony() != null); + } else { + throw new IllegalStateException("telephony service is null."); + } + } + + /** + * Unregister an existing {@link PhoneStateListener}. + * + * @param listener The {@link PhoneStateListener} object to unregister. + */ + public void unregisterPhoneStateListener(@NonNull PhoneStateListener listener) { + + if (mContext == null) { + throw new IllegalStateException("telephony service is null."); + } + + mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class); + if (mTelephonyRegistryMgr != null) { + mTelephonyRegistryMgr.unregisterPhoneStateListener(mSubId, getOpPackageName(), + getAttributionTag(), listener, getITelephony() != null); + } else { + throw new IllegalStateException("telephony service is null."); + } + } + + /** + * The network type is valid or not. + * + * @param networkType The network type {@link NetworkType}. + * @return {@code true} if valid, {@code false} otherwise. + * + * @hide + */ + public static boolean isNetworkTypeValid(@NetworkType int networkType) { + return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN && + networkType <= TelephonyManager.NETWORK_TYPE_NR; + } + + /** + * Set a {@link SignalStrengthUpdateRequest} to receive notification when signal quality + * measurements breach the specified thresholds. + * + * To be notified, set the signal strength update request and then register + * {@link TelephonyManager#listen(PhoneStateListener, int)} with + * {@link PhoneStateListener#LISTEN_SIGNAL_STRENGTHS}. The notification will arrive through + * {@link PhoneStateListener#onSignalStrengthsChanged(SignalStrength)}. + * + * To stop receiving the notification over the specified thresholds, pass the same + * {@link SignalStrengthUpdateRequest} object to + * {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}. + * + * System will clean up the {@link SignalStrengthUpdateRequest} if the caller process died + * without calling {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}. + * + * If this TelephonyManager object has been created with {@link #createForSubscriptionId}, + * applies to the given subId. Otherwise, applies to + * {@link SubscriptionManager#getDefaultSubscriptionId()}. To request for multiple subIds, + * pass a request object to each TelephonyManager object created with + * {@link #createForSubscriptionId}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * Note that the thresholds in the request will be used on a best-effort basis; the system may + * modify requests to multiplex various request sources or to optimize power consumption. The + * caller should not expect to be notified with the exactly the same thresholds. + * + * @see #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest) + * + * @param request the SignalStrengthUpdateRequest to be set into the System + * + * @throws IllegalStateException if a new request is set with same subId from the same caller + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) { + Objects.requireNonNull(request, "request must not be null"); + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.setSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setSignalStrengthUpdateRequest", e); + } + } + + /** + * Clear a {@link SignalStrengthUpdateRequest} from the system. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * or that the calling app has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges}). + * + * <p>If the given request was not set before, this operation is a no-op. + * + * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest) + * + * @param request the SignalStrengthUpdateRequest to be cleared from the System + */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void clearSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) { + Objects.requireNonNull(request, "request must not be null"); + + try { + ITelephony service = getITelephony(); + if (service != null) { + service.clearSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#clearSignalStrengthUpdateRequest", e); + } + } } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 8348502586a5..46ec4a39fd21 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -136,6 +136,7 @@ public final class DataCallResponse implements Parcelable { private final int mPduSessionId; private final Qos mDefaultQos; private final List<QosSession> mQosSessions; + private final SliceInfo mSliceInfo; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -186,6 +187,7 @@ public final class DataCallResponse implements Parcelable { mPduSessionId = PDU_SESSION_ID_NOT_SET; mDefaultQos = null; mQosSessions = new ArrayList<>(); + mSliceInfo = null; } private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id, @@ -194,7 +196,8 @@ public final class DataCallResponse implements Parcelable { @Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses, @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, @HandoverFailureMode int handoverFailureMode, int pduSessionId, - @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions) { + @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions, + @Nullable SliceInfo sliceInfo) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -216,6 +219,7 @@ public final class DataCallResponse implements Parcelable { mPduSessionId = pduSessionId; mDefaultQos = defaultQos; mQosSessions = qosSessions; + mSliceInfo = sliceInfo; } /** @hide */ @@ -243,6 +247,7 @@ public final class DataCallResponse implements Parcelable { mDefaultQos = source.readParcelable(Qos.class.getClassLoader()); mQosSessions = new ArrayList<>(); source.readList(mQosSessions, QosSession.class.getClassLoader()); + mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader()); } /** @@ -368,7 +373,7 @@ public final class DataCallResponse implements Parcelable { } /** - * @return default QOS of the data call received from the network + * @return default QOS of the data connection received from the network * * @hide */ @@ -379,16 +384,24 @@ public final class DataCallResponse implements Parcelable { } /** - * @return All the dedicated bearer QOS sessions of the data call received from the network + * @return All the dedicated bearer QOS sessions of the data connection received from the + * network. * * @hide */ - @NonNull public List<QosSession> getQosSessions() { return mQosSessions; } + /** + * @return The slice info related to this data connection. + */ + @Nullable + public SliceInfo getSliceInfo() { + return mSliceInfo; + } + @NonNull @Override public String toString() { @@ -411,6 +424,7 @@ public final class DataCallResponse implements Parcelable { .append(" pduSessionId=").append(getPduSessionId()) .append(" defaultQos=").append(mDefaultQos) .append(" qosSessions=").append(mQosSessions) + .append(" sliceInfo=").append(mSliceInfo) .append("}"); return sb.toString(); } @@ -454,7 +468,8 @@ public final class DataCallResponse implements Parcelable { && mHandoverFailureMode == other.mHandoverFailureMode && mPduSessionId == other.mPduSessionId && isQosSame - && isQosSessionsSame; + && isQosSessionsSame + && Objects.equals(mSliceInfo, other.mSliceInfo); } @Override @@ -462,7 +477,7 @@ public final class DataCallResponse implements Parcelable { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos, - mQosSessions); + mQosSessions, mSliceInfo); } @Override @@ -493,6 +508,7 @@ public final class DataCallResponse implements Parcelable { dest.writeParcelable((NrQos)mDefaultQos, flags); } dest.writeList(mQosSessions); + dest.writeParcelable(mSliceInfo, flags); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -576,6 +592,8 @@ public final class DataCallResponse implements Parcelable { private List<QosSession> mQosSessions = new ArrayList<>(); + private SliceInfo mSliceInfo; + /** * Default constructor for Builder. */ @@ -799,6 +817,21 @@ public final class DataCallResponse implements Parcelable { } /** + * The Slice used for this data connection. + * <p/> + * If a handover occurs from EPDG to 5G, + * this is the {@link SliceInfo} used in {@link DataService#setupDataCall}. + * + * @param sliceInfo the slice info for the data call + * + * @return The same instance of the builder. + */ + public @NonNull Builder setSliceInfo(@Nullable SliceInfo sliceInfo) { + mSliceInfo = sliceInfo; + return this; + } + + /** * Build the DataCallResponse. * * @return the DataCallResponse object. @@ -807,7 +840,7 @@ public final class DataCallResponse implements Parcelable { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, - mDefaultQos, mQosSessions); + mDefaultQos, mQosSessions, mSliceInfo); } } } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 2ec965101930..03c2ef9d9baa 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -194,13 +194,19 @@ public abstract class DataService extends Service { * The standard range of values are 1-15 while 0 means no pdu session id * was attached to this call. Reference: 3GPP TS 24.007 section * 11.2.3.1b. + * @param sliceInfo used within the data connection when a handover occurs from EPDG to 5G. + * The value is null unless the access network is + * {@link android.telephony.AccessNetworkConstants.AccessNetworkType#NGRAN} and a + * handover is occurring from EPDG to 5G. If the slice passed is rejected, then + * {@link DataCallResponse#getCause()} is + * {@link android.telephony.DataFailCause#SLICE_REJECTED}. * @param callback The result callback for this request. */ public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, @SetupDataReason int reason, @Nullable LinkProperties linkProperties, - @IntRange(from = 0, to = 15) int pduSessionId, + @IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo, @NonNull DataServiceCallback callback) { /* Call the old version since the new version isn't supported */ setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason, @@ -392,10 +398,11 @@ public abstract class DataService extends Service { public final int reason; public final LinkProperties linkProperties; public final int pduSessionId; + public final SliceInfo sliceInfo; public final IDataServiceCallback callback; SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, - int pduSessionId, IDataServiceCallback callback) { + int pduSessionId, SliceInfo sliceInfo, IDataServiceCallback callback) { this.accessNetworkType = accessNetworkType; this.dataProfile = dataProfile; this.isRoaming = isRoaming; @@ -403,6 +410,7 @@ public abstract class DataService extends Service { this.linkProperties = linkProperties; this.reason = reason; this.pduSessionId = pduSessionId; + this.sliceInfo = sliceInfo; this.callback = callback; } } @@ -513,6 +521,7 @@ public abstract class DataService extends Service { setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming, setupDataCallRequest.allowRoaming, setupDataCallRequest.reason, setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId, + setupDataCallRequest.sliceInfo, (setupDataCallRequest.callback != null) ? new DataServiceCallback(setupDataCallRequest.callback) : null); @@ -676,10 +685,12 @@ public abstract class DataService extends Service { @Override public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, - LinkProperties linkProperties, int pduSessionId, IDataServiceCallback callback) { + LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo, + IDataServiceCallback callback) { mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0, new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming, - allowRoaming, reason, linkProperties, pduSessionId, callback)) + allowRoaming, reason, linkProperties, pduSessionId, sliceInfo, + callback)) .sendToTarget(); } diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl new file mode 100644 index 000000000000..da31f9864cf1 --- /dev/null +++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl @@ -0,0 +1,19 @@ +/* + * 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.telephony.data; + + parcelable EpsBearerQosSessionAttributes;
\ No newline at end of file diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java new file mode 100644 index 000000000000..041edc00c4d2 --- /dev/null +++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java @@ -0,0 +1,234 @@ +/* + * 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.telephony.data; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.QosSessionAttributes; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Provides Qos attributes of an EPS bearer. + * + * {@hide} + */ +@SystemApi +public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessionAttributes { + private static final String TAG = EpsBearerQosSessionAttributes.class.getSimpleName(); + private final int mQci; + private final long mMaxUplinkBitRate; + private final long mMaxDownlinkBitRate; + private final long mGuaranteedUplinkBitRate; + private final long mGuaranteedDownlinkBitRate; + @NonNull private final List<InetSocketAddress> mRemoteAddresses; + + /** + * Quality of Service Class Identifier (QCI), see 3GPP TS 23.203 and 29.212. + * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85) + * defined in the spec and operator specific values in the range 128-254. + * + * @return the qci of the session + */ + public int getQci() { + return mQci; + } + + /** + * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink. + * + * see 3GPP TS 23.107 section 6.4.3.1 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the guaranteed bit rate in kbps + */ + public long getGuaranteedUplinkBitRate() { + return mGuaranteedUplinkBitRate; + } + + /** + * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink. + * + * see 3GPP TS 23.107 section 6.4.3.1 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the guaranteed bit rate in kbps + */ + public long getGuaranteedDownlinkBitRate() { + return mGuaranteedDownlinkBitRate; + } + + /** + * The maximum kbps that the network will accept. + * + * see 3GPP TS 23.107 section 6.4.3.1 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the max uplink bit rate in kbps + */ + public long getMaxUplinkBitRate() { + return mMaxUplinkBitRate; + } + + /** + * The maximum kbps that the network can provide. + * + * see 3GPP TS 23.107 section 6.4.3.1 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the max downlink bit rate in kbps + */ + public long getMaxDownlinkBitRate() { + return mMaxDownlinkBitRate; + } + + /** + * List of remote addresses associated with the Qos Session. The given uplink bit rates apply + * to this given list of remote addresses. + * + * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to + * all remote addresses that are not contained in a different set of attributes. + * + * @return list of remote socket addresses that the attributes apply to + */ + @NonNull + public List<InetSocketAddress> getRemoteAddresses() { + return mRemoteAddresses; + } + + /** + * ..ctor for attributes + * + * @param qci quality class indicator + * @param maxDownlinkBitRate the max downlink bit rate in kbps + * @param maxUplinkBitRate the max uplink bit rate in kbps + * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps + * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps + * @param remoteAddresses the remote addresses that the uplink bit rates apply to + * + * @hide + */ + public EpsBearerQosSessionAttributes(final int qci, + final long maxDownlinkBitRate, final long maxUplinkBitRate, + final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate, + @NonNull final List<InetSocketAddress> remoteAddresses) { + Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null"); + mQci = qci; + mMaxDownlinkBitRate = maxDownlinkBitRate; + mMaxUplinkBitRate = maxUplinkBitRate; + mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate; + mGuaranteedUplinkBitRate = guaranteedUplinkBitRate; + + final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses); + mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp); + } + + private static List<InetSocketAddress> copySocketAddresses( + @NonNull final List<InetSocketAddress> remoteAddresses) { + final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>(); + for (final InetSocketAddress socketAddress : remoteAddresses) { + if (socketAddress != null && socketAddress.getAddress() != null) { + remoteAddressesTemp.add(socketAddress); + } + } + return remoteAddressesTemp; + } + + private EpsBearerQosSessionAttributes(@NonNull final Parcel in) { + mQci = in.readInt(); + mMaxDownlinkBitRate = in.readLong(); + mMaxUplinkBitRate = in.readLong(); + mGuaranteedDownlinkBitRate = in.readLong(); + mGuaranteedUplinkBitRate = in.readLong(); + + final int size = in.readInt(); + final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + final byte[] addressBytes = in.createByteArray(); + final int port = in.readInt(); + try { + remoteAddresses.add( + new InetSocketAddress(InetAddress.getByAddress(addressBytes), port)); + } catch (final UnknownHostException e) { + // Impossible case since we filter out null values in the ..ctor + Log.e(TAG, "unable to unparcel remote address at index: " + i, e); + } + } + mRemoteAddresses = Collections.unmodifiableList(remoteAddresses); + } + + /** + * Creates attributes based off of a parcel + * @param in the parcel + * @return the attributes + */ + @NonNull + public static EpsBearerQosSessionAttributes create(@NonNull final Parcel in) { + return new EpsBearerQosSessionAttributes(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeInt(mQci); + dest.writeLong(mMaxDownlinkBitRate); + dest.writeLong(mMaxUplinkBitRate); + dest.writeLong(mGuaranteedDownlinkBitRate); + dest.writeLong(mGuaranteedUplinkBitRate); + + final int size = mRemoteAddresses.size(); + dest.writeInt(size); + for (int i = 0; i < size; i++) { + final InetSocketAddress address = mRemoteAddresses.get(i); + dest.writeByteArray(address.getAddress().getAddress()); + dest.writeInt(address.getPort()); + } + } + + @NonNull + public static final Creator<EpsBearerQosSessionAttributes> CREATOR = + new Creator<EpsBearerQosSessionAttributes>() { + @NonNull + @Override + public EpsBearerQosSessionAttributes createFromParcel(@NonNull final Parcel in) { + return new EpsBearerQosSessionAttributes(in); + } + + @NonNull + @Override + public EpsBearerQosSessionAttributes[] newArray(final int size) { + return new EpsBearerQosSessionAttributes[size]; + } + }; +} diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index 3f1f033d6f11..e0b9a1a9bb5a 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -19,6 +19,7 @@ package android.telephony.data; import android.net.LinkProperties; import android.telephony.data.DataProfile; import android.telephony.data.IDataServiceCallback; +import android.telephony.data.SliceInfo; /** * {@hide} @@ -29,7 +30,7 @@ oneway interface IDataService void removeDataServiceProvider(int slotId); void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, in LinkProperties linkProperties, - int pduSessionId, IDataServiceCallback callback); + int pduSessionId, in SliceInfo sliceInfo, IDataServiceCallback callback); void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback); void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming, IDataServiceCallback callback); diff --git a/telephony/java/android/telephony/data/SliceInfo.aidl b/telephony/java/android/telephony/data/SliceInfo.aidl new file mode 100644 index 000000000000..286ea5e4f8c7 --- /dev/null +++ b/telephony/java/android/telephony/data/SliceInfo.aidl @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** @hide */ +package android.telephony.data; + +parcelable SliceInfo; diff --git a/telephony/java/android/telephony/data/SliceInfo.java b/telephony/java/android/telephony/data/SliceInfo.java new file mode 100644 index 000000000000..51857a7b4908 --- /dev/null +++ b/telephony/java/android/telephony/data/SliceInfo.java @@ -0,0 +1,342 @@ +/* + * 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.telephony.data; + +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Represents a S-NSSAI as defined in 3GPP TS 24.501. + * + * @hide + */ +@SystemApi +public final class SliceInfo implements Parcelable { + /** + * When set on a Slice Differentiator, this value indicates that there is no corresponding + * Slice. + */ + public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; + + /** + * Indicates that the service type is not present. + */ + public static final int SLICE_SERVICE_TYPE_NONE = 0; + + /** + * Slice suitable for the handling of 5G enhanced Mobile Broadband. + */ + public static final int SLICE_SERVICE_TYPE_EMBB = 1; + + /** + * Slice suitable for the handling of ultra-reliable low latency communications. + */ + public static final int SLICE_SERVICE_TYPE_URLLC = 2; + + /** + * Slice suitable for the handling of massive IoT. + */ + public static final int SLICE_SERVICE_TYPE_MIOT = 3; + + /** + * The min acceptable value for a Slice Differentiator + */ + @SuppressLint("MinMaxConstant") + public static final int MIN_SLICE_DIFFERENTIATOR = -1; + + /** + * The max acceptable value for a Slice Differentiator + */ + @SuppressLint("MinMaxConstant") + public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE; + + /** @hide */ + @IntDef(prefix = { "SLICE_SERVICE_TYPE_" }, value = { + SLICE_SERVICE_TYPE_NONE, + SLICE_SERVICE_TYPE_EMBB, + SLICE_SERVICE_TYPE_URLLC, + SLICE_SERVICE_TYPE_MIOT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SliceServiceType {} + + + @SliceServiceType + private final int mSliceServiceType; + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + private final int mSliceDifferentiator; + @SliceServiceType + private final int mMappedHplmnSliceServiceType; + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + private final int mMappedHplmnSliceDifferentiator; + + private SliceInfo(@SliceServiceType int sliceServiceType, + int sliceDifferentiator, int mappedHplmnSliceServiceType, + int mappedHplmnSliceDifferentiator) { + mSliceServiceType = sliceServiceType; + mSliceDifferentiator = sliceDifferentiator; + mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator; + mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType; + } + + /** + * The type of service provided by the slice. + * <p/> + * see: 3GPP TS 24.501 Section 9.11.2.8. + */ + @SliceServiceType + public int getSliceServiceType() { + return mSliceServiceType; + } + + /** + * Identifies the slice from others with the same Slice Service Type. + * <p/> + * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if {@link #getSliceServiceType} returns + * {@link #SLICE_SERVICE_TYPE_NONE}. + * <p/> + * see: 3GPP TS 24.501 Section 9.11.2.8. + */ + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + public int getSliceDifferentiator() { + return mSliceDifferentiator; + } + + /** + * Corresponds to a Slice Info (S-NSSAI) of the HPLMN. + * <p/> + * see: 3GPP TS 24.501 Section 9.11.2.8. + */ + @SliceServiceType + public int getMappedHplmnSliceServiceType() { + return mMappedHplmnSliceServiceType; + } + + /** + * This Slice Differentiator corresponds to a {@link SliceInfo} (S-NSSAI) of the HPLMN; + * {@link #getSliceDifferentiator()} is mapped to this value. + * <p/> + * Returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE} if either of the following are true: + * <ul> + * <li>{@link #getSliceDifferentiator()} returns {@link #SLICE_DIFFERENTIATOR_NO_SLICE}</li> + * <li>{@link #getMappedHplmnSliceServiceType()} returns {@link #SLICE_SERVICE_TYPE_NONE}</li> + * </ul> + * <p/> + * see: 3GPP TS 24.501 Section 9.11.2.8. + */ + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + public int getMappedHplmnSliceDifferentiator() { + return mMappedHplmnSliceDifferentiator; + } + + private SliceInfo(@NonNull Parcel in) { + mSliceServiceType = in.readInt(); + mSliceDifferentiator = in.readInt(); + mMappedHplmnSliceServiceType = in.readInt(); + mMappedHplmnSliceDifferentiator = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mSliceServiceType); + dest.writeInt(mSliceDifferentiator); + dest.writeInt(mMappedHplmnSliceServiceType); + dest.writeInt(mMappedHplmnSliceDifferentiator); + } + + public static final @android.annotation.NonNull Parcelable.Creator<SliceInfo> CREATOR = + new Parcelable.Creator<SliceInfo>() { + @Override + @NonNull + public SliceInfo createFromParcel(@NonNull Parcel source) { + return new SliceInfo(source); + } + + @Override + @NonNull + public SliceInfo[] newArray(int size) { + return new SliceInfo[size]; + } + }; + + @Override + public String toString() { + return "SliceInfo{" + + "mSliceServiceType=" + sliceServiceTypeToString(mSliceServiceType) + + ", mSliceDifferentiator=" + mSliceDifferentiator + + ", mMappedHplmnSliceServiceType=" + + sliceServiceTypeToString(mMappedHplmnSliceServiceType) + + ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator + + '}'; + } + + private static String sliceServiceTypeToString(@SliceServiceType int sliceServiceType) { + switch(sliceServiceType) { + case SLICE_SERVICE_TYPE_NONE: + return "NONE"; + case SLICE_SERVICE_TYPE_EMBB: + return "EMBB"; + case SLICE_SERVICE_TYPE_URLLC: + return "URLLC"; + case SLICE_SERVICE_TYPE_MIOT: + return "MIOT"; + default: + return Integer.toString(sliceServiceType); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SliceInfo sliceInfo = (SliceInfo) o; + return mSliceServiceType == sliceInfo.mSliceServiceType + && mSliceDifferentiator == sliceInfo.mSliceDifferentiator + && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType + && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator; + } + + @Override + public int hashCode() { + return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType, + mMappedHplmnSliceDifferentiator); + } + + /** + * Provides a convenient way to set the fields of a {@link SliceInfo} when creating a + * new instance. + * + * <p>The example below shows how you might create a new {@code SliceInfo}: + * + * <pre><code> + * + * SliceInfo response = new SliceInfo.Builder() + * .setSliceServiceType(SLICE_SERVICE_TYPE_URLLC) + * .build(); + * </code></pre> + */ + public static final class Builder { + @SliceServiceType + private int mSliceServiceType = SLICE_SERVICE_TYPE_NONE; + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + private int mSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE; + @SliceServiceType + private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE; + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE; + + /** + * Default constructor for Builder. + */ + public Builder() { + } + + /** + * Set the Slice Service Type. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setSliceServiceType(@SliceServiceType int mSliceServiceType) { + this.mSliceServiceType = mSliceServiceType; + return this; + } + + /** + * Set the Slice Differentiator. + * <p/> + * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no + * corresponding Slice. + * + * @throws IllegalArgumentException if the parameter is not between + * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setSliceDifferentiator( + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + int sliceDifferentiator) { + if (sliceDifferentiator < MIN_SLICE_DIFFERENTIATOR + || sliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) { + throw new IllegalArgumentException("The slice diffentiator value is out of range"); + } + this.mSliceDifferentiator = sliceDifferentiator; + return this; + } + + /** + * Set the HPLMN Slice Service Type. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setMappedHplmnSliceServiceType( + @SliceServiceType int mappedHplmnSliceServiceType) { + this.mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType; + return this; + } + + /** + * Set the HPLMN Slice Differentiator. + * <p/> + * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no + * corresponding Slice of the HPLMN. + * + * @throws IllegalArgumentException if the parameter is not between + * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}. + * + * @return The same instance of the builder. + */ + @NonNull + public Builder setMappedHplmnSliceDifferentiator( + @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR) + int mappedHplmnSliceDifferentiator) { + if (mappedHplmnSliceDifferentiator < MIN_SLICE_DIFFERENTIATOR + || mappedHplmnSliceDifferentiator > MAX_SLICE_DIFFERENTIATOR) { + throw new IllegalArgumentException("The slice diffentiator value is out of range"); + } + this.mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator; + return this; + } + + /** + * Build the {@link SliceInfo}. + * + * @return the {@link SliceInfo} object. + */ + @NonNull + public SliceInfo build() { + return new SliceInfo(this.mSliceServiceType, this.mSliceDifferentiator, + this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator); + } + } +} diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java index 52b31d7f9611..a5150b010f57 100644 --- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java +++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java @@ -15,6 +15,7 @@ */ package android.telephony.euicc; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.PendingIntent; @@ -102,44 +103,81 @@ public final class DownloadableSubscription implements Parcelable { this.accessRules = accessRules; } - /** @hide */ - @SystemApi public static final class Builder { @Nullable private String encodedActivationCode; @Nullable private String confirmationCode; @Nullable private String carrierName; List<UiccAccessRule> accessRules; + /** @hide */ + @SystemApi public Builder() {} - public Builder(DownloadableSubscription baseSubscription) { + public Builder(@NonNull DownloadableSubscription baseSubscription) { encodedActivationCode = baseSubscription.getEncodedActivationCode(); confirmationCode = baseSubscription.getConfirmationCode(); carrierName = baseSubscription.getCarrierName(); accessRules = baseSubscription.getAccessRules(); } + public Builder(@NonNull String encodedActivationCode) { + this.encodedActivationCode = encodedActivationCode; + } + + /** + * Builds a {@link DownloadableSubscription} object. + * @return a non-null {@link DownloadableSubscription} object. + */ + @NonNull public DownloadableSubscription build() { return new DownloadableSubscription(encodedActivationCode, confirmationCode, carrierName, accessRules); } - public Builder setEncodedActivationCode(String value) { + /** + * Sets the encoded activation code. + * @param value the activation code to use. An activation code can be parsed from a user + * scanned QR code. The format of activation code is defined in SGP.22. For + * example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For + * detail, see {@code com.android.euicc.data.ActivationCode}. Must not be null. + */ + @NonNull + public Builder setEncodedActivationCode(@NonNull String value) { encodedActivationCode = value; return this; } - public Builder setConfirmationCode(String value) { + /** + * Sets the confirmation code. + * @param value the confirmation code to use to authenticate the carrier server got + * subscription download. + */ + @NonNull + public Builder setConfirmationCode(@NonNull String value) { confirmationCode = value; return this; } - public Builder setCarrierName(String value) { + /** + * Sets the user-visible carrier name. + * @param value carrier name. + * @hide + */ + @NonNull + @SystemApi + public Builder setCarrierName(@NonNull String value) { carrierName = value; return this; } - public Builder setAccessRules(List<UiccAccessRule> value) { + /** + * Sets the {@link UiccAccessRule}s dictating access to this subscription. + * @param value A list of {@link UiccAccessRule}s. + * @hide + */ + @NonNull + @SystemApi + public Builder setAccessRules(@NonNull List<UiccAccessRule> value) { accessRules = value; return this; } diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java index 66281edc0de1..fd206c1e803f 100644 --- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java @@ -320,4 +320,11 @@ public final class DelegateRegistrationState implements Parcelable { public int hashCode() { return Objects.hash(mRegisteredTags, mDeregisteringTags, mDeregisteredTags); } + + @Override + public String toString() { + return "DelegateRegistrationState{ registered={" + mRegisteredTags + + "}, deregistering={" + mDeregisteringTags + "}, deregistered={" + + mDeregisteredTags + "}}"; + } } diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java index baa0576cdf13..754814facb71 100644 --- a/telephony/java/android/telephony/ims/ImsUtListener.java +++ b/telephony/java/android/telephony/ims/ImsUtListener.java @@ -178,4 +178,11 @@ public class ImsUtListener { public ImsUtListener(IImsUtListener serviceInterface) { mServiceInterface = serviceInterface; } + + /** + * @hide + */ + public IImsUtListener getListenerInterface() { + return mServiceInterface; + } } diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java index e4d20e965c49..5eb75e762fc9 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -34,14 +35,135 @@ import java.util.List; * network during a SUBSCRIBE request. See RFC3863 for more information. * @hide */ -public class RcsContactPresenceTuple implements Parcelable { +@SystemApi +public final class RcsContactPresenceTuple implements Parcelable { - /** The service id of the MMTEL */ + /** + * The service ID used to indicate that MMTEL service is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; - /** The service id of the Call Composer */ + /** + * The service ID used to indicate that the chat(v1.0) is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session"; + + /** + * The service ID used to indicate that the chat(v2.0) is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession"; + + /** + * The service ID used to indicate that the File Transfer is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP"; + + /** + * The service ID used to indicate that the File Transfer over SMS is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_FT_OVER_SMS = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms"; + + /** + * The service ID used to indicate that the Geolocation Push is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_GEO_PUSH = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush"; + + /** + * The service ID used to indicate that the Geolocation Push via SMS is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms"; + + /** + * The service ID used to indicate that the Call Composer is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ public static final String SERVICE_ID_CALL_COMPOSER = - "org.3gpp.urn:urn-7:3gppservice.ims.icsi.gsma.callcomposer"; + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer"; + + /** + * The service ID used to indicate that the Post Call is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_POST_CALL = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered"; + + /** + * The service ID used to indicate that the Shared Map is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_SHARED_MAP = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap"; + + /** + * The service ID used to indicate that the Shared Sketch is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_SHARED_SKETCH = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch"; + + /** + * The service ID used to indicate that the Chatbot using Session is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot"; + + /** + * The service ID used to indicate that the Standalone Messaging is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT_STANDALONE = + " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa"; + + /** + * The service ID used to indicate that the Chatbot Role is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "SERVICE_ID_", value = { + SERVICE_ID_MMTEL, + SERVICE_ID_CHAT_V1, + SERVICE_ID_CHAT_V2, + SERVICE_ID_FT, + SERVICE_ID_FT_OVER_SMS, + SERVICE_ID_GEO_PUSH, + SERVICE_ID_GEO_PUSH_VIA_SMS, + SERVICE_ID_CALL_COMPOSER, + SERVICE_ID_POST_CALL, + SERVICE_ID_SHARED_MAP, + SERVICE_ID_SHARED_SKETCH, + SERVICE_ID_CHATBOT, + SERVICE_ID_CHATBOT_STANDALONE, + SERVICE_ID_CHATBOT_ROLE + }) + public @interface ServiceId {} /** The service capabilities is available. */ public static final String TUPLE_BASIC_STATUS_OPEN = "open"; @@ -61,7 +183,7 @@ public class RcsContactPresenceTuple implements Parcelable { * An optional addition to the PIDF Presence Tuple containing service capabilities, which is * defined in the servcaps element. See RFC5196, section 3.2.1. */ - public static class ServiceCapabilities implements Parcelable { + public static final class ServiceCapabilities implements Parcelable { /** The service can simultaneously send and receive data. */ public static final String DUPLEX_MODE_FULL = "full"; @@ -88,7 +210,7 @@ public class RcsContactPresenceTuple implements Parcelable { /** * Builder to help construct {@link ServiceCapabilities} instances. */ - public static class Builder { + public static final class Builder { private ServiceCapabilities mCapabilities; @@ -106,7 +228,7 @@ public class RcsContactPresenceTuple implements Parcelable { * Add the supported duplex mode. * @param mode The supported duplex mode */ - public Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) { + public @NonNull Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) { mCapabilities.mSupportedDuplexModeList.add(mode); return this; } @@ -115,7 +237,7 @@ public class RcsContactPresenceTuple implements Parcelable { * Add the unsupported duplex mode. * @param mode The unsupported duplex mode */ - public Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) { + public @NonNull Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) { mCapabilities.mUnsupportedDuplexModeList.add(mode); return this; } @@ -123,7 +245,7 @@ public class RcsContactPresenceTuple implements Parcelable { /** * @return the ServiceCapabilities instance. */ - public ServiceCapabilities build() { + public @NonNull ServiceCapabilities build() { return mCapabilities; } } @@ -149,6 +271,7 @@ public class RcsContactPresenceTuple implements Parcelable { in.readStringList(mSupportedDuplexModeList); in.readStringList(mUnsupportedDuplexModeList); } + @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeBoolean(mIsAudioCapable); @@ -211,18 +334,20 @@ public class RcsContactPresenceTuple implements Parcelable { /** * Builder to help construct {@link RcsContactPresenceTuple} instances. */ - public static class Builder { + public static final class Builder { - private RcsContactPresenceTuple mPresenceTuple; + private final RcsContactPresenceTuple mPresenceTuple; /** * Builds a RcsContactPresenceTuple instance. + * @param status The status associated with the service capability. See RFC3865 for more + * information. * @param serviceId The OMA Presence service-id associated with this capability. See the * OMA Presence SIMPLE specification v1.1, section 10.5.1. * @param serviceVersion The OMA Presence version associated with the service capability. * See the OMA Presence SIMPLE specification v1.1, section 10.5.1. */ - public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId, + public Builder(@NonNull @BasicStatus String status, @NonNull @ServiceId String serviceId, @NonNull String serviceVersion) { mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion); } @@ -230,16 +355,17 @@ public class RcsContactPresenceTuple implements Parcelable { /** * The optional SIP Contact URI associated with the PIDF tuple element. */ - public Builder addContactUri(@NonNull Uri contactUri) { + public @NonNull Builder setContactUri(@NonNull Uri contactUri) { mPresenceTuple.mContactUri = contactUri; return this; } /** * The optional timestamp indicating the data and time of the status change of this tuple. - * See RFC3863, section 4.1.7 for more information on the expected format. + * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format + * string per RFC3339. */ - public Builder addTimeStamp(@NonNull String timestamp) { + public @NonNull Builder setTimestamp(@NonNull String timestamp) { mPresenceTuple.mTimestamp = timestamp; return this; } @@ -248,7 +374,7 @@ public class RcsContactPresenceTuple implements Parcelable { * An optional parameter containing the description element of the service-description. See * OMA Presence SIMPLE specification v1.1 */ - public Builder addDescription(@NonNull String description) { + public @NonNull Builder setServiceDescription(@NonNull String description) { mPresenceTuple.mServiceDescription = description; return this; } @@ -257,7 +383,7 @@ public class RcsContactPresenceTuple implements Parcelable { * An optional parameter containing the service capabilities of the presence tuple if they * are present in the servcaps element. */ - public Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) { + public @NonNull Builder setServiceCapabilities(@NonNull ServiceCapabilities caps) { mPresenceTuple.mServiceCapabilities = caps; return this; } @@ -265,7 +391,7 @@ public class RcsContactPresenceTuple implements Parcelable { /** * @return the constructed instance. */ - public RcsContactPresenceTuple build() { + public @NonNull RcsContactPresenceTuple build() { return mPresenceTuple; } } diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index 5848be8b0bf2..fe855023f5d0 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -33,6 +34,7 @@ import java.util.List; * Contains the User Capability Exchange capabilities corresponding to a contact's URI. * @hide */ +@SystemApi public final class RcsContactUceCapability implements Parcelable { /** Contains presence information associated with the contact */ @@ -70,52 +72,46 @@ public final class RcsContactUceCapability implements Parcelable { public @interface SourceType {} /** + * Capability information for the requested contact has expired and can not be refreshed due to + * a temporary network error. This is a temporary error and the capabilities of the contact + * should be queried again at a later time. + */ + public static final int REQUEST_RESULT_UNKNOWN = 0; + + /** * The requested contact was found to be offline when queried. This is only applicable to * contact capabilities that were queried via OPTIONS requests and the network returned a * 408/480 response. */ - public static final int REQUEST_RESULT_NOT_ONLINE = 0; + public static final int REQUEST_RESULT_NOT_ONLINE = 1; /** * Capability information for the requested contact was not found. The contact should not be * considered an RCS user. */ - public static final int REQUEST_RESULT_NOT_FOUND = 1; + public static final int REQUEST_RESULT_NOT_FOUND = 2; /** * Capability information for the requested contact was found successfully. */ - public static final int REQUEST_RESULT_FOUND = 2; - - /** - * Capability information for the requested contact has expired and can not be refreshed due to - * a temporary network error. This is a temporary error and the capabilities of the contact - * should be queried again at a later time. - */ - public static final int REQUEST_RESULT_UNKNOWN = 3; + public static final int REQUEST_RESULT_FOUND = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "REQUEST_RESULT_", value = { + REQUEST_RESULT_UNKNOWN, REQUEST_RESULT_NOT_ONLINE, REQUEST_RESULT_NOT_FOUND, - REQUEST_RESULT_FOUND, - REQUEST_RESULT_UNKNOWN + REQUEST_RESULT_FOUND }) public @interface RequestResult {} /** - * The base class of {@link OptionsBuilder} and {@link PresenceBuilder} - */ - public static abstract class RcsUcsCapabilityBuilder { - public abstract @NonNull RcsContactUceCapability build(); - } - - /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through SIP OPTIONS. + * @hide */ - public static class OptionsBuilder extends RcsUcsCapabilityBuilder { + public static final class OptionsBuilder { private final RcsContactUceCapability mCapabilities; @@ -144,7 +140,7 @@ public final class RcsContactUceCapability implements Parcelable { * @param tag the supported feature tag * @return this OptionBuilder */ - public @NonNull OptionsBuilder addFeatureTag(String tag) { + public @NonNull OptionsBuilder addFeatureTag(@NonNull String tag) { mCapabilities.mFeatureTags.add(tag); return this; } @@ -154,7 +150,7 @@ public final class RcsContactUceCapability implements Parcelable { * @param tags the list of the supported feature tags * @return this OptionBuilder */ - public @NonNull OptionsBuilder addFeatureTags(List<String> tags) { + public @NonNull OptionsBuilder addFeatureTags(@NonNull List<String> tags) { mCapabilities.mFeatureTags.addAll(tags); return this; } @@ -162,7 +158,6 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the constructed instance. */ - @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -172,7 +167,7 @@ public final class RcsContactUceCapability implements Parcelable { * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through a presence server. */ - public static class PresenceBuilder extends RcsUcsCapabilityBuilder { + public static final class PresenceBuilder { private final RcsContactUceCapability mCapabilities; @@ -195,7 +190,7 @@ public final class RcsContactUceCapability implements Parcelable { * @param tuple The {@link RcsContactPresenceTuple} to be added into. * @return this PresenceBuilder */ - public @NonNull PresenceBuilder addCapabilityTuple(RcsContactPresenceTuple tuple) { + public @NonNull PresenceBuilder addCapabilityTuple(@NonNull RcsContactPresenceTuple tuple) { mCapabilities.mPresenceTuples.add(tuple); return this; } @@ -205,7 +200,8 @@ public final class RcsContactUceCapability implements Parcelable { * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into. * @return this PresenceBuilder */ - public @NonNull PresenceBuilder addCapabilityTuples(List<RcsContactPresenceTuple> tuples) { + public @NonNull PresenceBuilder addCapabilityTuples( + @NonNull List<RcsContactPresenceTuple> tuples) { mCapabilities.mPresenceTuples.addAll(tuples); return this; } @@ -213,7 +209,6 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the RcsContactUceCapability instance. */ - @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -282,7 +277,8 @@ public final class RcsContactUceCapability implements Parcelable { * @return The feature tags present in the OPTIONS response from the network. * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is - * {@link CAPABILITY_MECHANISM_OPTIONS} + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS} + * @hide */ public @NonNull List<String> getOptionsFeatureTags() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { @@ -296,9 +292,9 @@ public final class RcsContactUceCapability implements Parcelable { * contained in the NOTIFY response from the network. * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is - * {@link CAPABILITY_MECHANISM_PRESENCE} + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() { + public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return Collections.emptyList(); } @@ -308,13 +304,14 @@ public final class RcsContactUceCapability implements Parcelable { /** * Get the RcsContactPresenceTuple associated with the given service id. * @param serviceId The service id to get the presence tuple. - * @return The RcsContactPresenceTuple which has the given service id. + * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the + * service id does not exist in the list of presence tuples returned from the network. * * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is - * {@link CAPABILITY_MECHANISM_PRESENCE} + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @Nullable RcsContactPresenceTuple getPresenceTuple(String serviceId) { + public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return null; } diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 8d7742b7510b..070fd799d6cc 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -36,7 +36,9 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; /** @@ -61,6 +63,7 @@ public class RcsUceAdapter { * RcsFeature should not publish capabilities or service capability requests. * @hide */ + @SystemApi public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; /**@hide*/ @@ -75,12 +78,14 @@ public class RcsUceAdapter { * An unknown error has caused the request to fail. * @hide */ + @SystemApi public static final int ERROR_GENERIC_FAILURE = 1; /** * The carrier network does not have UCE support enabled for this subscriber. * @hide */ + @SystemApi public static final int ERROR_NOT_ENABLED = 2; /** @@ -88,12 +93,14 @@ public class RcsUceAdapter { * 1x only currently). * @hide */ + @SystemApi public static final int ERROR_NOT_AVAILABLE = 3; /** * The network has responded with SIP 403 error and a reason "User not registered." * @hide */ + @SystemApi public static final int ERROR_NOT_REGISTERED = 4; /** @@ -101,19 +108,22 @@ public class RcsUceAdapter { * presence" for this subscriber. * @hide */ + @SystemApi public static final int ERROR_NOT_AUTHORIZED = 5; /** * The network has responded to this request with a SIP 403 error and no reason. * @hide */ + @SystemApi public static final int ERROR_FORBIDDEN = 6; /** - * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS + * The contact URI requested is not provisioned for voice or it is not known as an IMS * subscriber to the carrier network. * @hide */ + @SystemApi public static final int ERROR_NOT_FOUND = 7; /** @@ -121,6 +131,7 @@ public class RcsUceAdapter { * with a lower number of contact numbers. The number varies per carrier. * @hide */ + @SystemApi // TODO: Try to integrate this into the API so that the service will split based on carrier. public static final int ERROR_REQUEST_TOO_LARGE = 8; @@ -128,26 +139,30 @@ public class RcsUceAdapter { * The network did not respond to the capabilities request before the request timed out. * @hide */ - public static final int ERROR_REQUEST_TIMEOUT = 10; + @SystemApi + public static final int ERROR_REQUEST_TIMEOUT = 9; /** * The request failed due to the service having insufficient memory. * @hide */ - public static final int ERROR_INSUFFICIENT_MEMORY = 11; + @SystemApi + public static final int ERROR_INSUFFICIENT_MEMORY = 10; /** * The network was lost while trying to complete the request. * @hide */ - public static final int ERROR_LOST_NETWORK = 12; + @SystemApi + public static final int ERROR_LOST_NETWORK = 11; /** * The network is temporarily unavailable or busy. Retries should only be done after the retry * time returned in {@link CapabilitiesCallback#onError} has elapsed. * @hide */ - public static final int ERROR_SERVER_UNAVAILABLE = 13; + @SystemApi + public static final int ERROR_SERVER_UNAVAILABLE = 12; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @@ -168,69 +183,93 @@ public class RcsUceAdapter { public @interface ErrorCode {} /** + * A capability update has been requested but the reason is unknown. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; + + /** * A capability update has been requested due to the Entity Tag (ETag) expiring. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; + /** * A capability update has been requested due to moving to LTE with VoPS disabled. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; + /** * A capability update has been requested due to moving to LTE with VoPS enabled. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; + /** * A capability update has been requested due to moving to eHRPD. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; + /** * A capability update has been requested due to moving to HSPA+. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; + /** * A capability update has been requested due to moving to 3G. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; + /** * A capability update has been requested due to moving to 2G. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; + /** * A capability update has been requested due to moving to WLAN * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; + /** * A capability update has been requested due to moving to IWLAN * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; - /** - * A capability update has been requested but the reason is unknown. - * @hide - */ - public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; + /** * A capability update has been requested due to moving to 5G NR with VoPS disabled. * @hide */ + @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; + /** * A capability update has been requested due to moving to 5G NR with VoPS enabled. * @hide */ + @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "ERROR_", value = { + CAPABILITY_UPDATE_TRIGGER_UNKNOWN, CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED, @@ -240,7 +279,6 @@ public class RcsUceAdapter { CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN, - CAPABILITY_UPDATE_TRIGGER_UNKNOWN, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED }) @@ -251,32 +289,37 @@ public class RcsUceAdapter { * UCE. * @hide */ + @SystemApi public static final int PUBLISH_STATE_OK = 1; /** * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. * @hide */ + @SystemApi public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; /** * The device has tried to publish its capabilities, which has resulted in an error. This error - * is related to the fact that the device is not VoLTE provisioned. + * is related to the fact that the device is not provisioned for voice. * @hide */ - public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; + @SystemApi + public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; /** * The device has tried to publish its capabilities, which has resulted in an error. This error * is related to the fact that the device is not RCS or UCE provisioned. * @hide */ + @SystemApi public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; /** * The last publish resulted in a "408 Request Timeout" response. * @hide */ + @SystemApi public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; /** @@ -286,6 +329,7 @@ public class RcsUceAdapter { * Device shall retry with exponential back-off. * @hide */ + @SystemApi public static final int PUBLISH_STATE_OTHER_ERROR = 6; /**@hide*/ @@ -293,7 +337,7 @@ public class RcsUceAdapter { @IntDef(prefix = "PUBLISH_STATE_", value = { PUBLISH_STATE_OK, PUBLISH_STATE_NOT_PUBLISHED, - PUBLISH_STATE_VOLTE_PROVISION_ERROR, + PUBLISH_STATE_VOICE_PROVISION_ERROR, PUBLISH_STATE_RCS_PROVISION_ERROR, PUBLISH_STATE_REQUEST_TIMEOUT, PUBLISH_STATE_OTHER_ERROR @@ -301,56 +345,62 @@ public class RcsUceAdapter { public @interface PublishState {} /** - * An application can use {@link #registerPublishStateCallback} to register a - * {@link PublishStateCallback), which will notify the user when the publish state to the - * network changes. + * An application can use {@link #addOnPublishStateChangedListener} to register a + * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to + * the network changes. * @hide */ - public static class PublishStateCallback { + @SystemApi + public interface OnPublishStateChangedListener { + /** + * Notifies the callback when the publish state has changed. + * @param publishState The latest update to the publish state. + */ + void onPublishStateChange(@PublishState int publishState); + } - private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub { + /** + * An application can use {@link #addOnPublishStateChangedListener} to register a + * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to + * the network changes. + * @hide + */ + public static class PublishStateCallbackAdapter { - private final PublishStateCallback mLocalCallback; - private Executor mExecutor; + private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub { + private final OnPublishStateChangedListener mPublishStateChangeListener; + private final Executor mExecutor; - PublishStateBinder(PublishStateCallback c) { - mLocalCallback = c; + PublishStateBinder(Executor executor, OnPublishStateChangedListener listener) { + mExecutor = executor; + mPublishStateChangeListener = listener; } @Override public void onPublishStateChanged(int publishState) { - if (mLocalCallback == null) return; + if (mPublishStateChangeListener == null) return; final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> mLocalCallback.onChanged(publishState)); + mExecutor.execute(() -> + mPublishStateChangeListener.onPublishStateChange(publishState)); } finally { restoreCallingIdentity(callingIdentity); } } - - private void setExecutor(Executor executor) { - mExecutor = executor; - } } - private final PublishStateBinder mBinder = new PublishStateBinder(this); + private final PublishStateBinder mBinder; + + public PublishStateCallbackAdapter(@NonNull Executor executor, + @NonNull OnPublishStateChangedListener listener) { + mBinder = new PublishStateBinder(executor, listener); + } /**@hide*/ public final IRcsUcePublishStateCallback getBinder() { return mBinder; } - - private void setExecutor(Executor executor) { - mBinder.setExecutor(executor); - } - - /** - * Notifies the callback when the publish state has changed. - * @param publishState The latest update to the publish state. - */ - public void onChanged(@PublishState int publishState) { - } } /** @@ -368,6 +418,7 @@ public class RcsUceAdapter { * @see #requestCapabilities(Executor, List, CapabilitiesCallback) * @hide */ + @SystemApi public interface CapabilitiesCallback { /** @@ -387,14 +438,16 @@ public class RcsUceAdapter { * The pending request has resulted in an error and may need to be retried, depending on the * error code. * @param errorCode The reason for the framework being unable to process the request. - * @param retryAfterMilliseconds The time in milliseconds the requesting application should + * @param retryIntervalMillis The time in milliseconds the requesting application should * wait before retrying, if non-zero. */ - void onError(@ErrorCode int errorCode, long retryAfterMilliseconds); + void onError(@ErrorCode int errorCode, long retryIntervalMillis); } private final Context mContext; private final int mSubId; + private final Map<OnPublishStateChangedListener, PublishStateCallbackAdapter> + mPublishStateCallbacks; /** * Not to be instantiated directly, use {@link ImsRcsManager#getUceAdapter()} to instantiate @@ -404,6 +457,7 @@ public class RcsUceAdapter { RcsUceAdapter(Context context, int subId) { mContext = context; mSubId = subId; + mPublishStateCallbacks = new HashMap<>(); } /** @@ -418,9 +472,9 @@ public class RcsUceAdapter { * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. * + * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param executor The executor that will be used when the request is completed and the * {@link CapabilitiesCallback} is called. - * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param c A one-time callback for when the request for capabilities completes or there is an * error processing the request. * @throws ImsException if the subscription associated with this instance of @@ -429,9 +483,10 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, - @NonNull List<Uri> contactNumbers, + public void requestCapabilities(@NonNull List<Uri> contactNumbers, + @NonNull @CallbackExecutor Executor executor, @NonNull CapabilitiesCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback."); @@ -455,8 +510,7 @@ public class RcsUceAdapter { public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> - c.onCapabilitiesReceived(contactCapabilities)); + executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); } finally { restoreCallingIdentity(callingIdentity); } @@ -510,13 +564,17 @@ public class RcsUceAdapter { * {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. * * @param contactNumber The contact of the capabilities is being requested for. + * @param executor The executor that will be used when the request is completed and the + * {@link CapabilitiesCallback} is called. * @param c A one-time callback for when the request for capabilities completes or there is * an error processing the request. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) - public void requestNetworkAvailability(@NonNull @CallbackExecutor Executor executor, - @NonNull Uri contactNumber, @NonNull CapabilitiesCallback c) throws ImsException { + public void requestAvailability(@NonNull Uri contactNumber, + @NonNull @CallbackExecutor Executor executor, + @NonNull CapabilitiesCallback c) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } @@ -529,7 +587,7 @@ public class RcsUceAdapter { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "requestNetworkAvailability: IImsRcsController is null"); + Log.e(TAG, "requestAvailability: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -539,8 +597,7 @@ public class RcsUceAdapter { public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> - c.onCapabilitiesReceived(contactCapabilities)); + executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); } finally { restoreCallingIdentity(callingIdentity); } @@ -566,12 +623,12 @@ public class RcsUceAdapter { }; try { - imsRcsController.requestNetworkAvailability(mSubId, mContext.getOpPackageName(), + imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(), mContext.getAttributionTag(), contactNumber, internalCallback); } catch (ServiceSpecificException e) { throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#requestNetworkAvailability", e); + Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -588,6 +645,7 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @PublishState int getUcePublishState() throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); @@ -609,43 +667,45 @@ public class RcsUceAdapter { } /** - * Registers a {@link PublishStateCallback} with the system, which will provide publish state - * updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}. + * Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish + * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}. * <p> * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription * changed events and call {@link #unregisterPublishStateCallback} to clean up. * <p> - * The registered {@link PublishStateCallback} will also receive a callback when it is + * The registered {@link OnPublishStateChangedListener} will also receive a callback when it is * registered with the current publish state. * * @param executor The executor the listener callback events should be run on. - * @param c The {@link PublishStateCallback} to be added. + * @param listener The {@link OnPublishStateChangedListener} to be added. * @throws ImsException if the subscription associated with this callback is valid, but * the {@link ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void registerPublishStateCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull PublishStateCallback c) throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null PublishStateCallback."); - } + public void addOnPublishStateChangedListener(@NonNull @CallbackExecutor Executor executor, + @NonNull OnPublishStateChangedListener listener) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } + if (listener == null) { + throw new IllegalArgumentException( + "Must include a non-null OnPublishStateChangedListener."); + } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "registerPublishStateCallback : IImsRcsController is null"); + Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } - c.setExecutor(executor); + PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener); try { - imsRcsController.registerUcePublishStateCallback(mSubId, c.getBinder()); + imsRcsController.registerUcePublishStateCallback(mSubId, stateCallback.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { @@ -656,34 +716,41 @@ public class RcsUceAdapter { } /** - * Removes an existing {@link PublishStateCallback}. + * Removes an existing {@link OnPublishStateChangedListener}. * <p> * When the subscription associated with this callback is removed * (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method * is called for an inactive subscription, it will result in a no-op. * - * @param c The callback to be unregistered. + * @param listener The callback to be unregistered. * @throws ImsException if the subscription associated with this callback is valid, but * the {@link ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void unregisterPublishStateCallback(@NonNull PublishStateCallback c) - throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null PublishStateCallback."); + public void removeOnPublishStateChangedListener( + @NonNull OnPublishStateChangedListener listener) throws ImsException { + if (listener == null) { + throw new IllegalArgumentException( + "Must include a non-null OnPublishStateChangedListener."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "unregisterPublishStateCallback: IImsRcsController is null"); + Log.e(TAG, "removeOnPublishStateChangedListener: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } + PublishStateCallbackAdapter callback = removePublishStateCallback(listener); + if (callback == null) { + return; + } + try { - imsRcsController.unregisterUcePublishStateCallback(mSubId, c.getBinder()); + imsRcsController.unregisterUcePublishStateCallback(mSubId, callback.getBinder()); } catch (android.os.ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { @@ -763,6 +830,36 @@ public class RcsUceAdapter { } } + /** + * Add the {@link OnPublishStateChangedListener} to collection for tracking. + * @param executor The executor that will be used when the publish state is changed and the + * {@link OnPublishStateChangedListener} is called. + * @param listener The {@link OnPublishStateChangedListener} to call the publish state changed. + * @return The {@link PublishStateCallbackAdapter} to wrapper the + * {@link OnPublishStateChangedListener} + */ + private PublishStateCallbackAdapter addPublishStateCallback(@NonNull Executor executor, + @NonNull OnPublishStateChangedListener listener) { + PublishStateCallbackAdapter adapter = new PublishStateCallbackAdapter(executor, listener); + synchronized (mPublishStateCallbacks) { + mPublishStateCallbacks.put(listener, adapter); + } + return adapter; + } + + /** + * Remove the existing {@link OnPublishStateChangedListener}. + * @param listener The {@link OnPublishStateChangedListener} to remove from the collection. + * @return The wrapper class {@link PublishStateCallbackAdapter} associated with the + * {@link OnPublishStateChangedListener}. + */ + private PublishStateCallbackAdapter removePublishStateCallback( + @NonNull OnPublishStateChangedListener listener) { + synchronized (mPublishStateCallbacks) { + return mPublishStateCallbacks.remove(listener); + } + } + private IImsRcsController getIImsRcsController() { IBinder binder = TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index e085dec10546..2c75368b86bf 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.net.Uri; import android.os.Binder; import android.telephony.AccessNetworkConstants; +import android.telephony.NetworkRegistrationInfo; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; @@ -85,6 +86,22 @@ public interface RegistrationManager { AccessNetworkConstants.TRANSPORT_TYPE_WLAN); }}; + /** @hide */ + @NonNull + static String registrationStateToString( + final @NetworkRegistrationInfo.RegistrationState int value) { + switch (value) { + case REGISTRATION_STATE_NOT_REGISTERED: + return "REGISTRATION_STATE_NOT_REGISTERED"; + case REGISTRATION_STATE_REGISTERING: + return "REGISTRATION_STATE_REGISTERING"; + case REGISTRATION_STATE_REGISTERED: + return "REGISTRATION_STATE_REGISTERED"; + default: + return Integer.toString(value); + } + } + /** * Callback class for receiving IMS network Registration callback events. * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java index eddbb1002f20..8762b6a712f2 100644 --- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java @@ -280,6 +280,12 @@ public final class SipDelegateImsConfiguration implements Parcelable { "sip_config_path_header_string"; /** + * The SIP User-Agent header value used by the IMS stack during IMS registration. + */ + public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = + "sip_config_sip_user_agent_header_string"; + + /** * SIP User part string in contact header */ public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = @@ -292,12 +298,20 @@ public final class SipDelegateImsConfiguration implements Parcelable { "sip_config_p_access_network_info_header_string"; /** - * SIP P-last-access-network-info header string + * The SIP P-last-access-network-info header value, populated for networks that require this + * information to be provided in outgoing SIP messages. */ public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; /** + * The Cellular-Network-Info header value (See 3GPP 24.229, section 7.2.15), populated for + * networks that require this information to be provided as part of outgoing SIP messages. + */ + public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = + "sip_config_cellular_network_info_header_string"; + + /** * SIP P-associated-uri header string */ public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = @@ -320,9 +334,11 @@ public final class SipDelegateImsConfiguration implements Parcelable { KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING, KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING, KEY_SIP_CONFIG_PATH_HEADER_STRING, + KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING, KEY_SIP_CONFIG_URI_USER_PART_STRING, KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING, KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING, + KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING, KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING }) @Retention(RetentionPolicy.SOURCE) diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java index 1539224dedcf..9cfa640fce18 100644 --- a/telephony/java/android/telephony/ims/SipMessage.java +++ b/telephony/java/android/telephony/ims/SipMessage.java @@ -16,12 +16,16 @@ package android.telephony.ims; +import static java.nio.charset.StandardCharsets.UTF_8; + import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.telephony.SipMessageParsingUtils; + import java.util.Arrays; import java.util.Objects; @@ -37,9 +41,7 @@ import java.util.Objects; public final class SipMessage implements Parcelable { // Should not be set to true for production! private static final boolean IS_DEBUGGING = Build.IS_ENG; - - private static final String[] SIP_REQUEST_METHODS = new String[] {"INVITE", "ACK", "OPTIONS", - "BYE", "CANCEL", "REGISTER"}; + private static final String CRLF = "\r\n"; private final String mStartLine; private final String mHeaderSection; @@ -72,6 +74,7 @@ public final class SipMessage implements Parcelable { mContent = new byte[source.readInt()]; source.readByteArray(mContent); } + /** * @return The start line of the SIP message, which contains either the request-line or * status-line. @@ -128,34 +131,25 @@ public final class SipMessage implements Parcelable { } else { b.append(sanitizeStartLineRequest(mStartLine)); } - b.append("], ["); - b.append("Header: ["); + b.append("], Header: ["); if (IS_DEBUGGING) { b.append(mHeaderSection); } else { // only identify transaction id/call ID when it is available. b.append("***"); } - b.append("], "); - b.append("Content: [NOT SHOWN]"); + b.append("], Content: "); + b.append(getContent().length == 0 ? "[NONE]" : "[NOT SHOWN]"); return b.toString(); } /** - * Start lines containing requests are formatted: METHOD SP Request-URI SP SIP-Version CRLF. * Detect if this is a REQUEST and redact Request-URI portion here, as it contains PII. */ private String sanitizeStartLineRequest(String startLine) { + if (!SipMessageParsingUtils.isSipRequest(startLine)) return startLine; String[] splitLine = startLine.split(" "); - if (splitLine == null || splitLine.length == 0) { - return "(INVALID STARTLINE)"; - } - for (String method : SIP_REQUEST_METHODS) { - if (splitLine[0].contains(method)) { - return splitLine[0] + " <Request-URI> " + splitLine[2]; - } - } - return startLine; + return splitLine[0] + " <Request-URI> " + splitLine[2]; } @Override @@ -174,4 +168,19 @@ public final class SipMessage implements Parcelable { result = 31 * result + Arrays.hashCode(mContent); return result; } + + /** + * @return the UTF-8 encoded SIP message. + */ + public @NonNull byte[] getEncodedMessage() { + byte[] header = new StringBuilder() + .append(mStartLine) + .append(mHeaderSection) + .append(CRLF) + .toString().getBytes(UTF_8); + byte[] sipMessage = new byte[header.length + mContent.length]; + System.arraycopy(header, 0, sipMessage, 0, header.length); + System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length); + return sipMessage; + } } diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java new file mode 100644 index 000000000000..4435640e008c --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java @@ -0,0 +1,115 @@ +/* + * 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.telephony.ims.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Binder; +import android.os.RemoteException; +import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.stub.CapabilityExchangeEventListener; +import android.util.Log; + +import java.util.List; + +/** + * The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by + * the framework. This wrapper class also delivers the request to the framework when receive the + * request from the network. + * @hide + */ +public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventListener { + + private static final String LOG_TAG = "CapExchangeListener"; + + private final ICapabilityExchangeEventListener mListenerBinder; + + public CapabilityExchangeAidlWrapper(@Nullable ICapabilityExchangeEventListener listener) { + mListenerBinder = listener; + } + + /** + * Receives the request of publishing capabilities from the network and deliver this request + * to the framework via the registered capability exchange event listener. + */ + public void onRequestPublishCapabilities(int publishTriggerType) { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + try { + listener.onRequestPublishCapabilities(publishTriggerType); + } catch (RemoteException e) { + Log.w(LOG_TAG, "request publish capabilities exception: " + e); + } + } + + /** + * Receives the unpublish notification and deliver this callback to the framework. + */ + public void onUnpublish() { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + try { + listener.onUnpublish(); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unpublish exception: " + e); + } + } + + /** + * Receives the callback of the remote capability request from the network and deliver this + * request to the framework. + */ + public void onRemoteCapabilityRequest(@NonNull Uri contactUri, + @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + + IOptionsRequestCallback internalCallback = new IOptionsRequestCallback.Stub() { + @Override + public void respondToCapabilityRequest(RcsContactUceCapability ownCapabilities) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + callback.onRespondToCapabilityRequest(ownCapabilities); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + @Override + public void respondToCapabilityRequestWithError(int code, String reason) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + callback.onRespondToCapabilityRequestWithError(code, reason); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + }; + + try { + listener.onRemoteCapabilityRequest(contactUri, remoteCapabilities, internalCallback); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Remote capability request exception: " + e); + } + } +} diff --git a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl index a4ffbef9fa84..078ac919b75e 100644 --- a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl @@ -22,54 +22,15 @@ import android.telephony.ims.aidl.IOptionsRequestCallback; import java.util.List; /** - * Listener interface for the ImsService to use to notify the framework of UCE events. + * Listener interface for the ImsService to use to notify the framework of UCE + * events. + * + * See CapabilityExchangeEventListener for more information. * {@hide} */ oneway interface ICapabilityExchangeEventListener { - /** - * Trigger the framework to provide a capability update using - * {@link RcsCapabilityExchangeImplBase#publishCapabilities}. - * <p> - * This is typically used when trying to generate an initial PUBLISH for a new - * subscription to the network. The device will cache all presence publications - * after boot until this method is called the first time. - * @param publishTriggerType {@link StackPublishTriggerType} The reason for the - * capability update request. - * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is - * not currently connected to the framework. This can happen if the - * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the - * {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare - * cases when the Telephony stack has crashed. - */ void onRequestPublishCapabilities(int publishTriggerType); - - /** - * Notify the framework that the device's capabilities have been unpublished from the network. - * - * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ void onUnpublish(); - - /** - * Inform the framework of a query for this device's UCE capabilities. - * <p> - * The framework will respond via the - * {@link IOptionsRequestCallback#respondToCapabilityRequest} or - * {@link IOptionsRequestCallback#respondToCapabilityRequestWithError} method. - * @param contactUri The URI associated with the remote contact that is requesting capabilities. - * @param remoteCapabilities The remote contact's capability information. - * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received - * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when - * the Telephony stack has crashed. - */ void onRemoteCapabilityRequest(in Uri contactUri, - in List<String> remoteCapabilities, - IOptionsRequestCallback cb); + in List<String> remoteCapabilities, IOptionsRequestCallback cb); } diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 36349895c35b..7a6c28bddd09 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -52,7 +52,7 @@ interface IImsRcsController { // ImsUceAdapter specific void requestCapabilities(int subId, String callingPackage, String callingFeatureId, in List<Uri> contactNumbers, IRcsUceControllerCallback c); - void requestNetworkAvailability(int subId, String callingPackage, + void requestAvailability(int subId, String callingPackage, String callingFeatureId, in Uri contactNumber, IRcsUceControllerCallback c); int getUcePublishState(int subId); diff --git a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl index d55670dd313b..d4d5301f38fa 100644 --- a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl @@ -33,7 +33,6 @@ oneway interface IOptionsRequestCallback { /** * Respond to a remote capability request from the contact specified with the * specified error. - * @param contactUri A URI containing the remote contact. * @param code The SIP response code to respond with. * @param reason A non-null String containing the reason associated with the SIP code. */ diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java index 522ad8160870..9d919015087d 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java @@ -28,6 +28,10 @@ import android.telephony.ims.SipDelegateImsConfiguration; import android.telephony.ims.SipDelegateManager; import android.telephony.ims.SipMessage; import android.telephony.ims.stub.SipDelegate; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.telephony.SipMessageParsingUtils; import java.util.ArrayList; import java.util.Set; @@ -40,6 +44,7 @@ import java.util.concurrent.Executor; * @hide */ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMessageCallback { + private static final String LOG_TAG = "SipDelegateAW"; private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() { @Override @@ -183,11 +188,15 @@ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMe } private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) { - //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage - // transaction ID can not be parsed. + String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection()); + if (TextUtils.isEmpty(transactionId)) { + Log.w(LOG_TAG, "failure to parse SipMessage."); + throw new IllegalArgumentException("Malformed SipMessage, can not determine " + + "transaction ID."); + } SipDelegate d = mDelegate; if (d != null) { - mExecutor.execute(() -> d.notifyMessageReceiveError(null, reason)); + mExecutor.execute(() -> d.notifyMessageReceiveError(transactionId, reason)); } } } diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java index a35039bd7668..c877aca8ba96 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java @@ -28,9 +28,12 @@ import android.telephony.ims.SipMessage; import android.telephony.ims.stub.DelegateConnectionMessageCallback; import android.telephony.ims.stub.DelegateConnectionStateCallback; import android.telephony.ims.stub.SipDelegate; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import com.android.internal.telephony.SipMessageParsingUtils; + import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.Executor; @@ -265,9 +268,13 @@ public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection, } private void notifyLocalMessageFailedToSend(SipMessage m, int reason) { - //TODO: parse transaction ID or throw IllegalArgumentException if the SipMessage - // transaction ID can not be parsed. + String transactionId = SipMessageParsingUtils.getTransactionId(m.getHeaderSection()); + if (TextUtils.isEmpty(transactionId)) { + Log.w(LOG_TAG, "sendMessage detected a malformed SipMessage and can not get a " + + "transaction ID."); + throw new IllegalArgumentException("Could not send SipMessage due to malformed header"); + } mExecutor.execute(() -> - mMessageCallback.onMessageSendFailure(null, reason)); + mMessageCallback.onMessageSendFailure(transactionId, reason)); } } diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 96ca0225040f..8b26c3b27a3d 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -336,7 +336,7 @@ public abstract class ImsFeature { /** * @hide */ - public final void initialize(Context context, int slotId) { + public void initialize(Context context, int slotId) { mContext = context; mSlotId = slotId; } diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index cde7067e8bf3..22df921c4214 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -21,9 +21,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.Context; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper; import android.telephony.ims.aidl.ICapabilityExchangeEventListener; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsRcsFeature; @@ -33,6 +35,7 @@ import android.telephony.ims.aidl.ISubscribeResponseCallback; import android.telephony.ims.aidl.RcsOptionsResponseAidlWrapper; import android.telephony.ims.aidl.RcsPublishResponseAidlWrapper; import android.telephony.ims.aidl.RcsSubscribeResponseAidlWrapper; +import android.telephony.ims.stub.CapabilityExchangeEventListener; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback; @@ -114,8 +117,10 @@ public class RcsFeature extends ImsFeature { @Override public void setCapabilityExchangeEventListener( @Nullable ICapabilityExchangeEventListener listener) throws RemoteException { - executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(listener), - "setCapabilityExchangeEventListener"); + CapabilityExchangeEventListener listenerWrapper = + new CapabilityExchangeAidlWrapper(listener); + executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener( + mExecutor, listenerWrapper), "setCapabilityExchangeEventListener"); } @Override @@ -245,9 +250,10 @@ public class RcsFeature extends ImsFeature { } } + private final Executor mExecutor; private final RcsFeatureBinder mImsRcsBinder; private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl; - private ICapabilityExchangeEventListener mCapExchangeEventListener; + private CapabilityExchangeEventListener mCapExchangeEventListener; /** * Create a new RcsFeature. @@ -255,26 +261,45 @@ public class RcsFeature extends ImsFeature { * Method stubs called from the framework will be called asynchronously. To specify the * {@link Executor} that the methods stubs will be called, use * {@link RcsFeature#RcsFeature(Executor)} instead. + * + * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature. */ + @Deprecated public RcsFeature() { super(); + mExecutor = Runnable::run; // Run on the Binder threads that call them. - mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run); + mImsRcsBinder = new RcsFeatureBinder(this, mExecutor); } /** * Create a new RcsFeature using the Executor specified for methods being called by the * framework. - * @param executor The executor for the framework to use when making calls to this service. - * @hide + * @param executor The executor for the framework to use when executing the methods overridden + * by the implementation of RcsFeature. */ public RcsFeature(@NonNull Executor executor) { super(); if (executor == null) { throw new IllegalArgumentException("executor can not be null."); } + mExecutor = executor; // Run on the Binder thread by default. - mImsRcsBinder = new RcsFeatureBinder(this, executor); + mImsRcsBinder = new RcsFeatureBinder(this, mExecutor); + } + + /** + * Called when the RcsFeature is initialized. + * + * @param context The context that is used in the ImsService. + * @param slotId The slot ID associated with the RcsFeature. + * @hide + */ + @Override + public void initialize(Context context, int slotId) { + super.initialize(context, slotId); + // Notify that the RcsFeature is ready. + mExecutor.execute(() -> onFeatureReady()); } /** @@ -348,13 +373,26 @@ public class RcsFeature extends ImsFeature { * operation and the RcsFeature sets the status of the capability to true using * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. * - * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements presence + * @param executor The executor for the framework to use when request RCS resquests to this + * service. + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability * exchange if it is supported by the device. - * @hide */ - public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl() { + public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl( + @NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) { // Base Implementation, override to implement functionality - return new RcsCapabilityExchangeImplBase(); + return new RcsCapabilityExchangeImplBase(executor); + } + + /** + * Remove the given CapabilityExchangeImplBase instance. + * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be removed. + */ + public void removeCapabilityExchangeImpl( + @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) { + // Override to implement the process of removing RcsCapabilityExchangeImplBase instance. } /**{@inheritDoc}*/ @@ -377,18 +415,58 @@ public class RcsFeature extends ImsFeature { return mImsRcsBinder; } - private void setCapabilityExchangeEventListener(ICapabilityExchangeEventListener listener) { - mCapExchangeEventListener = listener; - if (mCapExchangeEventListener != null) { - onFeatureReady(); + /** + * Set the capability exchange listener. + * @param executor The executor for the framework to use when request RCS requests to this + * service. + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + */ + private void setCapabilityExchangeEventListener(@NonNull Executor executor, + @Nullable CapabilityExchangeEventListener listener) { + synchronized (mLock) { + mCapExchangeEventListener = listener; + if (mCapExchangeEventListener != null) { + initRcsCapabilityExchangeImplBase(executor, mCapExchangeEventListener); + } else { + // Remove the RcsCapabilityExchangeImplBase instance when the capability exchange + // instance has been removed in the framework. + if (mCapabilityExchangeImpl != null) { + removeCapabilityExchangeImpl(mCapabilityExchangeImpl); + } + mCapabilityExchangeImpl = null; + } } } - private RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() { + /** + * Initialize the RcsCapabilityExchangeImplBase instance if the capability exchange instance + * has already been created in the framework. + * @param executor The executor for the framework to use when request RCS requests to this + * service. + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + */ + private void initRcsCapabilityExchangeImplBase(@NonNull Executor executor, + @NonNull CapabilityExchangeEventListener listener) { + synchronized (mLock) { + // Remove the original instance + if (mCapabilityExchangeImpl != null) { + removeCapabilityExchangeImpl(mCapabilityExchangeImpl); + } + mCapabilityExchangeImpl = createCapabilityExchangeImpl(executor, listener); + } + } + + /** + * @return the {@link RcsCapabilityExchangeImplBase} associated with the RcsFeature. + */ + private @NonNull RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() { synchronized (mLock) { + // The method should not be called if the instance of RcsCapabilityExchangeImplBase has + // not been created yet. if (mCapabilityExchangeImpl == null) { - mCapabilityExchangeImpl = createCapabilityExchangeImpl(); - mCapabilityExchangeImpl.setEventListener(mCapExchangeEventListener); + throw new IllegalStateException("Session is not available."); } return mCapabilityExchangeImpl; } diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java new file mode 100644 index 000000000000..d9734a7475c0 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java @@ -0,0 +1,84 @@ +/* + * 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.telephony.ims.stub; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.telephony.ims.ImsException; +import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; + +/** + * The interface of the capabilities event listener for ImsService to notify the framework of the + * UCE request and status updated. + * @hide + */ +@SystemApi +public interface CapabilityExchangeEventListener { + /** + * Interface used by the framework to respond to OPTIONS requests. + * @hide + */ + interface OptionsRequestCallback { + /** + * Respond to a remote capability request from the contact specified with the + * capabilities of this device. + * @param ownCapabilities The capabilities of this device. + */ + void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities); + + /** + * Respond to a remote capability request from the contact specified with the + * specified error. + * @param code The SIP response code to respond with. + * @param reason A non-null String containing the reason associated with the SIP code. + */ + void onRespondToCapabilityRequestWithError(int code, @NonNull String reason); + } + + /** + * Trigger the framework to provide a capability update using + * {@link RcsCapabilityExchangeImplBase#publishCapabilities}. + * <p> + * This is typically used when trying to generate an initial PUBLISH for a new subscription to + * the network. The device will cache all presence publications after boot until this method is + * called the first time. + * @param publishTriggerType {@link RcsUceAdapter#StackPublishTriggerType} The reason for the + * capability update request. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently + * connected to the framework. This can happen if the {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the + * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the + * Telephony stack has crashed. + */ + void onRequestPublishCapabilities( + @RcsUceAdapter.StackPublishTriggerType int publishTriggerType) throws ImsException; + + /** + * Notify the framework that the device's capabilities have been unpublished + * from the network. + * + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently + * connected to the framework. This can happen if the {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the + * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the + * Telephony stack has crashed. + */ + void onUnpublish() throws ImsException; +} diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java index 06c35eaec6dd..2e35d27614d1 100644 --- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -23,6 +23,8 @@ import android.util.Log; import com.android.ims.internal.IImsEcbm; import com.android.ims.internal.IImsEcbmListener; +import java.util.Objects; + /** * Base implementation of ImsEcbm, which implements stub versions of the methods * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports. @@ -36,11 +38,27 @@ import com.android.ims.internal.IImsEcbmListener; public class ImsEcbmImplBase { private static final String TAG = "ImsEcbmImplBase"; + private final Object mLock = new Object(); private IImsEcbmListener mListener; - private IImsEcbm mImsEcbm = new IImsEcbm.Stub() { + private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() { @Override public void setListener(IImsEcbmListener listener) { - mListener = listener; + synchronized (mLock) { + if (mImsEcbm != null && listener != null && Objects.equals( + mImsEcbm.asBinder(), listener.asBinder())) { + return; + } + if (listener == null) { + mListener = null; + } else if (listener != null && mListener == null) { + mListener = listener; + } else { + // Fail fast here instead of silently overwriting the listener to another + // listener due to another connection connecting. + throw new IllegalStateException("ImsEcbmImplBase: Listener already set by " + + "another connection."); + } + } } @Override @@ -69,9 +87,13 @@ public class ImsEcbmImplBase { */ public final void enteredEcbm() { Log.d(TAG, "Entered ECBM."); - if (mListener != null) { + IImsEcbmListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.enteredECBM(); + listener.enteredECBM(); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -85,9 +107,13 @@ public class ImsEcbmImplBase { */ public final void exitedEcbm() { Log.d(TAG, "Exited ECBM."); - if (mListener != null) { + IImsEcbmListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.exitedECBM(); + listener.exitedECBM(); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java index d002903a11b6..555a47eb8200 100644 --- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -25,6 +25,7 @@ import com.android.ims.internal.IImsExternalCallStateListener; import com.android.ims.internal.IImsMultiEndpoint; import java.util.List; +import java.util.Objects; /** * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods @@ -41,10 +42,28 @@ public class ImsMultiEndpointImplBase { private static final String TAG = "MultiEndpointImplBase"; private IImsExternalCallStateListener mListener; - private IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() { + private final Object mLock = new Object(); + private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() { + @Override public void setListener(IImsExternalCallStateListener listener) throws RemoteException { - mListener = listener; + synchronized (mLock) { + if (mListener != null && listener != null && Objects.equals( + mListener.asBinder(), listener.asBinder())) { + return; + } + + if (listener == null) { + mListener = null; + } else if (listener != null && mListener == null) { + mListener = listener; + } else { + // Fail fast here instead of silently overwriting the listener to another + // listener due to another connection connecting. + throw new IllegalStateException("ImsMultiEndpointImplBase: Listener already" + + " set by another connection."); + } + } } @Override @@ -65,9 +84,13 @@ public class ImsMultiEndpointImplBase { */ public final void onImsExternalCallStateUpdate(List<ImsExternalCallState> externalCallDialogs) { Log.d(TAG, "ims external call state update triggered."); - if (mListener != null) { + IImsExternalCallStateListener listener; + synchronized (mLock) { + listener = mListener; + } + if (listener != null) { try { - mListener.onImsExternalCallStateUpdate(externalCallDialogs); + listener.onImsExternalCallStateUpdate(externalCallDialogs); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index f5219d5b49e8..eef4fcaceeaf 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -29,6 +29,7 @@ import com.android.ims.internal.IImsUtListener; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Base implementation of IMS UT interface, which implements stubs. Override these methods to @@ -116,7 +117,10 @@ public class ImsUtImplBase { */ public static final int INVALID_RESULT = -1; - private IImsUt.Stub mServiceImpl = new IImsUt.Stub() { + private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() { + private final Object mLock = new Object(); + private ImsUtListener mUtListener; + @Override public void close() throws RemoteException { ImsUtImplBase.this.close(); @@ -202,7 +206,26 @@ public class ImsUtImplBase { @Override public void setListener(IImsUtListener listener) throws RemoteException { - ImsUtImplBase.this.setListener(new ImsUtListener(listener)); + synchronized (mLock) { + if (mUtListener != null && listener != null && Objects.equals( + mUtListener.getListenerInterface().asBinder(), listener.asBinder())) { + return; + } + + if (listener == null) { + mUtListener = null; + } else if (listener != null && mUtListener == null) { + mUtListener = new ImsUtListener(listener); + } else { + // This is a limitation of the current API surface, there can only be one + // listener connected. Fail fast instead of silently overwriting the other + // listener. + throw new IllegalStateException("ImsUtImplBase#setListener: listener already " + + "set by another connected interface!"); + } + } + + ImsUtImplBase.this.setListener(mUtListener); } @Override diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index 3a0fb6edb2fb..7eba709a11da 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -20,20 +20,29 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.net.Uri; import android.telephony.ims.ImsException; -import android.telephony.ims.aidl.ICapabilityExchangeEventListener; +import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; import android.util.Log; import android.util.Pair; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.concurrent.Executor; /** - * Base class for different types of Capability exchange. + * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform + * using the vendor ImsService. + * <p> + * See RCC.07 for more details on UCE as well as how UCE should be implemented. * @hide */ +@SystemApi public class RcsCapabilityExchangeImplBase { private static final String LOG_TAG = "RcsCapExchangeImplBase"; @@ -70,13 +79,11 @@ public class RcsCapabilityExchangeImplBase { /** * Network connection is lost. - * @hide */ public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; /** * Requested feature/resource is not supported. - * @hide */ public static final int COMMAND_CODE_NOT_SUPPORTED = 7; @@ -117,7 +124,8 @@ public class RcsCapabilityExchangeImplBase { */ public interface PublishResponseCallback { /** - * Notify the framework that the command associated with this callback has failed. + * Notify the framework that the command associated with the + * {@link #publishCapabilities(String, PublishResponseCallback)} has failed. * * @param code The reason why the associated command has failed. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is @@ -128,27 +136,29 @@ public class RcsCapabilityExchangeImplBase { */ void onCommandError(@CommandCode int code) throws ImsException; - /** * Provide the framework with a subsequent network response update to * {@link #publishCapabilities(String, PublishResponseCallback)}. * - * @param code The SIP response code sent from the network for the operation + * @param sipCode The SIP response code sent from the network for the operation * token specified. - * @param reason The optional reason response from the network. If the network - * provided no reason with the code, the string should be empty. + * @param reason The optional reason response from the network. If there is a reason header + * included in the response, that should take precedence over the reason provided in the + * status line. If the network provided no reason with the sip code, the string should be + * empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the {@link RcsFeature} * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases * when the Telephony stack has crashed. */ - void onNetworkResponse(@IntRange(from = 100, to = 699) int code, + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reason) throws ImsException; } /** * Interface used by the framework to respond to OPTIONS requests. + * @hide */ public interface OptionsResponseCallback { /** @@ -165,20 +175,20 @@ public class RcsCapabilityExchangeImplBase { /** * Send the response of a SIP OPTIONS capability exchange to the framework. - * @param code The SIP response code that was sent by the network in response + * @param sipCode The SIP response code that was sent by the network in response * to the request sent by {@link #sendOptionsCapabilityRequest}. * @param reason The optional SIP response reason sent by the network. * If none was sent, this should be an empty string. * @param theirCaps the contact's UCE capabilities associated with the * capability request. - * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not * currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} has not received the * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare * cases when the Telephony stack has crashed. */ - void onNetworkResponse(int code, @NonNull String reason, + void onNetworkResponse(int sipCode, @NonNull String reason, @Nullable List<String> theirCaps) throws ImsException; } @@ -188,6 +198,10 @@ public class RcsCapabilityExchangeImplBase { public interface SubscribeResponseCallback { /** * Notify the framework that the command associated with this callback has failed. + * <p> + * Must only be called when there was an error generating a SUBSCRIBE request due to an + * IMS stack error. This is a terminating event, so no other callback event will be + * expected after this callback. * * @param code The reason why the associated command has failed. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is @@ -202,89 +216,125 @@ public class RcsCapabilityExchangeImplBase { /** * Notify the framework of the response to the SUBSCRIBE request from * {@link #subscribeForCapabilities(List<Uri>, SubscribeResponseCallback)}. + * <p> + * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the + * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, + * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the + * subsequent NOTIFY responses to the subscription. * - * @param code The SIP response code sent from the network for the operation + * @param sipCode The SIP response code sent from the network for the operation * token specified. * @param reason The optional reason response from the network. If the network - * provided no reason with the code, the string should be empty. + * provided no reason with the sip code, the string should be empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback. * This may also happen in rare cases when the Telephony stack has crashed. */ - void onNetworkResponse(@IntRange(from = 100, to = 699) int code, + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reason) throws ImsException; /** - * Provides the framework with latest XML PIDF documents included in the - * network response for the requested contacts' capabilities requested by the - * Framework using {@link #requestCapabilities(List, int)}. This should be - * called every time a new NOTIFY event is received with new capability - * information. + * Notify the framework of the latest XML PIDF documents included in the network response + * for the requested contacts' capabilities requested by the Framework using + * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}. + * <p> + * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a + * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY + * responses that contain RLMI information and potentially multiple PIDF XMLs, each + * PIDF XML should be separated and added as a separate item in the List. This should be + * called every time a new NOTIFY event is received with new capability information. * + * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed + * for. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is - * not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received - * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in - * rare cases when the - * Telephony stack has crashed. + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. */ void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException; /** - * A resource in the resource list for the presence subscribe event has been terminated. + * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response + * for the ongoing SUBSCRIBE dialog has been terminated. * <p> - * This allows the framework to know that there will not be any capability information for - * a specific contact URI that they subscribed for. + * This will be used to notify the framework that a contact URI that the IMS stack has + * subscribed to on the Resource List Server has been terminated as well as the reason why. + * Usually this means that there will not be any capability information for the contact URI + * that they subscribed for. See RFC 4662 for more information. + * + * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the + * list is the contact URI and its terminated reason. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. */ void onResourceTerminated( @NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException; /** - * The subscription associated with a previous #requestCapabilities operation - * has been terminated. This will mostly be due to the subscription expiring, - * but may also happen due to an error. - * <p> - * This allows the framework to know that there will no longer be any - * capability updates for the requested operationToken. + * The subscription associated with a previous + * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)} + * operation has been terminated. This will mostly be due to the network sending a final + * NOTIFY response due to the subscription expiring, but this may also happen due to a + * network error. + * + * @param reason The reason for the request being unable to process. + * @param retryAfterMilliseconds The time in milliseconds the requesting application should + * wait before retrying, if non-zero. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. */ - void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException; + void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException; } - - private ICapabilityExchangeEventListener mListener; + private final Executor mBinderExecutor; /** - * Set the event listener to send the request to Framework. + * Create a new RcsCapabilityExchangeImplBase instance. + * + * @param executor The executor that remote calls from the framework will be called on. */ - public void setEventListener(ICapabilityExchangeEventListener listener) { - mListener = listener; - } - - /** - * Get the event listener. - */ - public ICapabilityExchangeEventListener getEventListener() { - return mListener; + public RcsCapabilityExchangeImplBase(@NonNull Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } + mBinderExecutor = executor; } /** * The user capabilities of one or multiple contacts have been requested by the framework. * <p> + * The implementer must follow up this call with an + * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed. * The response from the network to the SUBSCRIBE request must be sent back to the framework - * using {@link #onSubscribeNetworkResponse(int, String, int)}. As NOTIFY requests come in from - * the network, the requested contact’s capabilities should be sent back to the framework using - * {@link #onSubscribeNotifyRequest} and {@link onSubscribeResourceTerminated} + * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}. + * As NOTIFY requests come in from the network, the requested contact’s capabilities should be + * sent back to the framework using + * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and + * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)} * should be called with the presence information for the contacts specified. * <p> - * Once the subscription is terminated, {@link #onSubscriptionTerminated} must be called for - * the framework to finish listening for NOTIFY responses. + * Once the subscription is terminated, + * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the + * framework to finish listening for NOTIFY responses. + * * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE * capabilities for. * @param cb The callback of the subscribe request. */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") public void subscribeForCapabilities(@NonNull List<Uri> uris, @NonNull SubscribeResponseCallback cb) { // Stub - to be implemented by service @@ -300,11 +350,13 @@ public class RcsCapabilityExchangeImplBase { * The capabilities of this device have been updated and should be published to the network. * <p> * If this operation succeeds, network response updates should be sent to the framework using - * {@link #onNetworkResponse(int, String)}. + * {@link PublishResponseCallback#onNetworkResponse(int, String)}. * @param pidfXml The XML PIDF document containing the capabilities of this device to be sent * to the carrier’s presence server. * @param cb The callback of the publish request */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) { // Stub - to be implemented by service Log.w(LOG_TAG, "publishCapabilities called with no implementation."); @@ -324,7 +376,10 @@ public class RcsCapabilityExchangeImplBase { * @param contactUri The URI of the remote user that we wish to get the capabilities of. * @param myCapabilities The capabilities of this device to send to the remote user. * @param callback The callback of this request which is sent from the remote user. + * @hide */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") public void sendOptionsCapabilityRequest(@NonNull Uri contactUri, @NonNull List<String> myCapabilities, @NonNull OptionsResponseCallback callback) { // Stub - to be implemented by service diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index c60a44c3803a..77d46f4c39cd 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -49,6 +49,7 @@ import android.telephony.RadioAccessFamily; import android.telephony.RadioAccessSpecifier; import android.telephony.ServiceState; import android.telephony.SignalStrength; +import android.telephony.SignalStrengthUpdateRequest; import android.telephony.TelephonyHistogram; import android.telephony.VisualVoicemailSmsFilterSettings; import android.telephony.emergency.EmergencyNumber; @@ -629,7 +630,7 @@ interface ITelephony { * successful iccOpenLogicalChannel. * @return true if the channel was closed successfully. */ - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) boolean iccCloseLogicalChannel(int subId, int channel); /** @@ -671,7 +672,7 @@ interface ITelephony { * @return The APDU response from the ICC card with the status appended at * the end. */ - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) String iccTransmitApduLogicalChannel(int subId, int channel, int cla, int instruction, int p1, int p2, int p3, String data); @@ -2279,6 +2280,14 @@ interface ITelephony { CarrierBandwidth getCarrierBandwidth(int subId); /** + * Checks whether the device supports the given capability on the radio interface. + * + * @param capability the name of the capability + * @return the availability of the capability + */ + boolean isRadioInterfaceCapabilitySupported(String capability); + + /** * Thermal mitigation request to control functionalities at modem. * * @param subId the id of the subscription @@ -2358,6 +2367,11 @@ interface ITelephony { boolean setCarrierSingleRegistrationEnabledOverride(int subId, String enabled); /** + * Sends a device to device message; only for use through shell. + */ + void sendDeviceToDeviceMessage(int message, int value); + + /** * Gets the config of RCS VoLTE single registration enabled for the carrier/subscription. */ boolean getCarrierSingleRegistrationEnabled(int subId); @@ -2367,4 +2381,37 @@ interface ITelephony { * their mobile plan. */ String getMobileProvisioningUrl(); + + /* + * Remove the EAB contacts from the EAB database. + */ + int removeContactFromEab(int subId, String contacts); + + /** + * Get the EAB contact from the EAB database. + */ + String getContactFromEab(String contact); + + /* + * Check whether the device supports RCS User Capability Exchange or not. + */ + boolean getDeviceUceEnabled(); + + /* + * Set the device supports RCS User Capability Exchange. + */ + void setDeviceUceEnabled(boolean isEnabled); + + /** + * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the + * specified thresholds. + */ + void setSignalStrengthUpdateRequest(int subId, in SignalStrengthUpdateRequest request, + String callingPackage); + + /** + * Clear a SignalStrengthUpdateRequest from system. + */ + void clearSignalStrengthUpdateRequest(int subId, in SignalStrengthUpdateRequest request, + String callingPackage); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 52f263fad695..76243a5799c3 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -520,6 +520,7 @@ public interface RILConstants { int RIL_REQUEST_START_HANDOVER = 217; int RIL_REQUEST_CANCEL_HANDOVER = 218; int RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS = 219; + int RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES = 220; int RIL_REQUEST_SET_DATA_THROTTLING = 221; int RIL_REQUEST_SET_ALLOWED_NETWORK_TYPE_BITMAP = 222; int RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP = 223; diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt index 1110790c373f..d1a68d4e9cb2 100644 --- a/test-mock/api/current.txt +++ b/test-mock/api/current.txt @@ -117,6 +117,7 @@ package android.test.mock { method public void sendOrderedBroadcast(android.content.Intent, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); method public void sendStickyBroadcast(android.content.Intent); + method public void sendStickyBroadcast(android.content.Intent, android.os.Bundle); method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); method public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle); diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index cf3b03cae72e..6046d78240b6 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -493,6 +493,11 @@ public class MockContext extends Context { } @Override + public void sendStickyBroadcast(Intent intent, Bundle options) { + throw new UnsupportedOperationException(); + } + + @Override public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java index 2df0024bdea9..56db4f98e160 100644 --- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java +++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java @@ -35,7 +35,7 @@ import java.security.MessageDigest; import java.util.Random; import java.util.concurrent.TimeUnit; -public class DummyBlobData { +public class FakeBlobData { private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L; private final Random mRandom; @@ -47,7 +47,7 @@ public class DummyBlobData { byte[] mFileDigest; long mExpiryTimeMs; - private DummyBlobData(Builder builder) { + private FakeBlobData(Builder builder) { mRandom = new Random(builder.getRandomSeed()); mFile = new File(builder.getContext().getFilesDir(), builder.getFileName()); mFileSize = builder.getFileSize(); @@ -116,8 +116,8 @@ public class DummyBlobData { return mExpiryDurationMs; } - public DummyBlobData build() { - return new DummyBlobData(this); + public FakeBlobData build() { + return new FakeBlobData(this); } } diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt index e9227e94da98..eb04f6907748 100644 --- a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt +++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt @@ -131,6 +131,10 @@ class PlatformCompatCommandNotInstalledTest { assertThat(platformCompat.isChangeEnabled(TEST_CHANGE_ID, appInfo)).isEqualTo(params.result) } - private fun command(command: String) = - FileReader(uiAutomation.executeShellCommand(command).fileDescriptor).readText() + private fun command(command: String): String { + val fileDescriptor = uiAutomation.executeShellCommand(command) + return String(ParcelFileDescriptor.AutoCloseInputStream(fileDescriptor).use { + inputStream -> inputStream.readBytes() + }) + } } diff --git a/tests/StagedInstallTest/OWNERS b/tests/StagedInstallTest/OWNERS index d825dfd7cf00..aac68e994a39 100644 --- a/tests/StagedInstallTest/OWNERS +++ b/tests/StagedInstallTest/OWNERS @@ -1 +1,5 @@ include /services/core/java/com/android/server/pm/OWNERS + +dariofreni@google.com +ioffe@google.com +olilan@google.com diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java index ef973acf763b..861d221238ff 100644 --- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java +++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java @@ -182,6 +182,24 @@ public class UsbHandlerTest { @SmallTest @Test + public void setFunctionsNcmAndRndis() { + final long rndisPlusNcm = UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM; + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_NCM)); + assertEquals(UsbManager.FUNCTION_NCM, mUsbHandler.getEnabledFunctions() & rndisPlusNcm); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + rndisPlusNcm)); + assertEquals(rndisPlusNcm, mUsbHandler.getEnabledFunctions() & rndisPlusNcm); + + mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS, + UsbManager.FUNCTION_NCM)); + assertEquals(UsbManager.FUNCTION_NCM, mUsbHandler.getEnabledFunctions() & rndisPlusNcm); + } + + @SmallTest + @Test public void enableAdb() { sendBootCompleteMessages(mUsbHandler); Message msg = mUsbHandler.obtainMessage(MSG_ENABLE_ADB); diff --git a/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java b/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java index a0fd9d40506b..b8bd98ea3f21 100644 --- a/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java +++ b/tests/UsbTests/src/com/android/server/usb/UsbManagerNoPermTest.java @@ -16,6 +16,8 @@ package com.android.server.usb; +import static org.junit.Assert.assertEquals; + import android.content.Context; import android.hardware.usb.UsbManager; @@ -23,12 +25,12 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.usblib.UsbManagerTestLib; + import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import com.android.server.usblib.UsbManagerTestLib; - /** * Unit tests for {@link android.hardware.usb.UsbManager}. * Note: NOT claimed MANAGE_USB permission in Manifest @@ -78,4 +80,35 @@ public class UsbManagerNoPermTest { public void testUsbApi_SetCurrentFunctions_OnSecurityException() throws Exception { mUsbManagerTestLib.testSetCurrentFunctionsEx(UsbManager.FUNCTION_NONE); } + + public void assertSettableFunctions(boolean settable, long functions) { + assertEquals( + "areSettableFunctions(" + UsbManager.usbFunctionsToString(functions) + "):", + settable, UsbManager.areSettableFunctions(functions)); + } + + /** + * Tests the behaviour of the static areSettableFunctions method. This method performs no IPCs + * and requires no permissions. + */ + @Test + public void testUsbManager_AreSettableFunctions() { + // NONE is settable. + assertSettableFunctions(true, UsbManager.FUNCTION_NONE); + + // MTP, PTP, RNDIS, MIDI, NCM are all settable by themselves. + assertSettableFunctions(true, UsbManager.FUNCTION_MTP); + assertSettableFunctions(true, UsbManager.FUNCTION_PTP); + assertSettableFunctions(true, UsbManager.FUNCTION_RNDIS); + assertSettableFunctions(true, UsbManager.FUNCTION_MIDI); + assertSettableFunctions(true, UsbManager.FUNCTION_NCM); + + // Setting two functions at the same time is not allowed... + assertSettableFunctions(false, UsbManager.FUNCTION_MTP | UsbManager.FUNCTION_PTP); + assertSettableFunctions(false, UsbManager.FUNCTION_PTP | UsbManager.FUNCTION_RNDIS); + assertSettableFunctions(false, UsbManager.FUNCTION_MIDI | UsbManager.FUNCTION_NCM); + + // ... except in the special case of RNDIS and NCM. + assertSettableFunctions(true, UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM); + } } diff --git a/tests/net/Android.bp b/tests/net/Android.bp index a7622198cec7..e630da04a512 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -53,6 +53,7 @@ android_test { jarjar_rules: "jarjar-rules.txt", static_libs: [ "androidx.test.rules", + "bouncycastle-repackaged-unbundled", "FrameworksNetCommonTests", "frameworks-base-testutils", "frameworks-net-integration-testutils", @@ -70,4 +71,7 @@ android_test { "android.test.base", "android.test.mock", ], + jni_libs: [ + "libservice-connectivity", + ], } diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp index 373aac604b2a..c271f49ee537 100644 --- a/tests/net/common/Android.bp +++ b/tests/net/common/Android.bp @@ -24,6 +24,7 @@ java_library { "androidx.test.rules", "junit", "mockito-target-minus-junit4", + "modules-utils-build", "net-tests-utils", "net-utils-framework-common", "platform-test-annotations", diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt index bd1847b7c440..b2bcfeb9019d 100644 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt @@ -18,12 +18,15 @@ package android.net import android.os.Build import androidx.test.filters.SmallTest +import com.android.modules.utils.build.SdkLevel import com.android.testutils.assertParcelSane import com.android.testutils.assertParcelingIsLossless +import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.DevSdkIgnoreRunner import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import kotlin.test.assertEquals @@ -33,6 +36,9 @@ import kotlin.test.assertNotEquals @RunWith(DevSdkIgnoreRunner::class) @IgnoreUpTo(Build.VERSION_CODES.Q) class CaptivePortalDataTest { + @Rule @JvmField + val ignoreRule = DevSdkIgnoreRule() + private val data = CaptivePortalData.Builder() .setRefreshTime(123L) .setUserPortalUrl(Uri.parse("https://portal.example.com/test")) @@ -41,13 +47,19 @@ class CaptivePortalDataTest { .setBytesRemaining(456L) .setExpiryTime(789L) .setCaptive(true) + .apply { + if (SdkLevel.isAtLeastS()) { + setVenueFriendlyName("venue friendly name") + } + } .build() private fun makeBuilder() = CaptivePortalData.Builder(data) @Test fun testParcelUnparcel() { - assertParcelSane(data, fieldCount = 7) + val fieldCount = if (SdkLevel.isAtLeastS()) 8 else 7 + assertParcelSane(data, fieldCount) assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) @@ -66,6 +78,11 @@ class CaptivePortalDataTest { assertNotEqualsAfterChange { it.setBytesRemaining(789L) } assertNotEqualsAfterChange { it.setExpiryTime(12L) } assertNotEqualsAfterChange { it.setCaptive(false) } + + if (SdkLevel.isAtLeastS()) { + assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } + assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } + } } @Test @@ -108,6 +125,11 @@ class CaptivePortalDataTest { assertFalse(makeBuilder().setCaptive(false).build().isCaptive) } + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + fun testVenueFriendlyName() { + assertEquals("venue friendly name", data.venueFriendlyName) + } + private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) = CaptivePortalData.Builder(this).apply { mutator(this) }.build() diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 6b7ea66df233..5d0e016d50fa 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -42,9 +42,11 @@ import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES; +import static android.os.Process.INVALID_UID; import static com.android.testutils.ParcelUtils.assertParcelSane; import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; +import static com.android.testutils.ParcelUtils.parcelingRoundTrip; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -53,18 +55,19 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; +import android.net.wifi.WifiInfo; import android.net.wifi.aware.DiscoverySession; import android.net.wifi.aware.PeerHandle; import android.net.wifi.aware.WifiAwareNetworkSpecifier; import android.os.Build; -import android.os.Process; import android.test.suitebuilder.annotation.SmallTest; import android.util.ArraySet; -import androidx.core.os.BuildCompat; import androidx.test.runner.AndroidJUnit4; +import com.android.modules.utils.build.SdkLevel; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; @@ -89,10 +92,11 @@ public class NetworkCapabilitiesTest { private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class); private boolean isAtLeastR() { - // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R. - // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after - // releasing Android R. - return BuildCompat.isAtLeastR() || Build.VERSION.SDK_INT > Build.VERSION_CODES.Q; + return SdkLevel.isAtLeastR(); + } + + private boolean isAtLeastS() { + return SdkLevel.isAtLeastS(); } @Test @@ -324,8 +328,59 @@ public class NetworkCapabilitiesTest { testParcelSane(netCap); } + private NetworkCapabilities createNetworkCapabilitiesWithWifiInfo() { + // uses a real WifiInfo to test parceling of sensitive data. + final WifiInfo wifiInfo = new WifiInfo.Builder() + .setSsid("sssid1234".getBytes()) + .setBssid("00:11:22:33:44:55") + .build(); + return new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_NOT_METERED) + .setSSID(TEST_SSID) + .setTransportInfo(wifiInfo) + .setRequestorPackageName("com.android.test") + .setRequestorUid(9304); + } + + @Test + public void testParcelNetworkCapabilitiesWithLocationSensitiveFields() { + assumeTrue(isAtLeastS()); + + final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo(); + final NetworkCapabilities netCapWithLocationSensitiveFields = + new NetworkCapabilities(netCap, true); + + assertParcelingIsLossless(netCapWithLocationSensitiveFields); + testParcelSane(netCapWithLocationSensitiveFields); + + assertEquals(netCapWithLocationSensitiveFields, + parcelingRoundTrip(netCapWithLocationSensitiveFields)); + } + + @Test + public void testParcelNetworkCapabilitiesWithoutLocationSensitiveFields() { + assumeTrue(isAtLeastS()); + + final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo(); + final NetworkCapabilities netCapWithoutLocationSensitiveFields = + new NetworkCapabilities(netCap, false); + + final NetworkCapabilities sanitizedNetCap = + new NetworkCapabilities(netCapWithoutLocationSensitiveFields); + final WifiInfo sanitizedWifiInfo = new WifiInfo.Builder() + .setSsid(new byte[0]) + .setBssid(WifiInfo.DEFAULT_MAC_ADDRESS) + .build(); + sanitizedNetCap.setTransportInfo(sanitizedWifiInfo); + assertEquals(sanitizedNetCap, parcelingRoundTrip(netCapWithoutLocationSensitiveFields)); + } + private void testParcelSane(NetworkCapabilities cap) { - if (isAtLeastR()) { + if (isAtLeastS()) { + assertParcelSane(cap, 16); + } else if (isAtLeastR()) { assertParcelSane(cap, 15); } else { assertParcelSane(cap, 11); @@ -639,26 +694,23 @@ public class NetworkCapabilitiesTest { // Sequence 1: Transport + Transport + TransportInfo NetworkCapabilities nc1 = new NetworkCapabilities(); nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI) - .setTransportInfo(new TransportInfo() {}); + .setTransportInfo(new TestTransportInfo()); // Sequence 2: Transport + NetworkSpecifier + Transport NetworkCapabilities nc2 = new NetworkCapabilities(); - nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TransportInfo() {}) + nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TestTransportInfo()) .addTransportType(TRANSPORT_WIFI); } @Test public void testCombineTransportInfo() { NetworkCapabilities nc1 = new NetworkCapabilities(); - nc1.setTransportInfo(new TransportInfo() { - // empty - }); + nc1.setTransportInfo(new TestTransportInfo()); + NetworkCapabilities nc2 = new NetworkCapabilities(); // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where // combine fails) - nc2.setTransportInfo(new TransportInfo() { - // empty - }); + nc2.setTransportInfo(new TestTransportInfo()); try { nc1.combineCapabilities(nc2); @@ -761,7 +813,7 @@ public class NetworkCapabilitiesTest { // Test default owner uid. // If the owner uid is not set, the default value should be Process.INVALID_UID. final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build(); - assertEquals(Process.INVALID_UID, nc1.getOwnerUid()); + assertEquals(INVALID_UID, nc1.getOwnerUid()); // Test setAdministratorUids and getAdministratorUids. final int[] administratorUids = {1001, 10001}; final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() @@ -906,6 +958,16 @@ public class NetworkCapabilitiesTest { private class TestTransportInfo implements TransportInfo { TestTransportInfo() { } + + @Override + public TransportInfo makeCopy(boolean parcelLocationSensitiveFields) { + return this; + } + + @Override + public boolean hasLocationSensitiveFields() { + return false; + } } @Test @IgnoreUpTo(Build.VERSION_CODES.Q) diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt new file mode 100644 index 000000000000..87cfb345e5e0 --- /dev/null +++ b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt @@ -0,0 +1,50 @@ +/* + * 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 android.net + +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.assertParcelSane +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals + +private const val TEST_OWNER_UID = 123 +private const val TEST_IFACE = "test_tun0" +private val TEST_IFACE_LIST = listOf("wlan0", "rmnet_data0", "eth0") + +@SmallTest +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) +class UnderlyingNetworkInfoTest { + @Test + fun testParcelUnparcel() { + val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST) + assertEquals(TEST_OWNER_UID, testInfo.ownerUid) + assertEquals(TEST_IFACE, testInfo.iface) + assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces) + assertParcelSane(testInfo, 3) + + val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf()) + assertEquals(0, emptyInfo.ownerUid) + assertEquals(String(), emptyInfo.iface) + assertEquals(listOf(), emptyInfo.underlyingIfaces) + assertParcelSane(emptyInfo, 3) + } +}
\ No newline at end of file diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index 8e1875168a84..083c8c8741da 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -38,6 +38,7 @@ import android.net.metrics.IpConnectivityLog import android.os.ConditionVariable import android.os.IBinder import android.os.INetworkManagementService +import android.os.UserHandle import android.testing.TestableContext import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -46,8 +47,6 @@ import com.android.server.ConnectivityService import com.android.server.LocalServices import com.android.server.NetworkAgentWrapper import com.android.server.TestNetIdManager -import com.android.server.connectivity.DefaultNetworkMetrics -import com.android.server.connectivity.IpConnectivityMetrics import com.android.server.connectivity.MockableSystemProperties import com.android.server.connectivity.ProxyTracker import com.android.server.net.NetworkPolicyManagerInternal @@ -57,10 +56,13 @@ import org.junit.Before import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith +import org.mockito.AdditionalAnswers import org.mockito.Mock import org.mockito.Mockito.any +import org.mockito.Mockito.anyInt import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn +import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.spy import org.mockito.MockitoAnnotations @@ -92,10 +94,6 @@ class ConnectivityServiceIntegrationTest { private lateinit var netd: INetd @Mock private lateinit var dnsResolver: IDnsResolver - @Mock - private lateinit var metricsLogger: IpConnectivityMetrics.Logger - @Mock - private lateinit var defaultMetrics: DefaultNetworkMetrics @Spy private var context = TestableContext(realContext) @@ -149,8 +147,10 @@ class ConnectivityServiceIntegrationTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) - doReturn(defaultMetrics).`when`(metricsLogger).defaultNetworkMetrics() - doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any()) + val asUserCtx = mock(Context::class.java, AdditionalAnswers.delegatesTo<Context>(context)) + doReturn(UserHandle.ALL).`when`(asUserCtx).user + doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt()) + doNothing().`when`(context).sendStickyBroadcast(any(), any()) networkStackClient = TestNetworkStackClient(realContext) networkStackClient.init() @@ -173,7 +173,6 @@ class ConnectivityServiceIntegrationTest { private fun makeDependencies(): ConnectivityService.Dependencies { val deps = spy(ConnectivityService.Dependencies()) doReturn(networkStackClient).`when`(deps).networkStack - doReturn(metricsLogger).`when`(deps).metricsLogger doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any()) doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager() diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index 3d4dc4d67dcc..dc9e587332cb 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -31,6 +31,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import android.annotation.NonNull; import android.content.Context; import android.net.ConnectivityManager; import android.net.LinkProperties; @@ -40,6 +41,7 @@ import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkProvider; import android.net.NetworkSpecifier; +import android.net.QosFilter; import android.net.SocketKeepalive; import android.net.UidRange; import android.os.ConditionVariable; @@ -47,10 +49,12 @@ import android.os.HandlerThread; import android.os.Message; import android.util.Log; +import com.android.net.module.util.ArrayTrackRecord; import com.android.server.connectivity.ConnectivityConstants; import com.android.testutils.HandlerUtils; import com.android.testutils.TestableNetworkCallback; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -71,6 +75,8 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { // start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem. private long mKeepaliveResponseDelay = 0L; private Integer mExpectedKeepaliveSlot = null; + private final ArrayTrackRecord<CallbackType>.ReadHead mCallbackHistory = + new ArrayTrackRecord<CallbackType>().newReadHead(); public NetworkAgentWrapper(int transport, LinkProperties linkProperties, NetworkCapabilities ncTemplate, Context context) throws Exception { @@ -157,6 +163,20 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { } @Override + public void onQosCallbackRegistered(final int qosCallbackId, + final @NonNull QosFilter filter) { + Log.i(mWrapper.mLogTag, "onQosCallbackRegistered"); + mWrapper.mCallbackHistory.add( + new CallbackType.OnQosCallbackRegister(qosCallbackId, filter)); + } + + @Override + public void onQosCallbackUnregistered(final int qosCallbackId) { + Log.i(mWrapper.mLogTag, "onQosCallbackUnregistered"); + mWrapper.mCallbackHistory.add(new CallbackType.OnQosCallbackUnregister(qosCallbackId)); + } + + @Override protected void preventAutomaticReconnect() { mWrapper.mPreventReconnectReceived.open(); } @@ -279,7 +299,60 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { return mNetworkCapabilities; } + public @NonNull ArrayTrackRecord<CallbackType>.ReadHead getCallbackHistory() { + return mCallbackHistory; + } + public void waitForIdle(long timeoutMs) { HandlerUtils.waitForIdle(mHandlerThread, timeoutMs); } + + abstract static class CallbackType { + final int mQosCallbackId; + + protected CallbackType(final int qosCallbackId) { + mQosCallbackId = qosCallbackId; + } + + static class OnQosCallbackRegister extends CallbackType { + final QosFilter mFilter; + OnQosCallbackRegister(final int qosCallbackId, final QosFilter filter) { + super(qosCallbackId); + mFilter = filter; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final OnQosCallbackRegister that = (OnQosCallbackRegister) o; + return mQosCallbackId == that.mQosCallbackId + && Objects.equals(mFilter, that.mFilter); + } + + @Override + public int hashCode() { + return Objects.hash(mQosCallbackId, mFilter); + } + } + + static class OnQosCallbackUnregister extends CallbackType { + OnQosCallbackUnregister(final int qosCallbackId) { + super(qosCallbackId); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final OnQosCallbackUnregister that = (OnQosCallbackUnregister) o; + return mQosCallbackId == that.mQosCallbackId; + } + + @Override + public int hashCode() { + return Objects.hash(mQosCallbackId); + } + } + } } diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index d74a621842f9..c2fddf3d9e82 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -16,6 +16,7 @@ package android.net; +import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; @@ -31,16 +32,22 @@ import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; +import static android.net.NetworkRequest.Type.REQUEST; +import static android.net.NetworkRequest.Type.TRACK_DEFAULT; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -49,9 +56,7 @@ import static org.mockito.Mockito.when; import android.app.PendingIntent; import android.content.Context; import android.content.pm.ApplicationInfo; -import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; -import android.net.NetworkCapabilities; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; @@ -213,9 +218,8 @@ public class ConnectivityManagerTest { ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork( - any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class))) - .thenReturn(request); + when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), + any(), nullable(String.class))).thenReturn(request); manager.requestNetwork(request, callback, handler); // callback triggers @@ -242,9 +246,8 @@ public class ConnectivityManagerTest { ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork( - any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class))) - .thenReturn(req1); + when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), + any(), nullable(String.class))).thenReturn(req1); manager.requestNetwork(req1, callback, handler); // callback triggers @@ -261,9 +264,8 @@ public class ConnectivityManagerTest { verify(callback, timeout(100).times(0)).onLosing(any(), anyInt()); // callback can be registered again - when(mService.requestNetwork( - any(), captor.capture(), anyInt(), any(), anyInt(), any(), nullable(String.class))) - .thenReturn(req2); + when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), + any(), nullable(String.class))).thenReturn(req2); manager.requestNetwork(req2, callback, handler); // callback triggers @@ -286,7 +288,7 @@ public class ConnectivityManagerTest { info.targetSdkVersion = VERSION_CODES.N_MR1 + 1; when(mCtx.getApplicationInfo()).thenReturn(info); - when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any(), + when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(), nullable(String.class))).thenReturn(request); Handler handler = new Handler(Looper.getMainLooper()); @@ -340,6 +342,41 @@ public class ConnectivityManagerTest { } } + @Test + public void testRequestType() throws Exception { + final String testPkgName = "MyPackage"; + final ConnectivityManager manager = new ConnectivityManager(mCtx, mService); + when(mCtx.getOpPackageName()).thenReturn(testPkgName); + final NetworkRequest request = makeRequest(1); + final NetworkCallback callback = new ConnectivityManager.NetworkCallback(); + + manager.requestNetwork(request, callback); + verify(mService).requestNetwork(eq(request.networkCapabilities), + eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(testPkgName), eq(null)); + reset(mService); + + // Verify that register network callback does not calls requestNetwork at all. + manager.registerNetworkCallback(request, callback); + verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), + anyInt(), any(), any()); + verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), + eq(testPkgName)); + reset(mService); + + manager.registerDefaultNetworkCallback(callback); + verify(mService).requestNetwork(eq(null), + eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(testPkgName), eq(null)); + reset(mService); + + manager.requestBackgroundNetwork(request, null, callback); + verify(mService).requestNetwork(eq(request.networkCapabilities), + eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(testPkgName), eq(null)); + reset(mService); + } + static Message makeMessage(NetworkRequest req, int messageType) { Bundle bundle = new Bundle(); bundle.putParcelable(NetworkRequest.class.getSimpleName(), req); diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java index 076e41d33a8d..1abd39a32bdf 100644 --- a/tests/net/java/android/net/Ikev2VpnProfileTest.java +++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java @@ -30,7 +30,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.net.VpnProfile; import com.android.net.module.util.ProxyUtils; -import com.android.org.bouncycastle.x509.X509V1CertificateGenerator; +import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator; import org.junit.Before; import org.junit.Test; diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt index 9ba56e44fe88..91fcbc0fd5d7 100644 --- a/tests/net/java/android/net/NetworkTemplateTest.kt +++ b/tests/net/java/android/net/NetworkTemplateTest.kt @@ -67,6 +67,7 @@ class NetworkTemplateTest { val caps = NetworkCapabilities().apply { setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) + setSSID(ssid) } return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid) } diff --git a/tests/net/java/android/net/QosSocketFilterTest.java b/tests/net/java/android/net/QosSocketFilterTest.java new file mode 100644 index 000000000000..ad58960eaadd --- /dev/null +++ b/tests/net/java/android/net/QosSocketFilterTest.java @@ -0,0 +1,75 @@ +/* + * 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.net; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +@RunWith(AndroidJUnit4.class) +@androidx.test.filters.SmallTest +public class QosSocketFilterTest { + + @Test + public void testPortExactMatch() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); + assertTrue(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 10), addressB, 10, 10)); + + } + + @Test + public void testPortLessThanStart() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); + assertFalse(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 8), addressB, 10, 10)); + } + + @Test + public void testPortGreaterThanEnd() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); + assertFalse(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 18), addressB, 10, 10)); + } + + @Test + public void testPortBetweenStartAndEnd() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4"); + assertTrue(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 10), addressB, 8, 18)); + } + + @Test + public void testAddressesDontMatch() { + final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4"); + final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5"); + assertFalse(QosSocketFilter.matchesLocalAddress( + new InetSocketAddress(addressA, 10), addressB, 10, 10)); + } +} + diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 9421acde7b97..4c658e1be1e1 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; +import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; @@ -63,6 +64,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS; @@ -161,12 +163,12 @@ import android.net.DataStallReportParcelable; import android.net.EthernetManager; import android.net.IConnectivityDiagnosticsCallback; import android.net.IDnsResolver; -import android.net.IIpConnectivityMetrics; import android.net.INetd; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; import android.net.INetworkStatsService; +import android.net.IQosCallback; import android.net.InetAddresses; import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; @@ -190,18 +192,23 @@ import android.net.NetworkStackClient; import android.net.NetworkState; import android.net.NetworkTestResultParcelable; import android.net.ProxyInfo; +import android.net.QosCallbackException; +import android.net.QosFilter; +import android.net.QosSession; import android.net.ResolverParamsParcel; import android.net.RouteInfo; import android.net.RouteInfoParcel; import android.net.SocketKeepalive; import android.net.UidRange; import android.net.UidRangeParcel; +import android.net.UnderlyingNetworkInfo; import android.net.Uri; import android.net.VpnManager; import android.net.metrics.IpConnectivityLog; import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; +import android.net.wifi.WifiInfo; import android.os.BadParcelableException; import android.os.Binder; import android.os.Build; @@ -217,17 +224,21 @@ import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.security.Credentials; import android.security.KeyStore; import android.system.Os; import android.telephony.TelephonyManager; +import android.telephony.data.EpsBearerQosSessionAttributes; import android.test.mock.MockContentResolver; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import android.util.SparseArray; import androidx.test.InstrumentationRegistry; @@ -236,20 +247,19 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IBatteryStats; import com.android.internal.net.VpnConfig; -import com.android.internal.net.VpnInfo; +import com.android.internal.net.VpnProfile; import com.android.internal.util.ArrayUtils; import com.android.internal.util.WakeupMessage; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo; import com.android.server.connectivity.ConnectivityConstants; -import com.android.server.connectivity.DefaultNetworkMetrics; -import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.Nat464Xlat; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkNotificationManager.NotificationType; import com.android.server.connectivity.ProxyTracker; +import com.android.server.connectivity.QosCallbackTracker; import com.android.server.connectivity.Vpn; import com.android.server.net.NetworkPinner; import com.android.server.net.NetworkPolicyManagerInternal; @@ -281,6 +291,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -290,13 +301,16 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -344,6 +358,11 @@ public class ConnectivityServiceTest { private static final String INTERFACE_NAME = "interface"; + private static final String TEST_VENUE_URL_NA = "https://android.com/"; + private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/"; + private static final String TEST_FRIENDLY_NAME = "Network friendly name"; + private static final String TEST_REDIRECT_URL = "http://example.com/firstPath"; + private MockContext mServiceContext; private HandlerThread mCsHandlerThread; private ConnectivityService.Dependencies mDeps; @@ -358,10 +377,13 @@ public class ConnectivityServiceTest { private WrappedMultinetworkPolicyTracker mPolicyTracker; private HandlerThread mAlarmManagerThread; private TestNetIdManager mNetIdManager; + private QosCallbackMockHelper mQosCallbackMockHelper; + private QosCallbackTracker mQosCallbackTracker; + + // State variables required to emulate NetworkPolicyManagerService behaviour. + private int mUidRules = RULE_NONE; + private boolean mRestrictBackground = false; - @Mock IIpConnectivityMetrics mIpConnectivityMetrics; - @Mock IpConnectivityMetrics.Logger mMetricsService; - @Mock DefaultNetworkMetrics mDefaultNetworkMetrics; @Mock DeviceIdleInternal mDeviceIdleInternal; @Mock INetworkManagementService mNetworkManagementService; @Mock INetworkStatsService mStatsService; @@ -381,6 +403,7 @@ public class ConnectivityServiceTest { @Mock MockableSystemProperties mSystemProperties; @Mock EthernetManager mEthernetManager; @Mock NetworkPolicyManager mNetworkPolicyManager; + @Mock KeyStore mKeyStore; private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -407,9 +430,6 @@ public class ConnectivityServiceTest { private class MockContext extends BroadcastInterceptingContext { private final MockContentResolver mContentResolver; - // Contains all registered receivers since this object was created. Useful to clear - // them when needed, as BroadcastInterceptingContext does not provide this facility. - private final List<BroadcastReceiver> mRegisteredReceivers = new ArrayList<>(); @Spy private Resources mResources; private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>(); @@ -546,19 +566,6 @@ public class ConnectivityServiceTest { public void setPermission(String permission, Integer granted) { mMockedPermissions.put(permission, granted); } - - @Override - public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { - mRegisteredReceivers.add(receiver); - return super.registerReceiver(receiver, filter); - } - - public void clearRegisteredReceivers() { - // super.unregisterReceiver is a no-op for receivers that are not registered (because - // they haven't been registered or because they have already been unregistered). - // For the same reason, don't bother clearing mRegisteredReceivers. - for (final BroadcastReceiver rcv : mRegisteredReceivers) unregisterReceiver(rcv); - } } private void waitForIdle() { @@ -587,10 +594,10 @@ public class ConnectivityServiceTest { } // Bring up a network that we can use to send messages to ConnectivityService. - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); Network n = mWiFiNetworkAgent.getNetwork(); assertNotNull(n); @@ -607,10 +614,10 @@ public class ConnectivityServiceTest { @Ignore public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception { // Bring up a network that we can use to send messages to ConnectivityService. - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); Network n = mWiFiNetworkAgent.getNetwork(); assertNotNull(n); @@ -869,7 +876,7 @@ public class ConnectivityServiceTest { mProbesSucceeded = probesSucceeded; } - void notifyCaptivePortalDataChanged(CaptivePortalData data) { + void notifyCapportApiDataChanged(CaptivePortalData data) { try { mNmCallbacks.notifyCaptivePortalDataChanged(data); } catch (RemoteException e) { @@ -1073,7 +1080,16 @@ public class ConnectivityServiceTest { private boolean mAgentRegistered = false; private int mVpnType = VpnManager.TYPE_VPN_SERVICE; - private VpnInfo mVpnInfo; + private UnderlyingNetworkInfo mUnderlyingNetworkInfo; + + // These ConditionVariables allow tests to wait for LegacyVpnRunner to be stopped/started. + // TODO: this scheme is ad-hoc and error-prone because it does not fail if, for example, the + // test expects two starts in a row, or even if the production code calls start twice in a + // row. find a better solution. Simply putting a method to create a LegacyVpnRunner into + // Vpn.Dependencies doesn't work because LegacyVpnRunner is not a static class and has + // extensive access into the internals of Vpn. + private ConditionVariable mStartLegacyVpnCv = new ConditionVariable(); + private ConditionVariable mStopVpnRunnerCv = new ConditionVariable(); public MockVpn(int userId) { super(startHandlerThreadAndReturnLooper(), mServiceContext, @@ -1088,7 +1104,7 @@ public class ConnectivityServiceTest { return mDeviceIdleInternal; } }, - mNetworkManagementService, mMockNetd, userId, mock(KeyStore.class)); + mNetworkManagementService, mMockNetd, userId, mKeyStore); } public void setUids(Set<UidRange> uids) { @@ -1199,17 +1215,54 @@ public class ConnectivityServiceTest { updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect"); } mAgentRegistered = false; + setUids(null); + // Remove NET_CAPABILITY_INTERNET or MockNetworkAgent will refuse to connect later on. + mNetworkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); + mInterface = null; } @Override - public synchronized VpnInfo getVpnInfo() { - if (mVpnInfo != null) return mVpnInfo; + public void startLegacyVpnRunner() { + mStartLegacyVpnCv.open(); + } + + public void expectStartLegacyVpnRunner() { + assertTrue("startLegacyVpnRunner not called after " + TIMEOUT_MS + " ms", + mStartLegacyVpnCv.block(TIMEOUT_MS)); - return super.getVpnInfo(); + // startLegacyVpn calls stopVpnRunnerPrivileged, which will open mStopVpnRunnerCv, just + // before calling startLegacyVpnRunner. Restore mStopVpnRunnerCv, so the test can expect + // that the VpnRunner is stopped and immediately restarted by calling + // expectStartLegacyVpnRunner() and expectStopVpnRunnerPrivileged() back-to-back. + mStopVpnRunnerCv = new ConditionVariable(); } - private synchronized void setVpnInfo(VpnInfo vpnInfo) { - mVpnInfo = vpnInfo; + @Override + public void stopVpnRunnerPrivileged() { + if (mVpnRunner != null) { + super.stopVpnRunnerPrivileged(); + disconnect(); + mStartLegacyVpnCv = new ConditionVariable(); + } + mVpnRunner = null; + mStopVpnRunnerCv.open(); + } + + public void expectStopVpnRunnerPrivileged() { + assertTrue("stopVpnRunnerPrivileged not called after " + TIMEOUT_MS + " ms", + mStopVpnRunnerCv.block(TIMEOUT_MS)); + } + + @Override + public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() { + if (mUnderlyingNetworkInfo != null) return mUnderlyingNetworkInfo; + + return super.getUnderlyingNetworkInfo(); + } + + private synchronized void setUnderlyingNetworkInfo( + UnderlyingNetworkInfo underlyingNetworkInfo) { + mUnderlyingNetworkInfo = underlyingNetworkInfo; } } @@ -1229,12 +1282,23 @@ public class ConnectivityServiceTest { } } + private void updateUidNetworkingBlocked() { + doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked( + i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */, + mRestrictBackground) + ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean()); + } + private void setUidRulesChanged(int uidRules) throws RemoteException { - mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules); + mUidRules = uidRules; + updateUidNetworkingBlocked(); + mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules); } private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException { - mPolicyListener.onRestrictBackgroundChanged(restrictBackground); + mRestrictBackground = restrictBackground; + updateUidNetworkingBlocked(); + mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground); } private Nat464Xlat getNat464Xlat(NetworkAgentWrapper mna) { @@ -1282,10 +1346,19 @@ public class ConnectivityServiceTest { } } - private static final int VPN_USER = 0; - private static final int APP1_UID = UserHandle.getUid(VPN_USER, 10100); - private static final int APP2_UID = UserHandle.getUid(VPN_USER, 10101); - private static final int VPN_UID = UserHandle.getUid(VPN_USER, 10043); + private static final int PRIMARY_USER = 0; + private static final int APP1_UID = UserHandle.getUid(PRIMARY_USER, 10100); + private static final int APP2_UID = UserHandle.getUid(PRIMARY_USER, 10101); + private static final int VPN_UID = UserHandle.getUid(PRIMARY_USER, 10043); + private static final UserInfo PRIMARY_USER_INFO = new UserInfo(PRIMARY_USER, "", + UserInfo.FLAG_PRIMARY); + + private static final int RESTRICTED_USER = 1; + private static final UserInfo RESTRICTED_USER_INFO = new UserInfo(RESTRICTED_USER, "", + UserInfo.FLAG_RESTRICTED); + static { + RESTRICTED_USER_INFO.restrictedProfileParentId = PRIMARY_USER; + } @Before public void setUp() throws Exception { @@ -1294,12 +1367,14 @@ public class ConnectivityServiceTest { mContext = InstrumentationRegistry.getContext(); MockitoAnnotations.initMocks(this); - when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics); - when(mUserManager.getAliveUsers()).thenReturn( - Arrays.asList(new UserInfo[] { - new UserInfo(VPN_USER, "", 0), - })); + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); + when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO); + // canHaveRestrictedProfile does not take a userId. It applies to the userId of the context + // it was started from, i.e., PRIMARY_USER. + when(mUserManager.canHaveRestrictedProfile()).thenReturn(true); + when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO); + final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) @@ -1347,6 +1422,7 @@ public class ConnectivityServiceTest { mService.systemReadyInternal(); mockVpn(Process.myUid()); mCm.bindProcessToNetwork(null); + mQosCallbackTracker = mock(QosCallbackTracker.class); // Ensure that the default setting for Captive Portals is used for most tests setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); @@ -1369,10 +1445,9 @@ public class ConnectivityServiceTest { doReturn(mNetworkStack).when(deps).getNetworkStack(); doReturn(mSystemProperties).when(deps).getSystemProperties(); doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); - doReturn(mMetricsService).when(deps).getMetricsLogger(); doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt()); - doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics(); doReturn(mBatteryStatsService).when(deps).getBatteryStatsService(); + doReturn(mKeyStore).when(deps).getKeyStore(); doAnswer(inv -> { mPolicyTracker = new WrappedMultinetworkPolicyTracker( inv.getArgument(0), inv.getArgument(1), inv.getArgument(2)); @@ -1423,6 +1498,11 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.disconnect(); mEthernetNetworkAgent = null; } + + if (mQosCallbackMockHelper != null) { + mQosCallbackMockHelper.tearDown(); + mQosCallbackMockHelper = null; + } mMockVpn.disconnect(); waitForIdle(); @@ -1509,29 +1589,79 @@ public class ConnectivityServiceTest { } /** - * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION - * broadcasts are received. + * Class to simplify expecting broadcasts using BroadcastInterceptingContext. + * Ensures that the receiver is unregistered after the expected broadcast is received. This + * cannot be done in the BroadcastReceiver itself because BroadcastInterceptingContext runs + * the receivers' receive method while iterating over the list of receivers, and unregistering + * the receiver during iteration throws ConcurrentModificationException. */ - private ConditionVariable registerConnectivityBroadcast(final int count) { + private class ExpectedBroadcast extends CompletableFuture<Intent> { + private final BroadcastReceiver mReceiver; + + ExpectedBroadcast(BroadcastReceiver receiver) { + mReceiver = receiver; + } + + public Intent expectBroadcast(int timeoutMs) throws Exception { + try { + return get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + fail("Expected broadcast not received after " + timeoutMs + " ms"); + return null; + } finally { + mServiceContext.unregisterReceiver(mReceiver); + } + } + + public Intent expectBroadcast() throws Exception { + return expectBroadcast(TIMEOUT_MS); + } + + public void expectNoBroadcast(int timeoutMs) throws Exception { + waitForIdle(); + try { + final Intent intent = get(timeoutMs, TimeUnit.MILLISECONDS); + fail("Unexpected broadcast: " + intent.getAction() + " " + intent.getExtras()); + } catch (TimeoutException expected) { + } finally { + mServiceContext.unregisterReceiver(mReceiver); + } + } + } + + /** Expects that {@code count} CONNECTIVITY_ACTION broadcasts are received. */ + private ExpectedBroadcast registerConnectivityBroadcast(final int count) { return registerConnectivityBroadcastThat(count, intent -> true); } - private ConditionVariable registerConnectivityBroadcastThat(final int count, + private ExpectedBroadcast registerConnectivityBroadcastThat(final int count, @NonNull final Predicate<Intent> filter) { - final ConditionVariable cv = new ConditionVariable(); final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION); + // AtomicReference allows receiver to access expected even though it is constructed later. + final AtomicReference<ExpectedBroadcast> expectedRef = new AtomicReference<>(); final BroadcastReceiver receiver = new BroadcastReceiver() { - private int remaining = count; - public void onReceive(Context context, Intent intent) { - if (!filter.test(intent)) return; - if (--remaining == 0) { - cv.open(); - mServiceContext.unregisterReceiver(this); - } - } - }; + private int mRemaining = count; + public void onReceive(Context context, Intent intent) { + final int type = intent.getIntExtra(EXTRA_NETWORK_TYPE, -1); + final NetworkInfo ni = intent.getParcelableExtra(EXTRA_NETWORK_INFO); + Log.d(TAG, "Received CONNECTIVITY_ACTION type=" + type + " ni=" + ni); + if (!filter.test(intent)) return; + if (--mRemaining == 0) { + expectedRef.get().complete(intent); + } + } + }; + final ExpectedBroadcast expected = new ExpectedBroadcast(receiver); + expectedRef.set(expected); mServiceContext.registerReceiver(receiver, intentFilter); - return cv; + return expected; + } + + private ExpectedBroadcast expectConnectivityAction(int type, NetworkInfo.DetailedState state) { + return registerConnectivityBroadcastThat(1, intent -> + type == intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) && state.equals( + ((NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO)) + .getDetailedState())); } @Test @@ -1555,10 +1685,9 @@ public class ConnectivityServiceTest { // Connect the cell agent and wait for the connected broadcast. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL); - final ConditionVariable cv1 = registerConnectivityBroadcastThat(1, - intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE); + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); mCellNetworkAgent.connect(true); - waitFor(cv1); + b.expectBroadcast(); // Build legacy request for SUPL. final NetworkCapabilities legacyCaps = new NetworkCapabilities(); @@ -1568,27 +1697,17 @@ public class ConnectivityServiceTest { ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST); // File request, withdraw it and make sure no broadcast is sent - final ConditionVariable cv2 = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); final TestNetworkCallback callback = new TestNetworkCallback(); mCm.requestNetwork(legacyRequest, callback); callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); mCm.unregisterNetworkCallback(callback); - assertFalse(cv2.block(800)); // 800ms long enough to at least flake if this is sent - // As the broadcast did not fire, the receiver was not unregistered. Do this now. - mServiceContext.clearRegisteredReceivers(); - - // Disconnect the network and expect mobile disconnected broadcast. Use a small hack to - // check that has been sent. - final AtomicBoolean vanillaAction = new AtomicBoolean(false); - final ConditionVariable cv3 = registerConnectivityBroadcastThat(1, intent -> { - if (intent.getAction().equals(CONNECTIVITY_ACTION)) { - vanillaAction.set(true); - } - return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected(); - }); + b.expectNoBroadcast(800); // 800ms long enough to at least flake if this is sent + + // Disconnect the network and expect mobile disconnected broadcast. + b = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); mCellNetworkAgent.disconnect(); - waitFor(cv3); - assertTrue(vanillaAction.get()); + b.expectBroadcast(); } @Test @@ -1599,9 +1718,9 @@ public class ConnectivityServiceTest { assertNull(mCm.getActiveNetworkInfo()); assertNull(mCm.getActiveNetwork()); // Test bringing up validated cellular. - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); assertLength(2, mCm.getAllNetworks()); assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) || @@ -1609,9 +1728,9 @@ public class ConnectivityServiceTest { assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) || mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork())); // Test bringing up validated WiFi. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); assertLength(2, mCm.getAllNetworks()); assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) || @@ -1626,9 +1745,9 @@ public class ConnectivityServiceTest { assertLength(1, mCm.getAllNetworks()); assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork()); // Test WiFi disconnect. - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyNoNetwork(); } @@ -1636,9 +1755,9 @@ public class ConnectivityServiceTest { public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception { // Test bringing up unvalidated WiFi mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up unvalidated cellular mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); @@ -1651,19 +1770,19 @@ public class ConnectivityServiceTest { verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test cellular disconnect. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi disconnect. - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyNoNetwork(); } @@ -1671,25 +1790,25 @@ public class ConnectivityServiceTest { public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception { // Test bringing up unvalidated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi disconnect. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test cellular disconnect. - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mCellNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyNoNetwork(); } @@ -1697,24 +1816,24 @@ public class ConnectivityServiceTest { public void testUnlingeringDoesNotValidate() throws Exception { // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test cellular disconnect. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Unlingering a network should not cause it to be marked as validated. assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( @@ -1725,25 +1844,25 @@ public class ConnectivityServiceTest { public void testCellularOutscoresWeakWifi() throws Exception { // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi getting really weak. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.adjustScore(-11); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test WiFi restoring signal strength. - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.adjustScore(11); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); } @@ -1761,9 +1880,9 @@ public class ConnectivityServiceTest { mCellNetworkAgent.expectDisconnected(); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final ConditionVariable cv = registerConnectivityBroadcast(1); + final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up unvalidated cellular. // Expect it to be torn down because it could never be the highest scoring network @@ -1780,33 +1899,33 @@ public class ConnectivityServiceTest { public void testCellularFallback() throws Exception { // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Reevaluate WiFi (it'll instantly fail DNS). - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork()); // Should quickly fall back to Cellular. - waitFor(cv); + b.expectBroadcast(); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); verifyActiveNetwork(TRANSPORT_CELLULAR); // Reevaluate cellular (it'll instantly fail DNS). - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); // Should quickly fall back to WiFi. - waitFor(cv); + b.expectBroadcast(); assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( @@ -1818,23 +1937,23 @@ public class ConnectivityServiceTest { public void testWiFiFallback() throws Exception { // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Reevaluate cellular (it'll instantly fail DNS). - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); // Should quickly fall back to WiFi. - waitFor(cv); + b.expectBroadcast(); assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1904,13 +2023,13 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); // Test unvalidated networks - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); cellNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - waitFor(cv); + b.expectBroadcast(); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); // This should not trigger spurious onAvailable() callbacks, b/21762680. @@ -1919,28 +2038,28 @@ public class ConnectivityServiceTest { assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - waitFor(cv); + b.expectBroadcast(); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - cv = registerConnectivityBroadcast(2); + b = registerConnectivityBroadcast(2); mWiFiNetworkAgent.disconnect(); genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); cellNetworkCallback.assertNoCallback(); - waitFor(cv); + b.expectBroadcast(); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mCellNetworkAgent.disconnect(); genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - waitFor(cv); + b.expectBroadcast(); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); // Test validated networks @@ -2005,7 +2124,7 @@ public class ConnectivityServiceTest { Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())); final CaptivePortalData expectedCapportData = sanitized ? null : capportData; - mWiFiNetworkAgent.notifyCaptivePortalDataChanged(capportData); + mWiFiNetworkAgent.notifyCapportApiDataChanged(capportData); callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> Objects.equals(expectedCapportData, lp.getCaptivePortalData())); defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> @@ -2043,10 +2162,6 @@ public class ConnectivityServiceTest { @Test public void testOwnerUidCannotChange() throws Exception { - // Owner UIDs are not visible without location permission. - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); - final NetworkCapabilities ncTemplate = new NetworkCapabilities(); final int originalOwnerUid = Process.myUid(); ncTemplate.setOwnerUid(originalOwnerUid); @@ -2066,6 +2181,10 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.setNetworkCapabilities(agentCapabilities, true); waitForIdle(); + // Owner UIDs are not visible without location permission. + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + // Check that the capability change has been applied but the owner UID is not modified. NetworkCapabilities nc = mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()); assertEquals(originalOwnerUid, nc.getOwnerUid()); @@ -2661,9 +2780,9 @@ public class ConnectivityServiceTest { // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final ConditionVariable cv = registerConnectivityBroadcast(1); + final ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); // Register MMS NetworkRequest @@ -2689,9 +2808,9 @@ public class ConnectivityServiceTest { public void testMMSonCell() throws Exception { // Test bringing up cellular without MMS mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); mCellNetworkAgent.connect(false); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_CELLULAR); // Register MMS NetworkRequest @@ -3043,7 +3162,7 @@ public class ConnectivityServiceTest { .setBytesRemaining(12345L) .build(); - mWiFiNetworkAgent.notifyCaptivePortalDataChanged(testData); + mWiFiNetworkAgent.notifyCapportApiDataChanged(testData); captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> testData.equals(lp.getCaptivePortalData())); @@ -3056,6 +3175,136 @@ public class ConnectivityServiceTest { lp -> testData.equals(lp.getCaptivePortalData()) && lp.getMtu() == 1234); } + private TestNetworkCallback setupNetworkCallbackAndConnectToWifi() throws Exception { + // Grant NETWORK_SETTINGS permission to be able to receive LinkProperties change callbacks + // with sensitive (captive portal) data + mServiceContext.setPermission( + android.Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + mWiFiNetworkAgent.connectWithCaptivePortal(TEST_REDIRECT_URL, false /* isStrictMode */); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + return captivePortalCallback; + } + + private class CaptivePortalTestData { + CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData, + CaptivePortalData expectedMergedData) { + mNaData = naData; + mCapportData = capportData; + mExpectedMergedData = expectedMergedData; + } + + public final CaptivePortalData mNaData; + public final CaptivePortalData mCapportData; + public final CaptivePortalData mExpectedMergedData; + } + + private CaptivePortalTestData setupCaptivePortalData() { + final CaptivePortalData capportData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) + .setExpiryTime(1000000L) + .setBytesRemaining(12345L) + .build(); + + final CaptivePortalData naData = new CaptivePortalData.Builder() + .setBytesRemaining(80802L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA)) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + final CaptivePortalData expectedMergedData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setBytesRemaining(12345L) + .setExpiryTime(1000000L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA)) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + return new CaptivePortalTestData(naData, capportData, expectedMergedData); + } + + @Test + public void testMergeCaptivePortalApiWithFriendlyNameAndVenueUrl() throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Baseline capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); + + // Venue URL and friendly name from Network agent, confirm that API data gets precedence + // on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the capport data is merged + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); + + // Create a new LP with no Network agent capport data + final LinkProperties newLps = new LinkProperties(); + newLps.setMtu(1234); + mWiFiNetworkAgent.sendLinkProperties(newLps); + // CaptivePortalData is not lost and has the original values when LPs are received from the + // NetworkAgent + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()) + && lp.getMtu() == 1234); + + // Now send capport data only from the Network agent + mWiFiNetworkAgent.notifyCapportApiDataChanged(null); + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> lp.getCaptivePortalData() == null); + + newLps.setCaptivePortalData(captivePortalTestData.mNaData); + mWiFiNetworkAgent.sendLinkProperties(newLps); + + // Make sure that only the network agent capport data is available + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); + } + + @Test + public void testMergeCaptivePortalDataFromNetworkAgentFirstThenCapport() throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Venue URL and friendly name from Network agent, confirm that API data gets precedence + // on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the data is saved correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); + + // Expected merged data: Network agent data is preferred, and values that are not used by + // it are merged from capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + // Make sure that the Capport data is merged correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); + + // Now set the naData to null + linkProperties.setCaptivePortalData(null); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the Capport data is retained correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); + } + private NetworkRequest.Builder newWifiRequestBuilder() { return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI); } @@ -3226,8 +3475,8 @@ public class ConnectivityServiceTest { NetworkCapabilities networkCapabilities = new NetworkCapabilities(); networkCapabilities.addTransportType(TRANSPORT_WIFI) .setNetworkSpecifier(new MatchAllNetworkSpecifier()); - mService.requestNetwork(networkCapabilities, null, 0, null, - ConnectivityManager.TYPE_WIFI, mContext.getPackageName(), + mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(), + null, 0, null, ConnectivityManager.TYPE_WIFI, mContext.getPackageName(), getAttributionTag()); }); @@ -3361,6 +3610,7 @@ public class ConnectivityServiceTest { assertEquals(null, mCm.getActiveNetwork()); mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -3459,10 +3709,13 @@ public class ConnectivityServiceTest { @Test public void testBackgroundNetworks() throws Exception { - // Create a background request. We can't do this ourselves because ConnectivityService - // doesn't have an API for it. So just turn on mobile data always on. - setAlwaysOnNetworks(true); + // Create a cellular background request. grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid()); + final TestNetworkCallback cellBgCallback = new TestNetworkCallback(); + mCm.requestBackgroundNetwork(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback); + + // Make callbacks for monitoring. final NetworkRequest request = new NetworkRequest.Builder().build(); final NetworkRequest fgRequest = new NetworkRequest.Builder() .addCapability(NET_CAPABILITY_FOREGROUND).build(); @@ -3531,6 +3784,7 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(callback); mCm.unregisterNetworkCallback(fgCallback); + mCm.unregisterNetworkCallback(cellBgCallback); } @Ignore // This test has instrinsic chances of spurious failures: ignore for continuous testing. @@ -3624,51 +3878,55 @@ public class ConnectivityServiceTest { // Register the factory and expect it to start looking for a network. testFactory.expectAddRequestsWithScores(0); // Score 0 as the request is not served yet. testFactory.register(); - testFactory.waitForNetworkRequests(1); - assertTrue(testFactory.getMyStartRequested()); - // Bring up wifi. The factory stops looking for a network. - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - // Score 60 - 40 penalty for not validated yet, then 60 when it validates - testFactory.expectAddRequestsWithScores(20, 60); - mWiFiNetworkAgent.connect(true); - testFactory.waitForRequests(); - assertFalse(testFactory.getMyStartRequested()); - - ContentResolver cr = mServiceContext.getContentResolver(); - - // Turn on mobile data always on. The factory starts looking again. - testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0 - setAlwaysOnNetworks(true); - testFactory.waitForNetworkRequests(2); - assertTrue(testFactory.getMyStartRequested()); - - // Bring up cell data and check that the factory stops looking. - assertLength(1, mCm.getAllNetworks()); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated - mCellNetworkAgent.connect(true); - cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - testFactory.waitForNetworkRequests(2); - assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us. + try { + testFactory.waitForNetworkRequests(1); + assertTrue(testFactory.getMyStartRequested()); - // Check that cell data stays up. - waitForIdle(); - verifyActiveNetwork(TRANSPORT_WIFI); - assertLength(2, mCm.getAllNetworks()); + // Bring up wifi. The factory stops looking for a network. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + // Score 60 - 40 penalty for not validated yet, then 60 when it validates + testFactory.expectAddRequestsWithScores(20, 60); + mWiFiNetworkAgent.connect(true); + testFactory.waitForRequests(); + assertFalse(testFactory.getMyStartRequested()); - // Turn off mobile data always on and expect the request to disappear... - testFactory.expectRemoveRequests(1); - setAlwaysOnNetworks(false); - testFactory.waitForNetworkRequests(1); + ContentResolver cr = mServiceContext.getContentResolver(); + + // Turn on mobile data always on. The factory starts looking again. + testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0 + setAlwaysOnNetworks(true); + testFactory.waitForNetworkRequests(2); + assertTrue(testFactory.getMyStartRequested()); + + // Bring up cell data and check that the factory stops looking. + assertLength(1, mCm.getAllNetworks()); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + testFactory.waitForNetworkRequests(2); + assertFalse( + testFactory.getMyStartRequested()); // Because the cell network outscores us. + + // Check that cell data stays up. + waitForIdle(); + verifyActiveNetwork(TRANSPORT_WIFI); + assertLength(2, mCm.getAllNetworks()); - // ... and cell data to be torn down. - cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - assertLength(1, mCm.getAllNetworks()); + // Turn off mobile data always on and expect the request to disappear... + testFactory.expectRemoveRequests(1); + setAlwaysOnNetworks(false); + testFactory.waitForNetworkRequests(1); - testFactory.terminate(); - mCm.unregisterNetworkCallback(cellNetworkCallback); - handlerThread.quit(); + // ... and cell data to be torn down. + cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + assertLength(1, mCm.getAllNetworks()); + } finally { + testFactory.terminate(); + mCm.unregisterNetworkCallback(cellNetworkCallback); + handlerThread.quit(); + } } @Test @@ -4158,15 +4416,15 @@ public class ConnectivityServiceTest { } private Network connectKeepaliveNetwork(LinkProperties lp) throws Exception { - // Ensure the network is disconnected before we do anything. + // Ensure the network is disconnected before anything else occurs if (mWiFiNetworkAgent != null) { assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork())); } mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); mWiFiNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); verifyActiveNetwork(TRANSPORT_WIFI); mWiFiNetworkAgent.sendLinkProperties(lp); waitForIdle(); @@ -4722,10 +4980,10 @@ public class ConnectivityServiceTest { assertNotPinnedToWifi(); // Disconnect cell and wifi. - ConditionVariable cv = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down. + ExpectedBroadcast b = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down. mCellNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); // Pinning takes effect even if the pinned network is the default when the pin is set... TestNetworkPinner.pin(mServiceContext, wifiRequest); @@ -4735,10 +4993,10 @@ public class ConnectivityServiceTest { assertPinnedToWifiWithWifiDefault(); // ... and is maintained even when that network is no longer the default. - cv = registerConnectivityBroadcast(1); + b = registerConnectivityBroadcast(1); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mCellNetworkAgent.connect(true); - waitFor(cv); + b.expectBroadcast(); assertPinnedToWifiWithCellDefault(); } @@ -4838,7 +5096,7 @@ public class ConnectivityServiceTest { @Test public void testNetworkInfoOfTypeNone() throws Exception { - ConditionVariable broadcastCV = registerConnectivityBroadcast(1); + ExpectedBroadcast b = registerConnectivityBroadcast(1); verifyNoNetwork(); TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE); @@ -4871,9 +5129,7 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(callback); verifyNoNetwork(); - if (broadcastCV.block(10)) { - fail("expected no broadcast, but got CONNECTIVITY_ACTION broadcast"); - } + b.expectNoBroadcast(10); } @Test @@ -4959,20 +5215,22 @@ public class ConnectivityServiceTest { private void expectForceUpdateIfaces(Network[] networks, String defaultIface, Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception { ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class); - ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class); + ArgumentCaptor<UnderlyingNetworkInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass( + UnderlyingNetworkInfo[].class); verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(), any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture()); assertSameElementsNoDuplicates(networksCaptor.getValue(), networks); - VpnInfo[] infos = vpnInfosCaptor.getValue(); + UnderlyingNetworkInfo[] infos = vpnInfosCaptor.getValue(); if (vpnUid != null) { assertEquals("Should have exactly one VPN:", 1, infos.length); - VpnInfo info = infos[0]; + UnderlyingNetworkInfo info = infos[0]; assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid); - assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface); - assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces); + assertEquals("Unexpected VPN interface:", vpnIfname, info.iface); + assertSameElementsNoDuplicates(underlyingIfaces, + info.underlyingIfaces.toArray(new String[0])); } else { assertEquals(0, infos.length); return; @@ -5033,7 +5291,7 @@ public class ConnectivityServiceTest { waitForIdle(); verify(mStatsService, never()) .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME), - eq(new VpnInfo[0])); + eq(new UnderlyingNetworkInfo[0])); reset(mStatsService); // Roaming change should update ifaces @@ -5047,6 +5305,7 @@ public class ConnectivityServiceTest { lp.setInterfaceName(VPN_IFNAME); mMockVpn.establishForMyUid(lp); + assertUidRangesUpdatedForMyUid(true); final Network[] cellAndVpn = new Network[] { mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; @@ -5115,8 +5374,8 @@ public class ConnectivityServiceTest { // network for the VPN... verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class), any(NetworkState[].class), any() /* anyString() doesn't match null */, - argThat(infos -> infos[0].underlyingIfaces.length == 1 - && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0]))); + argThat(infos -> infos[0].underlyingIfaces.size() == 1 + && WIFI_IFNAME.equals(infos[0].underlyingIfaces.get(0)))); verifyNoMoreInteractions(mStatsService); reset(mStatsService); @@ -5129,8 +5388,8 @@ public class ConnectivityServiceTest { waitForIdle(); verify(mStatsService).forceUpdateIfaces(any(Network[].class), any(NetworkState[].class), any() /* anyString() doesn't match null */, - argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1 - && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0]))); + argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.size() == 1 + && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces.get(0)))); mEthernetNetworkAgent.disconnect(); waitForIdle(); reset(mStatsService); @@ -5632,6 +5891,7 @@ public class ConnectivityServiceTest { // (and doing so is difficult without using reflection) but it's good to test that the code // behaves approximately correctly. mMockVpn.establishForMyUid(false, true, false); + assertUidRangesUpdatedForMyUid(true); final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId()); mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork}); callback.expectAvailableCallbacksUnvalidated(mMockVpn); @@ -5671,6 +5931,126 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(callback); } + private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) { + // What Chromium used to do before https://chromium-review.googlesource.com/2605304 + assertEquals("Unexpected result for getActiveNetworkInfo(getActiveNetwork())", + expectedConnectivity, mCm.getNetworkInfo(mCm.getActiveNetwork()).isConnected()); + } + + @Test + public void testVpnUnderlyingNetworkSuspended() throws Exception { + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(callback); + + // Connect a VPN. + mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, + false /* isStrictMode */); + callback.expectAvailableCallbacksUnvalidated(mMockVpn); + + // Connect cellular data. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(false /* validated */); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + + // Suspend the cellular network and expect the VPN to be suspended. + mCellNetworkAgent.suspend(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); + callback.assertNoCallback(); + + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + // VPN's main underlying network is suspended, so no connectivity. + assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + + // Switch to another network. The VPN should no longer be suspended. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false /* validated */); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_WIFI)); + callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + + // Unsuspend cellular and then switch back to it. The VPN remains not suspended. + mCellNetworkAgent.resume(); + callback.assertNoCallback(); + mWiFiNetworkAgent.disconnect(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + // Spurious double callback? + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + + // Suspend cellular and expect no connectivity. + mCellNetworkAgent.suspend(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); + callback.assertNoCallback(); + + assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(false); + + // Resume cellular and expect that connectivity comes back. + mCellNetworkAgent.resume(); + callback.expectCapabilitiesThat(mMockVpn, + nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && nc.hasTransport(TRANSPORT_CELLULAR)); + callback.expectCallback(CallbackEntry.RESUMED, mMockVpn); + callback.assertNoCallback(); + + assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork()) + .hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertGetNetworkInfoOfGetActiveNetworkIsConnected(true); + } + @Test public void testVpnNetworkActive() throws Exception { final int uid = Process.myUid(); @@ -5789,6 +6169,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); defaultCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -5814,6 +6195,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -5839,6 +6221,7 @@ public class ConnectivityServiceTest { // Bring up a VPN that has the INTERNET capability, initially unvalidated. mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); // Even though the VPN is unvalidated, it becomes the default network for our app. callback.expectAvailableCallbacksUnvalidated(mMockVpn); @@ -5890,6 +6273,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(), false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS); @@ -5931,6 +6315,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); @@ -6040,10 +6425,7 @@ public class ConnectivityServiceTest { && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - // While the SUSPENDED callback should in theory be sent here, it is not. This is - // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never - // been public and are deprecated and slated for removal, there is no sense in spending - // resources fixing this bug now. + vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn); assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Use both again. @@ -6055,8 +6437,7 @@ public class ConnectivityServiceTest { && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI) && !caps.hasCapability(NET_CAPABILITY_NOT_METERED) && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - // As above, the RESUMED callback not being sent here is a bug, but not a bug that's - // worth anybody's time to fix. + vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn); assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent); // Disconnect cell. Receive update without even removing the dead network from the @@ -6098,6 +6479,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); @@ -6143,7 +6525,7 @@ public class ConnectivityServiceTest { } @Test - public void testVpnRestrictedUsers() throws Exception { + public void testRestrictedProfileAffectsVpnUidRanges() throws Exception { // NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities. mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); @@ -6156,6 +6538,7 @@ public class ConnectivityServiceTest { // Bring up a VPN mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); callback.expectAvailableThenValidatedCallbacks(mMockVpn); callback.assertNoCallback(); @@ -6174,15 +6557,11 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps) -> caps.hasCapability(NET_CAPABILITY_VALIDATED)); - // Create a fake restricted profile whose parent is our user ID. - final int userId = UserHandle.getUserId(uid); - final int restrictedUserId = userId + 1; - final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED); - info.restrictedProfileParentId = userId; - assertTrue(info.isRestricted()); - when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info); + when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER)) + .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); + final Intent addedIntent = new Intent(ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); // Send a USER_ADDED broadcast for it. // The BroadcastReceiver for this broadcast checks that is being run on the handler thread. @@ -6194,7 +6573,7 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 2 && caps.getUids().contains(new UidRange(uid, uid)) - && caps.getUids().contains(UidRange.createForUser(restrictedUserId)) + && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER)) && caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_WIFI)); @@ -6204,13 +6583,13 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 2 && caps.getUids().contains(new UidRange(uid, uid)) - && caps.getUids().contains(UidRange.createForUser(restrictedUserId)) + && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER)) && caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_WIFI)); // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); - removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); + removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); // Expect that the VPN gains the UID range for the restricted user, and that the capability @@ -6223,6 +6602,73 @@ public class ConnectivityServiceTest { } @Test + public void testLockdownVpnWithRestrictedProfiles() throws Exception { + // For ConnectivityService#setAlwaysOnVpnPackage. + mServiceContext.setPermission( + Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED); + // For call Vpn#setAlwaysOnPackage. + mServiceContext.setPermission( + Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); + // Necessary to see the UID ranges in NetworkCapabilities. + mServiceContext.setPermission( + Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + + final NetworkRequest request = new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_VPN) + .build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); + + final int uid = Process.myUid(); + + // Connect wifi and check that UIDs in the main and restricted profiles have network access. + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true /* validated */); + final int restrictedUid = UserHandle.getUid(RESTRICTED_USER, 42 /* appId */); + assertNotNull(mCm.getActiveNetworkForUid(uid)); + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Enable always-on VPN lockdown. The main user loses network access because no VPN is up. + final ArrayList<String> allowList = new ArrayList<>(); + mService.setAlwaysOnVpnPackage(PRIMARY_USER, ALWAYS_ON_PACKAGE, true /* lockdown */, + allowList); + waitForIdle(); + assertNull(mCm.getActiveNetworkForUid(uid)); + // This is arguably overspecified: a UID that is not running doesn't have an active network. + // But it's useful to check that non-default users do not lose network access, and to prove + // that the loss of connectivity below is indeed due to the restricted profile coming up. + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Start the restricted profile, and check that the UID within it loses network access. + when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER)) + .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO, + RESTRICTED_USER_INFO)); + // TODO: check that VPN app within restricted profile still has access, etc. + final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); + final Handler handler = new Handler(mCsHandlerThread.getLooper()); + handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); + waitForIdle(); + assertNull(mCm.getActiveNetworkForUid(uid)); + assertNull(mCm.getActiveNetworkForUid(restrictedUid)); + + // Stop the restricted profile, and check that the UID within it has network access again. + when(mUserManager.getAliveUsers()).thenReturn(Arrays.asList(PRIMARY_USER_INFO)); + + // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. + final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); + handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); + waitForIdle(); + assertNull(mCm.getActiveNetworkForUid(uid)); + assertNotNull(mCm.getActiveNetworkForUid(restrictedUid)); + + mService.setAlwaysOnVpnPackage(PRIMARY_USER, null, false /* lockdown */, allowList); + waitForIdle(); + } + + @Test public void testIsActiveNetworkMeteredOverWifi() throws Exception { // Returns true by default when no network is available. assertTrue(mCm.isActiveNetworkMetered()); @@ -6258,6 +6704,7 @@ public class ConnectivityServiceTest { // Connect VPN network. By default it is using current default network (Cell). mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); // Ensure VPN is now the active network. assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); @@ -6310,6 +6757,7 @@ public class ConnectivityServiceTest { // Connect VPN network. mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); // Ensure VPN is now the active network. assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); @@ -6409,9 +6857,15 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); setUidRulesChanged(RULE_REJECT_ALL); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); // ConnectivityService should cache it not to invoke the callback again. setUidRulesChanged(RULE_REJECT_METERED); @@ -6419,32 +6873,60 @@ public class ConnectivityServiceTest { setUidRulesChanged(RULE_NONE); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); setUidRulesChanged(RULE_REJECT_METERED); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); // Restrict the network based on UID rule and NOT_METERED capability change. mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + setUidRulesChanged(RULE_ALLOW_METERED); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); setUidRulesChanged(RULE_NONE); cellNetworkCallback.assertNoCallback(); - // Restrict the network based on BackgroundRestricted. + // Restrict background data. Networking is not blocked because the network is unmetered. setRestrictBackgroundChanged(true); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + assertNull(mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); setRestrictBackgroundChanged(true); cellNetworkCallback.assertNoCallback(); - setRestrictBackgroundChanged(false); + + setUidRulesChanged(RULE_ALLOW_METERED); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + + setRestrictBackgroundChanged(false); cellNetworkCallback.assertNoCallback(); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); mCm.unregisterNetworkCallback(cellNetworkCallback); } @@ -6557,6 +7039,7 @@ public class ConnectivityServiceTest { final int userId = UserHandle.getUserId(uid); final ArrayList<String> allowList = new ArrayList<>(); mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList); + waitForIdle(); UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1); UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999); @@ -6578,10 +7061,10 @@ public class ConnectivityServiceTest { // Disable lockdown, expect to see the network unblocked. mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); - expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf); callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); vpnUidCallback.assertNoCallback(); + expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -6624,9 +7107,11 @@ public class ConnectivityServiceTest { // Disable lockdown, remove our UID from the allowlist, and re-enable lockdown. // Everything should now be blocked. mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList); + waitForIdle(); expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3); allowList.clear(); mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList); + waitForIdle(); expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf); defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent); assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); @@ -6684,6 +7169,7 @@ public class ConnectivityServiceTest { assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability. assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); @@ -6703,39 +7189,262 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(vpnUidCallback); } + private void setupLegacyLockdownVpn() { + final String profileName = "testVpnProfile"; + final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8); + when(mKeyStore.contains(Credentials.LOCKDOWN_VPN)).thenReturn(true); + when(mKeyStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag); + + final VpnProfile profile = new VpnProfile(profileName); + profile.name = "My VPN"; + profile.server = "192.0.2.1"; + profile.dnsServers = "8.8.8.8"; + profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK; + final byte[] encodedProfile = profile.encode(); + when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile); + } + @Test - public final void testLoseTrusted() throws Exception { - final NetworkRequest trustedRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_TRUSTED) - .build(); - final TestNetworkCallback trustedCallback = new TestNetworkCallback(); - mCm.requestNetwork(trustedRequest, trustedCallback); + public void testLegacyLockdownVpn() throws Exception { + mServiceContext.setPermission( + Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); + final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + mCm.registerNetworkCallback(request, callback); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); - reset(mMockNetd); + final TestNetworkCallback defaultCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultCallback); - mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); - trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); + // Pretend lockdown VPN was configured. + setupLegacyLockdownVpn(); + + // LockdownVpnTracker disables the Vpn teardown code and enables lockdown. + // Check the VPN's state before it does so. + assertTrue(mMockVpn.getEnableTeardown()); + assertFalse(mMockVpn.getLockdown()); - mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); - trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - verify(mMockNetd).networkClearDefault(); + // Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker. + final int userId = UserHandle.getUserId(Process.myUid()); + final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + final Handler handler = new Handler(mCsHandlerThread.getLooper()); + handler.post(() -> mServiceContext.sendBroadcast(addedIntent)); + waitForIdle(); - mCm.unregisterNetworkCallback(trustedCallback); + // Lockdown VPN disables teardown and enables lockdown. + assertFalse(mMockVpn.getEnableTeardown()); + assertTrue(mMockVpn.getLockdown()); + + // Bring up a network. + // Expect nothing to happen because the network does not have an IPv4 default route: legacy + // VPN only supports IPv4. + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName("rmnet0"); + cellLp.addLinkAddress(new LinkAddress("2001:db8::1/64")); + cellLp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, "rmnet0")); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + mCellNetworkAgent.connect(false /* validated */); + callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + waitForIdle(); + assertNull(mMockVpn.getAgent()); + + // Add an IPv4 address. Ideally the VPN should start, but it doesn't because nothing calls + // LockdownVpnTracker#handleStateChangedLocked. This is a bug. + // TODO: consider fixing this. + cellLp.addLinkAddress(new LinkAddress("192.0.2.2/25")); + cellLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "rmnet0")); + mCellNetworkAgent.sendLinkProperties(cellLp); + callback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); + waitForIdle(); + assertNull(mMockVpn.getAgent()); + + // Disconnect, then try again with a network that supports IPv4 at connection time. + // Expect lockdown VPN to come up. + ExpectedBroadcast b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); + mCellNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + b1.expectBroadcast(); + + // When lockdown VPN is active, the NetworkInfo state in CONNECTIVITY_ACTION is overwritten + // with the state of the VPN network. So expect a CONNECTING broadcast. + b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTING); + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); + mCellNetworkAgent.connect(false /* validated */); + callback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent); + b1.expectBroadcast(); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED); + + // TODO: it would be nice if we could simply rely on the production code here, and have + // LockdownVpnTracker start the VPN, have the VPN code register its NetworkAgent with + // ConnectivityService, etc. That would require duplicating a fair bit of code from the + // Vpn tests around how to mock out LegacyVpnRunner. But even if we did that, this does not + // work for at least two reasons: + // 1. In this test, calling registerNetworkAgent does not actually result in an agent being + // registered. This is because nothing calls onNetworkMonitorCreated, which is what + // actually ends up causing handleRegisterNetworkAgent to be called. Code in this test + // that wants to register an agent must use TestNetworkAgentWrapper. + // 2. Even if we exposed Vpn#agentConnect to the test, and made MockVpn#agentConnect call + // the TestNetworkAgentWrapper code, this would deadlock because the + // TestNetworkAgentWrapper code cannot be called on the handler thread since it calls + // waitForIdle(). + mMockVpn.expectStartLegacyVpnRunner(); + b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); + ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED); + mMockVpn.establishForMyUid(); + callback.expectAvailableThenValidatedCallbacks(mMockVpn); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + b1.expectBroadcast(); + b2.expectBroadcast(); + assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + + // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect. + final LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName("wlan0"); + wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25")); + wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0")); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp); + + b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED); + // Wifi is CONNECTING because the VPN isn't up yet. + b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTING); + ExpectedBroadcast b3 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.connect(false /* validated */); + b1.expectBroadcast(); + b2.expectBroadcast(); + b3.expectBroadcast(); + mMockVpn.expectStopVpnRunnerPrivileged(); + mMockVpn.expectStartLegacyVpnRunner(); + + // TODO: why is wifi not blocked? Is it because when this callback is sent, the VPN is still + // connected, so the network is not considered blocked by the lockdown UID ranges? But the + // fact that a VPN is connected should only result in the VPN itself being unblocked, not + // any other network. Bug in isUidBlockedByVpn? + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI)); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectCapabilitiesThat(mMockVpn, nc -> nc.hasTransport(TRANSPORT_WIFI)); + defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn); + defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); + + // While the VPN is reconnecting on the new network, everything is blocked. + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); + assertNetworkInfo(TYPE_VPN, DetailedState.BLOCKED); + + // The VPN comes up again on wifi. + b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED); + b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mMockVpn.establishForMyUid(); + callback.expectAvailableThenValidatedCallbacks(mMockVpn); + defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); + b1.expectBroadcast(); + b2.expectBroadcast(); + + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + + // Disconnect cell. Nothing much happens since it's not the default network. + // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any + // NetworkInfo is updated. This is probably a bug. + // TODO: consider fixing this. + b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED); + mCellNetworkAgent.disconnect(); + b1.expectBroadcast(); + callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + defaultCallback.assertNoCallback(); + + assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED); + assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); + assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED); + + b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + b1.expectBroadcast(); + callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI)); + b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED); + mMockVpn.expectStopVpnRunnerPrivileged(); + callback.expectCallback(CallbackEntry.LOST, mMockVpn); + b2.expectBroadcast(); + } + + /** + * Test mutable and requestable network capabilities such as + * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and + * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the + * {@code ConnectivityService} re-assign the networks accordingly. + */ + @Test + public final void testLoseMutableAndRequestableCaps() throws Exception { + final int[] testCaps = new int [] { + NET_CAPABILITY_TRUSTED, + NET_CAPABILITY_NOT_VCN_MANAGED + }; + for (final int testCap : testCaps) { + // Create requests with and without the testing capability. + final TestNetworkCallback callbackWithCap = new TestNetworkCallback(); + final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback(); + mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(), + callbackWithCap); + mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(), + callbackWithoutCap); + + // Setup networks with testing capability and verify the default network changes. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(testCap); + mCellNetworkAgent.connect(true); + callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(testCap); + mWiFiNetworkAgent.connect(true); + callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); + reset(mMockNetd); + + // Remove the testing capability on wifi, verify the callback and default network + // changes back to cellular. + mWiFiNetworkAgent.removeCapability(testCap); + callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent); + callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent); + // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has + // it. + if (testCap == NET_CAPABILITY_TRUSTED) { + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); + } + + mCellNetworkAgent.removeCapability(testCap); + callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + callbackWithoutCap.assertNoCallback(); + if (testCap == NET_CAPABILITY_TRUSTED) { + verify(mMockNetd).networkClearDefault(); + } + + mCm.unregisterNetworkCallback(callbackWithCap); + mCm.unregisterNetworkCallback(callbackWithoutCap); + } } - @Ignore // 40%+ flakiness : figure out why and re-enable. @Test public final void testBatteryStatsNetworkType() throws Exception { final LinkProperties cellLp = new LinkProperties(); @@ -6743,8 +7452,8 @@ public class ConnectivityServiceTest { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); mCellNetworkAgent.connect(true); waitForIdle(); - verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(), - TYPE_MOBILE); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); reset(mBatteryStatsService); final LinkProperties wifiLp = new LinkProperties(); @@ -6752,18 +7461,20 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp); mWiFiNetworkAgent.connect(true); waitForIdle(); - verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(), - TYPE_WIFI); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports(wifiLp.getInterfaceName(), + new int[] { TRANSPORT_WIFI }); reset(mBatteryStatsService); mCellNetworkAgent.disconnect(); + mWiFiNetworkAgent.disconnect(); cellLp.setInterfaceName("wifi0"); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp); mCellNetworkAgent.connect(true); waitForIdle(); - verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(), - TYPE_MOBILE); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); + mCellNetworkAgent.disconnect(); } /** @@ -6836,8 +7547,8 @@ public class ConnectivityServiceTest { assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute); verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId)); verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME); - verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(), - TYPE_MOBILE); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(), + new int[] { TRANSPORT_CELLULAR }); networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId); @@ -6857,7 +7568,8 @@ public class ConnectivityServiceTest { // Make sure BatteryStats was not told about any v4- interfaces, as none should have // come online yet. waitForIdle(); - verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt()); + verify(mBatteryStatsService, never()).noteNetworkInterfaceForTransports(startsWith("v4-"), + any()); verifyNoMoreInteractions(mMockNetd); verifyNoMoreInteractions(mMockDnsResolver); @@ -6910,8 +7622,8 @@ public class ConnectivityServiceTest { assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8")); for (final LinkProperties stackedLp : stackedLpsAfterChange) { - verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(), - TYPE_MOBILE); + verify(mBatteryStatsService).noteNetworkInterfaceForTransports( + stackedLp.getInterfaceName(), new int[] { TRANSPORT_CELLULAR }); } reset(mMockNetd); when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME)) @@ -7046,11 +7758,11 @@ public class ConnectivityServiceTest { // prefix discovery is never started. LinkProperties lp = new LinkProperties(baseLp); lp.setNat64Prefix(pref64FromRa); - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); - mCellNetworkAgent.connect(false); - final Network network = mCellNetworkAgent.getNetwork(); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, lp); + mWiFiNetworkAgent.connect(false); + final Network network = mWiFiNetworkAgent.getNetwork(); int netId = network.getNetId(); - callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId); @@ -7059,8 +7771,8 @@ public class ConnectivityServiceTest { // If the RA prefix is withdrawn, clatd is stopped and prefix discovery is started. lp.setNat64Prefix(null); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); @@ -7068,8 +7780,8 @@ public class ConnectivityServiceTest { // If the RA prefix appears while DNS discovery is in progress, discovery is stopped and // clatd is started with the prefix from the RA. lp.setNat64Prefix(pref64FromRa); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa); inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString()); inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString()); @@ -7077,21 +7789,21 @@ public class ConnectivityServiceTest { // Withdraw the RA prefix so we can test the case where an RA prefix appears after DNS // discovery has succeeded. lp.setNat64Prefix(null); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */, pref64FromDnsStr, 96); - expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns); inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString()); // If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix // discovery is not stopped, and there are no callbacks. lp.setNat64Prefix(pref64FromDns); - mCellNetworkAgent.sendLinkProperties(lp); + mWiFiNetworkAgent.sendLinkProperties(lp); callback.assertNoCallback(); inOrder.verify(mMockNetd, never()).clatdStop(iface); inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); @@ -7101,7 +7813,7 @@ public class ConnectivityServiceTest { // If the RA is later withdrawn, nothing happens again. lp.setNat64Prefix(null); - mCellNetworkAgent.sendLinkProperties(lp); + mWiFiNetworkAgent.sendLinkProperties(lp); callback.assertNoCallback(); inOrder.verify(mMockNetd, never()).clatdStop(iface); inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); @@ -7111,8 +7823,8 @@ public class ConnectivityServiceTest { // If the RA prefix changes, clatd is restarted and prefix discovery is stopped. lp.setNat64Prefix(pref64FromRa); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); @@ -7126,8 +7838,8 @@ public class ConnectivityServiceTest { // If the RA prefix changes, clatd is restarted and prefix discovery is not started. lp.setNat64Prefix(newPref64FromRa); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, newPref64FromRa); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, newPref64FromRa); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString()); @@ -7137,7 +7849,7 @@ public class ConnectivityServiceTest { // If the RA prefix changes to the same value, nothing happens. lp.setNat64Prefix(newPref64FromRa); - mCellNetworkAgent.sendLinkProperties(lp); + mWiFiNetworkAgent.sendLinkProperties(lp); callback.assertNoCallback(); assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix()); inOrder.verify(mMockNetd, never()).clatdStop(iface); @@ -7151,19 +7863,19 @@ public class ConnectivityServiceTest { // If the same prefix is learned first by DNS and then by RA, and clat is later stopped, // (e.g., because the network disconnects) setPrefix64(netid, "") is never called. lp.setNat64Prefix(null); - mCellNetworkAgent.sendLinkProperties(lp); - expectNat64PrefixChange(callback, mCellNetworkAgent, null); + mWiFiNetworkAgent.sendLinkProperties(lp); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, null); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).setPrefix64(netId, ""); inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId); mService.mNetdEventCallback.onNat64PrefixEvent(netId, true /* added */, pref64FromDnsStr, 96); - expectNat64PrefixChange(callback, mCellNetworkAgent, pref64FromDns); + expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns); inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString()); inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any()); lp.setNat64Prefix(pref64FromDns); - mCellNetworkAgent.sendLinkProperties(lp); + mWiFiNetworkAgent.sendLinkProperties(lp); callback.assertNoCallback(); inOrder.verify(mMockNetd, never()).clatdStop(iface); inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString()); @@ -7174,10 +7886,10 @@ public class ConnectivityServiceTest { // When tearing down a network, clat state is only updated after CALLBACK_LOST is fired, but // before CONNECTIVITY_ACTION is sent. Wait for CONNECTIVITY_ACTION before verifying that // clat has been stopped, or the test will be flaky. - ConditionVariable cv = registerConnectivityBroadcast(1); - mCellNetworkAgent.disconnect(); - callback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - waitFor(cv); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + b.expectBroadcast(); inOrder.verify(mMockNetd).clatdStop(iface); inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId); @@ -7202,7 +7914,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(true); networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), - eq(ConnectivityManager.TYPE_MOBILE)); + eq(NetworkCapabilities.TRANSPORT_CELLULAR)); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); final LinkProperties wifiLp = new LinkProperties(); @@ -7216,7 +7928,7 @@ public class ConnectivityServiceTest { networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(), - eq(ConnectivityManager.TYPE_WIFI)); + eq(NetworkCapabilities.TRANSPORT_WIFI)); verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME)); // Disconnect wifi and switch back to cell @@ -7226,7 +7938,7 @@ public class ConnectivityServiceTest { assertNoCallbacks(networkCallback); verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), - eq(ConnectivityManager.TYPE_MOBILE)); + eq(NetworkCapabilities.TRANSPORT_CELLULAR)); // reconnect wifi mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); @@ -7252,10 +7964,10 @@ public class ConnectivityServiceTest { .destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId)); // Disconnect wifi - ConditionVariable cv = registerConnectivityBroadcast(1); + ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); reset(mNetworkManagementService); mWiFiNetworkAgent.disconnect(); - waitFor(cv); + b.expectBroadcast(); verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); // Clean up @@ -7341,6 +8053,7 @@ public class ConnectivityServiceTest { LinkProperties testLinkProperties = new LinkProperties(); testLinkProperties.setHttpProxy(testProxyInfo); mMockVpn.establishForMyUid(testLinkProperties); + assertUidRangesUpdatedForMyUid(true); // Test that the VPN network returns a proxy, and the WiFi does not. assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork())); @@ -7376,8 +8089,9 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); // A connected VPN should have interface rules set up. There are two expected invocations, // one during the VPN initial connection, one during the VPN LinkProperties update. @@ -7403,8 +8117,9 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); // Legacy VPN should not have interface rules set up verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -7418,8 +8133,9 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0")); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); // IPv6 unreachable route should not be misinterpreted as a default route verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -7432,8 +8148,9 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); // Connected VPN should have interface rules set up. There are two expected invocations, // one during VPN uid update, one during VPN LinkProperties update @@ -7483,8 +8200,10 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final UidRange vpnRange = UidRange.createForUser(VPN_USER); - mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange)); + final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER); + final Set<UidRange> vpnRanges = Collections.singleton(vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRanges); + assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); reset(mMockNetd); InOrder inOrder = inOrder(mMockNetd); @@ -7546,8 +8265,22 @@ public class ConnectivityServiceTest { naExtraInfo.unregister(); } + // To avoid granting location permission bypass. + private void denyAllLocationPrivilegedPermissions() { + mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_STACK, + PERMISSION_DENIED); + mServiceContext.setPermission(Manifest.permission.NETWORK_SETUP_WIZARD, + PERMISSION_DENIED); + } + private void setupLocationPermissions( int targetSdk, boolean locationToggle, String op, String perm) throws Exception { + denyAllLocationPrivilegedPermissions(); + final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = targetSdk; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) @@ -7568,51 +8301,76 @@ public class ConnectivityServiceTest { private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) { final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid); - return mService - .maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName()) - .getOwnerUid(); + return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, callerUid, mContext.getPackageName()).getOwnerUid(); + } + + private void verifyWifiInfoCopyNetCapsForCallerPermission( + int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) { + final WifiInfo wifiInfo = mock(WifiInfo.class); + when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true); + final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo); + + mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, callerUid, mContext.getPackageName()); + verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable)); } @Test - public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ() + throws Exception { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); final int myUid = Process.myUid(); assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ() + throws Exception { setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION); final int myUid = Process.myUid(); assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception { // Test that even with fine location permission, and UIDs matching, the UID is sanitized. setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); final int myUid = Process.myUid(); assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception { // Test that even with fine location permission, not being the owner leads to sanitization. setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); final int myUid = Process.myUid(); assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ() + throws Exception { // Test that not having fine location permission leads to sanitization. setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION); @@ -7620,26 +8378,34 @@ public class ConnectivityServiceTest { // Test that without the location permission, the owner field is sanitized. final int myUid = Process.myUid(); assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission() + throws Exception { setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */); // Test that without the location permission, the owner field is sanitized. final int myUid = Process.myUid(); assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); + final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid); mMockVpn.setVpnType(vpnType); - final VpnInfo vpnInfo = new VpnInfo(); - vpnInfo.ownerUid = vpnOwnerUid; - mMockVpn.setVpnInfo(vpnInfo); + final UnderlyingNetworkInfo underlyingNetworkInfo = + new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<String>()); + mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo); } private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType) @@ -7836,11 +8602,18 @@ public class ConnectivityServiceTest { assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder)); } + public NetworkAgentInfo fakeMobileNai(NetworkCapabilities nc) { + final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE, + ConnectivityManager.getNetworkTypeName(TYPE_MOBILE), + TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE)); + return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(), + nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, null, + 0, INVALID_UID, mQosCallbackTracker); + } + @Test public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception { - final NetworkAgentInfo naiWithoutUid = - new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0, - mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); mServiceContext.setPermission( android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED); @@ -7853,9 +8626,7 @@ public class ConnectivityServiceTest { @Test public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception { - final NetworkAgentInfo naiWithoutUid = - new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0, - mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); @@ -7868,9 +8639,7 @@ public class ConnectivityServiceTest { @Test public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception { - final NetworkAgentInfo naiWithoutUid = - new NetworkAgentInfo(null, null, null, null, new NetworkCapabilities(), 0, - mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); @@ -7883,21 +8652,17 @@ public class ConnectivityServiceTest { @Test public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception { - final Network network = new Network(NET_ID); - final NetworkAgentInfo naiWithoutUid = - new NetworkAgentInfo(null, network, null, null, new NetworkCapabilities(), 0, - mServiceContext, null, null, mService, null, null, null, 0, INVALID_UID); - - setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION); + final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); // Wait for networks to connect and broadcasts to be sent before removing permissions. waitForIdle(); - mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); + setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); - assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network})); + assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network})); waitForIdle(); assertTrue( "Active VPN permission not applied", @@ -7918,9 +8683,7 @@ public class ConnectivityServiceTest { public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception { final NetworkCapabilities nc = new NetworkCapabilities(); nc.setAdministratorUids(new int[] {Process.myUid()}); - final NetworkAgentInfo naiWithUid = - new NetworkAgentInfo(null, null, null, null, nc, 0, mServiceContext, null, null, - mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); @@ -7937,9 +8700,7 @@ public class ConnectivityServiceTest { final NetworkCapabilities nc = new NetworkCapabilities(); nc.setOwnerUid(Process.myUid()); nc.setAdministratorUids(new int[] {Process.myUid()}); - final NetworkAgentInfo naiWithUid = - new NetworkAgentInfo(null, null, null, null, nc, 0, mServiceContext, null, null, - mService, null, null, null, 0, INVALID_UID); + final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); @@ -8160,6 +8921,7 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + waitForIdle(); final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById(); @@ -8171,4 +8933,217 @@ public class ConnectivityServiceTest { assertTrue(isRequestIdInOrder); } } + + private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception { + final int uid = Process.myUid(); + assertVpnUidRangesUpdated(add, uidRangesForUid(uid), uid); + } + + private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid) + throws Exception { + InOrder inOrder = inOrder(mMockNetd); + ArgumentCaptor<int[]> exemptUidCaptor = ArgumentCaptor.forClass(int[].class); + + inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)), + exemptUidCaptor.capture()); + assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); + + if (add) { + inOrder.verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()), + eq(toUidRangeStableParcels(vpnRanges))); + } else { + inOrder.verify(mMockNetd, times(1)).networkRemoveUidRanges(eq(mMockVpn.getNetId()), + eq(toUidRangeStableParcels(vpnRanges))); + } + + inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)), + exemptUidCaptor.capture()); + assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); + } + + @Test + public void testVpnUidRangesUpdate() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER); + Set<UidRange> vpnRanges = Collections.singleton(vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRanges); + assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); + + reset(mMockNetd); + // Update to new range which is old range minus APP1, i.e. only APP2 + final Set<UidRange> newRanges = new HashSet<>(Arrays.asList( + new UidRange(vpnRange.start, APP1_UID - 1), + new UidRange(APP1_UID + 1, vpnRange.stop))); + mMockVpn.setUids(newRanges); + waitForIdle(); + + assertVpnUidRangesUpdated(true, newRanges, VPN_UID); + assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID); + } + + @Test + public void testInvalidRequestTypes() { + final int[] invalidReqTypeInts = new int[]{-1, NetworkRequest.Type.NONE.ordinal(), + NetworkRequest.Type.LISTEN.ordinal(), NetworkRequest.Type.values().length}; + final NetworkCapabilities nc = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI); + + for (int reqTypeInt : invalidReqTypeInts) { + assertThrows("Expect throws for invalid request type " + reqTypeInt, + IllegalArgumentException.class, + () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null, + ConnectivityManager.TYPE_NONE, mContext.getPackageName(), + getAttributionTag()) + ); + } + } + + private class QosCallbackMockHelper { + @NonNull public final QosFilter mFilter; + @NonNull public final IQosCallback mCallback; + @NonNull public final TestNetworkAgentWrapper mAgentWrapper; + @NonNull private final List<IQosCallback> mCallbacks = new ArrayList(); + + QosCallbackMockHelper() throws Exception { + Log.d(TAG, "QosCallbackMockHelper: "); + mFilter = mock(QosFilter.class); + + // Ensure the network is disconnected before anything else occurs + assertNull(mCellNetworkAgent); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + + verifyActiveNetwork(TRANSPORT_CELLULAR); + waitForIdle(); + final Network network = mCellNetworkAgent.getNetwork(); + + final Pair<IQosCallback, IBinder> pair = createQosCallback(); + mCallback = pair.first; + + when(mFilter.getNetwork()).thenReturn(network); + when(mFilter.validate()).thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + mAgentWrapper = mCellNetworkAgent; + } + + void registerQosCallback(@NonNull final QosFilter filter, + @NonNull final IQosCallback callback) { + mCallbacks.add(callback); + final NetworkAgentInfo nai = + mService.getNetworkAgentInfoForNetwork(filter.getNetwork()); + mService.registerQosCallbackInternal(filter, callback, nai); + } + + void tearDown() { + for (int i = 0; i < mCallbacks.size(); i++) { + mService.unregisterQosCallback(mCallbacks.get(i)); + } + } + } + + private Pair<IQosCallback, IBinder> createQosCallback() { + final IQosCallback callback = mock(IQosCallback.class); + final IBinder binder = mock(Binder.class); + when(callback.asBinder()).thenReturn(binder); + when(binder.isBinderAlive()).thenReturn(true); + return new Pair<>(callback, binder); + } + + + @Test + public void testQosCallbackRegistration() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper; + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); + + final NetworkAgentWrapper.CallbackType.OnQosCallbackRegister cbRegister1 = + (NetworkAgentWrapper.CallbackType.OnQosCallbackRegister) + wrapper.getCallbackHistory().poll(1000, x -> true); + assertNotNull(cbRegister1); + + final int registerCallbackId = cbRegister1.mQosCallbackId; + mService.unregisterQosCallback(mQosCallbackMockHelper.mCallback); + final NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister cbUnregister; + cbUnregister = (NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister) + wrapper.getCallbackHistory().poll(1000, x -> true); + assertNotNull(cbUnregister); + assertEquals(registerCallbackId, cbUnregister.mQosCallbackId); + assertNull(wrapper.getCallbackHistory().poll(200, x -> true)); + } + + @Test + public void testQosCallbackNoRegistrationOnValidationError() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED); + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); + waitForIdle(); + verify(mQosCallbackMockHelper.mCallback) + .onError(eq(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED)); + } + + @Test + public void testQosCallbackAvailableAndLost() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + final int sessionId = 10; + final int qosCallbackId = 1; + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback); + waitForIdle(); + + final EpsBearerQosSessionAttributes attributes = new EpsBearerQosSessionAttributes( + 1, 2, 3, 4, 5, new ArrayList<>()); + mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() + .sendQosSessionAvailable(qosCallbackId, sessionId, attributes); + waitForIdle(); + + verify(mQosCallbackMockHelper.mCallback).onQosEpsBearerSessionAvailable(argThat(session -> + session.getSessionId() == sessionId + && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes)); + + mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() + .sendQosSessionLost(qosCallbackId, sessionId); + waitForIdle(); + verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session -> + session.getSessionId() == sessionId + && session.getSessionType() == QosSession.TYPE_EPS_BEARER)); + } + + @Test + public void testQosCallbackTooManyRequests() throws Exception { + mQosCallbackMockHelper = new QosCallbackMockHelper(); + + when(mQosCallbackMockHelper.mFilter.validate()) + .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE); + for (int i = 0; i < 100; i++) { + final Pair<IQosCallback, IBinder> pair = createQosCallback(); + + try { + mQosCallbackMockHelper.registerQosCallback( + mQosCallbackMockHelper.mFilter, pair.first); + } catch (ServiceSpecificException e) { + assertEquals(e.errorCode, ConnectivityManager.Errors.TOO_MANY_REQUESTS); + if (i < 50) { + fail("TOO_MANY_REQUESTS thrown too early, the count is " + i); + } + + // As long as there is at least 50 requests, it is safe to assume it works. + // Note: The count isn't being tested precisely against 100 because the counter + // is shared with request network. + return; + } + } + fail("TOO_MANY_REQUESTS never thrown"); + } } diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index 3a071667a542..8c5d1d6d05e5 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -124,6 +124,22 @@ public class IpConnectivityMetricsTest { assertEquals("", output2); } + private void logDefaultNetworkEvent(long timeMs, NetworkAgentInfo nai, + NetworkAgentInfo oldNai) { + final Network network = (nai != null) ? nai.network() : null; + final int score = (nai != null) ? nai.getCurrentScore() : 0; + final boolean validated = (nai != null) ? nai.lastValidated : false; + final LinkProperties lp = (nai != null) ? nai.linkProperties : null; + final NetworkCapabilities nc = (nai != null) ? nai.networkCapabilities : null; + + final Network prevNetwork = (oldNai != null) ? oldNai.network() : null; + final int prevScore = (oldNai != null) ? oldNai.getCurrentScore() : 0; + final LinkProperties prevLp = (oldNai != null) ? oldNai.linkProperties : null; + final NetworkCapabilities prevNc = (oldNai != null) ? oldNai.networkCapabilities : null; + + mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, network, score, validated, + lp, nc, prevNetwork, prevScore, prevLp, prevNc); + } @Test public void testDefaultNetworkEvents() throws Exception { final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); @@ -147,7 +163,7 @@ public class IpConnectivityMetricsTest { for (NetworkAgentInfo[] pair : defaultNetworks) { timeMs += durationMs; durationMs += durationMs; - mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]); + logDefaultNetworkEvent(timeMs, pair[1], pair[0]); } String want = String.join("\n", @@ -331,8 +347,8 @@ public class IpConnectivityMetricsTest { final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell); NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi); - mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null); - mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai); + logDefaultNetworkEvent(timeMs + 200L, cellNai, null); + logDefaultNetworkEvent(timeMs + 300L, wifiNai, cellNai); String want = String.join("\n", "dropped_events: 0", diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 96c56e32f156..52cb836e19c8 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -34,7 +34,9 @@ import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.IDnsResolver; import android.net.INetd; +import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkProvider; @@ -76,6 +78,7 @@ public class LingerMonitorTest { @Mock Context mCtx; @Mock NetworkNotificationManager mNotifier; @Mock Resources mResources; + @Mock QosCallbackTracker mQosCallbackTracker; @Before public void setUp() { @@ -353,9 +356,10 @@ public class LingerMonitorTest { NetworkCapabilities caps = new NetworkCapabilities(); caps.addCapability(0); caps.addTransportType(transport); - NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, null, - caps, 50, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS, - NetworkProvider.ID_NONE, Binder.getCallingUid()); + NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, + new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */, + mConnService, mNetd, mDnsResolver, mNMS, NetworkProvider.ID_NONE, + Binder.getCallingUid(), mQosCallbackTracker); nai.everValidated = true; return nai; } diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 3648c4db1e16..68aaaeda1b12 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -252,6 +252,7 @@ public class VpnTest { @Test public void testRestrictedProfilesAreAddedToVpn() { + if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB); final Vpn vpn = createVpn(primaryUser.id); @@ -265,6 +266,7 @@ public class VpnTest { @Test public void testManagedProfilesAreNotAddedToVpn() { + if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. setMockedUsers(primaryUser, managedProfileA); final Vpn vpn = createVpn(primaryUser.id); @@ -287,6 +289,7 @@ public class VpnTest { @Test public void testUidAllowAndDenylist() throws Exception { + if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRange user = PRI_USER_RANGE; final String[] packages = {PKGS[0], PKGS[1], PKGS[2]}; @@ -312,6 +315,7 @@ public class VpnTest { @Test public void testGetAlwaysAndOnGetLockDown() throws Exception { + if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); // Default state. @@ -336,17 +340,12 @@ public class VpnTest { @Test public void testLockdownChangingPackage() throws Exception { + if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRange user = PRI_USER_RANGE; - // Default state. - assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], - user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); - // Set always-on without lockdown. assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore)); - assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], - user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); // Set always-on with lockdown. assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore)); @@ -355,10 +354,6 @@ public class VpnTest { new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2], - user.start + PKG_UIDS[3]); - assertUnblocked(vpn, user.start + PKG_UIDS[1]); - // Switch to another app. assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore)); verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { @@ -369,13 +364,11 @@ public class VpnTest { new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1), new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], - user.start + PKG_UIDS[2]); - assertUnblocked(vpn, user.start + PKG_UIDS[3]); } @Test public void testLockdownAllowlist() throws Exception { + if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRange user = PRI_USER_RANGE; @@ -386,8 +379,6 @@ public class VpnTest { new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1), new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]); - assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]); // Change allowed app list to PKGS[3]. assertTrue(vpn.setAlwaysOnPackage( PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore)); @@ -398,8 +389,6 @@ public class VpnTest { new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1), new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]); - assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]); // Change the VPN app. assertTrue(vpn.setAlwaysOnPackage( @@ -412,8 +401,6 @@ public class VpnTest { new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1), new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1) })); - assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]); - assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]); // Remove the list of allowed packages. assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore)); @@ -424,9 +411,6 @@ public class VpnTest { verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop), })); - assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], - user.start + PKG_UIDS[3]); - assertUnblocked(vpn, user.start + PKG_UIDS[0]); // Add the list of allowed packages. assertTrue(vpn.setAlwaysOnPackage( @@ -438,8 +422,6 @@ public class VpnTest { new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1), new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop) })); - assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]); - assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]); // Try allowing a package with a comma, should be rejected. assertFalse(vpn.setAlwaysOnPackage( @@ -460,46 +442,8 @@ public class VpnTest { } @Test - public void testLockdownAddingAProfile() throws Exception { - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); - - // Make a copy of the restricted profile, as we're going to mark it deleted halfway through. - final UserInfo tempProfile = new UserInfo(restrictedProfileA.id, restrictedProfileA.name, - restrictedProfileA.flags); - tempProfile.restrictedProfileParentId = primaryUser.id; - - final UidRange user = PRI_USER_RANGE; - final UidRange profile = UidRange.createForUser(tempProfile.id); - - // Set lockdown. - assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore)); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1), - new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop) - })); - // Verify restricted user isn't affected at first. - assertUnblocked(vpn, profile.start + PKG_UIDS[0]); - - // Add the restricted user. - setMockedUsers(primaryUser, tempProfile); - vpn.onUserAdded(tempProfile.id); - verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] { - new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1), - new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop) - })); - - // Remove the restricted user. - tempProfile.partial = true; - vpn.onUserRemoved(tempProfile.id); - verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] { - new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1), - new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop) - })); - } - - @Test public void testLockdownRuleRepeatability() throws Exception { + if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] { new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)}; @@ -532,6 +476,7 @@ public class VpnTest { @Test public void testLockdownRuleReversibility() throws Exception { + if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API. final Vpn vpn = createVpn(primaryUser.id); final UidRangeParcel[] entireUser = { new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop) @@ -1207,20 +1152,6 @@ public class VpnTest { return vpn; } - private static void assertBlocked(Vpn vpn, int... uids) { - for (int uid : uids) { - final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid); - assertTrue("Uid " + uid + " should be blocked", blocked); - } - } - - private static void assertUnblocked(Vpn vpn, int... uids) { - for (int uid : uids) { - final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid); - assertFalse("Uid " + uid + " should not be blocked", blocked); - } - } - /** * Populate {@link #mUserManager} with a list of fake users. */ @@ -1251,7 +1182,7 @@ public class VpnTest { doAnswer(invocation -> { final int id = (int) invocation.getArguments()[0]; return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0; - }).when(mUserManager).canHaveRestrictedProfile(anyInt()); + }).when(mUserManager).canHaveRestrictedProfile(); } /** diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java index 3aafe0b075f2..a058a466a4ff 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java @@ -33,8 +33,9 @@ import static android.net.NetworkStats.TAG_NONE; import static org.junit.Assert.assertEquals; import android.net.NetworkStats; +import android.net.UnderlyingNetworkInfo; -import com.android.internal.net.VpnInfo; +import java.util.Arrays; /** Superclass with utilities for NetworkStats(Service|Factory)Test */ abstract class NetworkStatsBaseTest { @@ -108,15 +109,11 @@ abstract class NetworkStatsBaseTest { assertEquals("unexpected operations", operations, entry.operations); } - static VpnInfo createVpnInfo(String[] underlyingIfaces) { + static UnderlyingNetworkInfo createVpnInfo(String[] underlyingIfaces) { return createVpnInfo(TUN_IFACE, underlyingIfaces); } - static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) { - VpnInfo info = new VpnInfo(); - info.ownerUid = UID_VPN; - info.vpnIface = vpnIface; - info.underlyingIfaces = underlyingIfaces; - return info; + static UnderlyingNetworkInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) { + return new UnderlyingNetworkInfo(UID_VPN, vpnIface, Arrays.asList(underlyingIfaces)); } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java index e4996d981fac..f3ae9b051e7c 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java @@ -36,13 +36,13 @@ import static org.junit.Assert.fail; import android.content.res.Resources; import android.net.NetworkStats; import android.net.TrafficStats; +import android.net.UnderlyingNetworkInfo; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.tests.net.R; -import com.android.internal.net.VpnInfo; import libcore.io.IoUtils; import libcore.io.Streams; @@ -79,7 +79,7 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { // related to networkStatsFactory is compiled to a minimal native library and loaded here. System.loadLibrary("networkstatsfactorytestjni"); mFactory = new NetworkStatsFactory(mTestProc, false); - mFactory.updateVpnInfos(new VpnInfo[0]); + mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]); } @After @@ -105,8 +105,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnRewriteTrafficThroughItself() throws Exception { - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -134,8 +135,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnWithClat() throws Exception { - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] { + createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); mFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption @@ -167,8 +169,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnWithOneUnderlyingIface() throws Exception { - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -191,8 +194,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception { // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -219,8 +223,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { @Test public void testVpnWithOneUnderlyingIface_withCompression() throws Exception { // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -242,8 +247,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. // Additionally, VPN is duplicating traffic across both WiFi and Cell. - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -267,10 +273,10 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { public void testConcurrentVpns() throws Exception { // Assume two VPNs are connected on two different network interfaces. VPN1 is using // TEST_IFACE and VPN2 is using TEST_IFACE2. - final VpnInfo[] vpnInfos = new VpnInfo[] { + final UnderlyingNetworkInfo[] underlyingNetworkInfos = new UnderlyingNetworkInfo[] { createVpnInfo(TUN_IFACE, new String[] {TEST_IFACE}), createVpnInfo(TUN_IFACE2, new String[] {TEST_IFACE2})}; - mFactory.updateVpnInfos(vpnInfos); + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -308,8 +314,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell. - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): @@ -335,8 +342,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell. - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface: // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. @@ -357,8 +365,9 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { public void testVpnWithIncorrectUnderlyingIface() throws Exception { // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2), // but has declared only WiFi (TEST_IFACE) in its underlying network set. - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - mFactory.updateVpnInfos(vpnInfos); + final UnderlyingNetworkInfo[] underlyingNetworkInfos = + new UnderlyingNetworkInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + mFactory.updateUnderlyingNetworkInfos(underlyingNetworkInfos); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index c7836297df75..dde78aa54199 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -21,7 +21,6 @@ import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.DEFAULT_NETWORK_YES; @@ -44,6 +43,7 @@ import static android.net.NetworkStatsHistory.FIELD_ALL; import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; +import static android.net.NetworkTemplate.buildTemplateWifi; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.UID_REMOVED; @@ -86,6 +86,7 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; +import android.net.UnderlyingNetworkInfo; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.ConditionVariable; import android.os.Handler; @@ -104,7 +105,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.net.VpnInfo; import com.android.internal.util.ArrayUtils; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.server.net.NetworkStatsService.NetworkStatsSettings; @@ -146,7 +146,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private static final String IMSI_2 = "310260"; private static final String TEST_SSID = "AndroidAP"; - private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard(); + private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID); private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); @@ -286,12 +286,12 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); - // modify some number on wifi, and trigger poll event incrementCurrentTime(HOUR_IN_MILLIS); expectDefaultSettings(); @@ -329,7 +329,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -402,7 +403,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // modify some number on wifi, and trigger poll event incrementCurrentTime(2 * HOUR_IN_MILLIS); @@ -442,7 +444,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some traffic on first network incrementCurrentTime(HOUR_IN_MILLIS); @@ -476,7 +479,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); forcePollAndWaitForIdle(); @@ -515,7 +519,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -567,61 +572,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } @Test - public void testUid3gWimaxCombinedByTemplate() throws Exception { - // pretend that network comes online - expectDefaultSettings(); - NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(buildEmptyStats()); - - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); - - // create some traffic - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); - mService.incrementOperationCount(UID_RED, 0xF00D, 5); - - forcePollAndWaitForIdle(); - - // verify service recorded history - assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5); - - - // now switch over to wimax network - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - states = new NetworkState[] {buildWimaxState(TEST_IFACE2)}; - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); - - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); - forcePollAndWaitForIdle(); - - - // create traffic on second network - incrementCurrentTime(HOUR_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(buildEmptyStats()); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L) - .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L) - .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L) - .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L)); - mService.incrementOperationCount(UID_RED, 0xFAAD, 5); - - forcePollAndWaitForIdle(); - - // verify that ALL_MOBILE template combines both - assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10); - } - - @Test public void testMobileStatsByRatType() throws Exception { final NetworkTemplate template3g = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS); @@ -637,7 +587,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new VpnInfo[0]); + new UnderlyingNetworkInfo[0]); // Create some traffic. incrementCurrentTime(MINUTE_IN_MILLIS); @@ -711,7 +661,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some traffic for two apps incrementCurrentTime(HOUR_IN_MILLIS); @@ -769,7 +720,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); NetworkStats.Entry entry1 = new NetworkStats.Entry( TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L); @@ -812,7 +764,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); NetworkStats.Entry uidStats = new NetworkStats.Entry( TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); @@ -866,7 +819,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some initial traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -923,7 +877,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some initial traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -962,7 +917,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // Create some traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -999,7 +955,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // create some tethering traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -1055,7 +1012,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -1160,7 +1118,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mService.registerNetworkStatsProvider("TEST", provider); assertNotNull(cb); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // Verifies that one requestStatsUpdate will be called during iface update. provider.expectOnRequestStatsUpdate(0 /* unused */); @@ -1211,7 +1170,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { expectDefaultSettings(); NetworkState[] states = new NetworkState[]{buildWifiState(true /* isMetered */, TEST_IFACE)}; - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // Register custom provider and retrieve callback. final TestableNetworkStatsProviderBinder provider = @@ -1260,7 +1220,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { // 3G network comes online. setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_UMTS); mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), - new VpnInfo[0]); + new UnderlyingNetworkInfo[0]); // Create some traffic. incrementCurrentTime(MINUTE_IN_MILLIS); @@ -1330,7 +1290,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { NetworkState[] states = new NetworkState[]{ buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)}; expectNetworkStatsUidDetail(buildEmptyStats()); - mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); // Create some traffic on mobile network. incrementCurrentTime(HOUR_IN_MILLIS); @@ -1503,6 +1464,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + capabilities.setSSID(TEST_SSID); return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID); } @@ -1524,17 +1486,6 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null); } - private static NetworkState buildWimaxState(@NonNull String iface) { - final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null); - info.setDetailedState(DetailedState.CONNECTED, null, null); - final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(iface); - final NetworkCapabilities capabilities = new NetworkCapabilities(); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); - capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); - return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, null, null); - } - private NetworkStats buildEmptyStats() { return new NetworkStats(getElapsedRealtime(), 0); } diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java index 25bd7c06be49..1102624da031 100644 --- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java +++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java @@ -29,7 +29,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -197,6 +196,11 @@ public class BroadcastInterceptingContext extends ContextWrapper { } @Override + public void sendStickyBroadcast(Intent intent, Bundle options) { + sendBroadcast(intent); + } + + @Override public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) { sendBroadcast(intent); } diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp index f967bf0d8f6b..c04ddd78e69b 100644 --- a/tests/vcn/Android.bp +++ b/tests/vcn/Android.bp @@ -16,10 +16,12 @@ android_test { "frameworks-base-testutils", "framework-protos", "mockito-target-minus-junit4", + "net-tests-utils", "platform-test-annotations", "services.core", ], libs: [ + "android.net.ipsec.ike.stubs.module_lib", "android.test.runner", "android.test.base", "android.test.mock", diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index e98b6ef2b3a6..86a15912b6b4 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -28,17 +28,25 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionConfigTest { - private static final int[] EXPOSED_CAPS = + // Public for use in VcnGatewayConnectionTest + public static final int[] EXPOSED_CAPS = new int[] { NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS }; - private static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN}; - private static final long[] RETRY_INTERVALS_MS = + public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN}; + + static { + Arrays.sort(EXPOSED_CAPS); + Arrays.sort(UNDERLYING_CAPS); + } + + public static final long[] RETRY_INTERVALS_MS = new long[] { TimeUnit.SECONDS.toMillis(5), TimeUnit.SECONDS.toMillis(30), @@ -47,10 +55,10 @@ public class VcnGatewayConnectionConfigTest { TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(30) }; - private static final int MAX_MTU = 1360; + public static final int MAX_MTU = 1360; - // Package protected for use in VcnConfigTest - static VcnGatewayConnectionConfig buildTestConfig() { + // Public for use in VcnGatewayConnectionTest + public static VcnGatewayConnectionConfig buildTestConfig() { final VcnGatewayConnectionConfig.Builder builder = new VcnGatewayConnectionConfig.Builder() .setRetryInterval(RETRY_INTERVALS_MS) @@ -123,12 +131,13 @@ public class VcnGatewayConnectionConfigTest { public void testBuilderAndGetters() { final VcnGatewayConnectionConfig config = buildTestConfig(); - for (int cap : EXPOSED_CAPS) { - config.hasExposedCapability(cap); - } - for (int cap : UNDERLYING_CAPS) { - config.requiresUnderlyingCapability(cap); - } + int[] exposedCaps = config.getExposedCapabilities(); + Arrays.sort(exposedCaps); + assertArrayEquals(EXPOSED_CAPS, exposedCaps); + + int[] underlyingCaps = config.getRequiredUnderlyingCapabilities(); + Arrays.sort(underlyingCaps); + assertArrayEquals(UNDERLYING_CAPS, underlyingCaps); assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs()); assertEquals(MAX_MTU, config.getMaxMtu()); diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java new file mode 100644 index 000000000000..f9db408462b7 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -0,0 +1,135 @@ +/* + * 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.net.vcn; + +import static androidx.test.InstrumentationRegistry.getContext; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; +import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.concurrent.Executor; + +public class VcnManagerTest { + private static final Executor INLINE_EXECUTOR = Runnable::run; + + private IVcnManagementService mMockVcnManagementService; + private VcnUnderlyingNetworkPolicyListener mMockPolicyListener; + + private Context mContext; + private VcnManager mVcnManager; + + @Before + public void setUp() { + mMockVcnManagementService = mock(IVcnManagementService.class); + mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class); + + mContext = getContext(); + mVcnManager = new VcnManager(mContext, mMockVcnManagementService); + } + + @Test + public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener); + + ArgumentCaptor<IVcnUnderlyingNetworkPolicyListener> captor = + ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class); + verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture()); + + assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + + IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue(); + listenerWrapper.onPolicyChanged(); + verify(mMockPolicyListener).onPolicyChanged(); + } + + @Test + public void testRemoveVcnUnderlyingNetworkPolicyListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener); + + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + verify(mMockVcnManagementService) + .addVcnUnderlyingNetworkPolicyListener( + any(IVcnUnderlyingNetworkPolicyListener.class)); + } + + @Test + public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception { + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener)); + verify(mMockVcnManagementService, never()) + .addVcnUnderlyingNetworkPolicyListener( + any(IVcnUnderlyingNetworkPolicyListener.class)); + } + + @Test(expected = NullPointerException.class) + public void testAddVcnUnderlyingNetworkPolicyListenerNullExecutor() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(null, mMockPolicyListener); + } + + @Test(expected = NullPointerException.class) + public void testAddVcnUnderlyingNetworkPolicyListenerNullListener() throws Exception { + mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, null); + } + + @Test(expected = NullPointerException.class) + public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() { + mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null); + } + + @Test + public void testGetUnderlyingNetworkPolicy() throws Exception { + NetworkCapabilities nc = new NetworkCapabilities(); + LinkProperties lp = new LinkProperties(); + when(mMockVcnManagementService.getUnderlyingNetworkPolicy(eq(nc), eq(lp))) + .thenReturn(new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, nc)); + + VcnUnderlyingNetworkPolicy policy = mVcnManager.getUnderlyingNetworkPolicy(nc, lp); + + assertFalse(policy.isTeardownRequested()); + assertEquals(nc, policy.getMergedNetworkCapabilities()); + verify(mMockVcnManagementService).getUnderlyingNetworkPolicy(eq(nc), eq(lp)); + } + + @Test(expected = NullPointerException.class) + public void testGetUnderlyingNetworkPolicyNullNetworkCapabilities() throws Exception { + mVcnManager.getUnderlyingNetworkPolicy(null, new LinkProperties()); + } + + @Test(expected = NullPointerException.class) + public void testGetUnderlyingNetworkPolicyNullLinkProperties() throws Exception { + mVcnManager.getUnderlyingNetworkPolicy(new NetworkCapabilities(), null); + } +} diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java new file mode 100644 index 000000000000..31561901be9e --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java @@ -0,0 +1,71 @@ +/* + * 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 android.net.vcn; + +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import android.net.wifi.WifiInfo; +import android.os.Parcel; + +import org.junit.Test; + +public class VcnTransportInfoTest { + private static final int SUB_ID = 1; + private static final int NETWORK_ID = 5; + private static final WifiInfo WIFI_INFO = + new WifiInfo.Builder().setNetworkId(NETWORK_ID).build(); + + private static final VcnTransportInfo CELL_UNDERLYING_INFO = new VcnTransportInfo(SUB_ID); + private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO); + + @Test + public void testGetWifiInfo() { + assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo()); + + assertNull(CELL_UNDERLYING_INFO.getWifiInfo()); + } + + @Test + public void testGetSubId() { + assertEquals(SUB_ID, CELL_UNDERLYING_INFO.getSubId()); + + assertEquals(INVALID_SUBSCRIPTION_ID, WIFI_UNDERLYING_INFO.getSubId()); + } + + @Test + public void testEquals() { + assertEquals(CELL_UNDERLYING_INFO, CELL_UNDERLYING_INFO); + assertEquals(WIFI_UNDERLYING_INFO, WIFI_UNDERLYING_INFO); + assertNotEquals(CELL_UNDERLYING_INFO, WIFI_UNDERLYING_INFO); + } + + @Test + public void testParcelUnparcel() { + verifyParcelingIsNull(CELL_UNDERLYING_INFO); + verifyParcelingIsNull(WIFI_UNDERLYING_INFO); + } + + private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) { + Parcel parcel = Parcel.obtain(); + vcnTransportInfo.writeToParcel(parcel, 0 /* flags */); + assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel)); + } +} diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java new file mode 100644 index 000000000000..3ba0a1f53a9f --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java @@ -0,0 +1,51 @@ +/* + * 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.net.vcn; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.net.NetworkCapabilities; + +import org.junit.Test; + +public class VcnUnderlyingNetworkPolicyTest { + private static final VcnUnderlyingNetworkPolicy DEFAULT_NETWORK_POLICY = + new VcnUnderlyingNetworkPolicy( + false /* isTearDownRequested */, new NetworkCapabilities()); + private static final VcnUnderlyingNetworkPolicy SAMPLE_NETWORK_POLICY = + new VcnUnderlyingNetworkPolicy( + true /* isTearDownRequested */, + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build()); + + @Test + public void testEquals() { + assertEquals(DEFAULT_NETWORK_POLICY, DEFAULT_NETWORK_POLICY); + assertEquals(SAMPLE_NETWORK_POLICY, SAMPLE_NETWORK_POLICY); + + assertNotEquals(DEFAULT_NETWORK_POLICY, SAMPLE_NETWORK_POLICY); + } + + @Test + public void testParcelUnparcel() { + assertParcelSane(SAMPLE_NETWORK_POLICY, 2); + } +} diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 696110f01869..e26bf19488d0 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -18,15 +18,23 @@ package com.android.server; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; +import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -35,8 +43,13 @@ import static org.mockito.Mockito.verify; import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; +import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfigTest; +import android.net.vcn.VcnUnderlyingNetworkPolicy; +import android.os.IBinder; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.os.Process; @@ -55,6 +68,7 @@ import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; import com.android.server.vcn.util.PersistableBundleUtils; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -126,12 +140,21 @@ public class VcnManagementServiceTest { private final VcnManagementService mVcnMgmtSvc; + private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener = + mock(IVcnUnderlyingNetworkPolicyListener.class); + private final IBinder mMockIBinder = mock(IBinder.class); + public VcnManagementServiceTest() throws Exception { - setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); - setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class); setupSystemService( - mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class); - setupSystemService(mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class); + mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + setupSystemService( + mMockContext, mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class); + setupSystemService( + mMockContext, + mSubMgr, + Context.TELEPHONY_SUBSCRIPTION_SERVICE, + SubscriptionManager.class); + setupSystemService(mMockContext, mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class); doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName(); @@ -169,13 +192,18 @@ public class VcnManagementServiceTest { setupMockedCarrierPrivilege(true); mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps); + doReturn(mMockIBinder).when(mMockPolicyListener).asBinder(); + // Make sure the profiles are loaded. mTestLooper.dispatchAll(); } - private void setupSystemService(Object service, String name, Class<?> serviceClass) { - doReturn(name).when(mMockContext).getSystemServiceName(serviceClass); - doReturn(service).when(mMockContext).getSystemService(name); + @Before + public void setUp() { + doNothing() + .when(mMockContext) + .enforceCallingOrSelfPermission( + eq(android.Manifest.permission.NETWORK_FACTORY), any()); } private void setupMockedCarrierPrivilege(boolean isPrivileged) { @@ -438,4 +466,53 @@ public class VcnManagementServiceTest { mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2); verify(vcnInstance).teardownAsynchronously(); } + + @Test + public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception { + mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + verify(mMockIBinder).linkToDeath(any(), anyInt()); + } + + @Test(expected = SecurityException.class) + public void testAddVcnUnderlyingNetworkPolicyListenerInvalidPermission() { + doThrow(new SecurityException()) + .when(mMockContext) + .enforceCallingOrSelfPermission( + eq(android.Manifest.permission.NETWORK_FACTORY), any()); + + mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + } + + @Test + public void testRemoveVcnUnderlyingNetworkPolicyListener() { + mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + + mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + } + + @Test + public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() { + mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + } + + @Test + public void testGetUnderlyingNetworkPolicy() throws Exception { + VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy( + new NetworkCapabilities(), new LinkProperties()); + + assertFalse(policy.isTeardownRequested()); + assertNotNull(policy.getMergedNetworkCapabilities()); + } + + @Test(expected = SecurityException.class) + public void testGetUnderlyingNetworkPolicyInvalidPermission() { + doThrow(new SecurityException()) + .when(mMockContext) + .enforceCallingOrSelfPermission( + eq(android.Manifest.permission.NETWORK_FACTORY), any()); + + mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties()); + } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java new file mode 100644 index 000000000000..d936183e5a0b --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java @@ -0,0 +1,105 @@ +/* + * 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 com.android.server.vcn; + +import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for VcnGatewayConnection.ConnectingState */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectionTestBase { + private VcnIkeSession mIkeSession; + + @Before + public void setUp() throws Exception { + super.setUp(); + + mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); + mGatewayConnection.transitionTo(mGatewayConnection.mConnectingState); + mTestLooper.dispatchAll(); + + mIkeSession = mGatewayConnection.getIkeSession(); + } + + @Test + public void testEnterStateCreatesNewIkeSession() throws Exception { + verify(mDeps).newIkeSession(any(), any(), any(), any(), any()); + } + + @Test + public void testNullNetworkTriggersDisconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(null); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).kill(); + } + + @Test + public void testNewNetworkTriggersReconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + verify(mIkeSession, never()).kill(); + } + + @Test + public void testSameNetworkDoesNotTriggerReconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testChildSessionClosedTriggersDisconnect() throws Exception { + getChildSessionCallback().onClosed(); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + } + + @Test + public void testIkeSessionClosedTriggersDisconnect() throws Exception { + getIkeSessionCallback().onClosed(); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java new file mode 100644 index 000000000000..4ecd21503165 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java @@ -0,0 +1,84 @@ +/* + * 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 com.android.server.vcn; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for VcnGatewayConnection.DisconnectedState */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnectionTestBase { + @Before + public void setUp() throws Exception { + super.setUp(); + + mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState); + mTestLooper.dispatchAll(); + } + + @Test + public void testEnterWhileNotRunningTriggersQuit() throws Exception { + final VcnGatewayConnection vgc = + new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps); + + vgc.setIsRunning(false); + vgc.transitionTo(vgc.mDisconnectedState); + mTestLooper.dispatchAll(); + + assertNull(vgc.getCurrentState()); + } + + @Test + public void testNetworkChangesTriggerStateTransitions() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testNullNetworkDoesNotTriggerStateTransition() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(null); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testTeardown() throws Exception { + mGatewayConnection.teardownAsynchronously(); + mTestLooper.dispatchAll(); + + assertNull(mGatewayConnection.getCurrentState()); + verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any()); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java new file mode 100644 index 000000000000..d0fec55a6827 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java @@ -0,0 +1,71 @@ +/* + * 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 com.android.server.vcn; + +import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeUnit; + +/** Tests for VcnGatewayConnection.DisconnectedState */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase { + @Before + public void setUp() throws Exception { + super.setUp(); + + mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession()); + + mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState); + mTestLooper.dispatchAll(); + } + + @Test + public void testIkeSessionClosed() throws Exception { + getIkeSessionCallback().onClosed(); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testTimeoutExpired() throws Exception { + mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS)); + mTestLooper.dispatchAll(); + + verify(mMockIkeSession).kill(); + } + + @Test + public void testTeardown() throws Exception { + mGatewayConnection.teardownAsynchronously(); + mTestLooper.dispatchAll(); + + // Should do nothing; already tearing down. + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java new file mode 100644 index 000000000000..d741e5cf4b35 --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java @@ -0,0 +1,82 @@ +/* + * 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 com.android.server.vcn; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.NetworkCapabilities; +import android.net.vcn.VcnGatewayConnectionConfigTest; +import android.os.ParcelUuid; +import android.os.test.TestLooper; +import android.telephony.SubscriptionInfo; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** Tests for TelephonySubscriptionTracker */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionTest { + private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID()); + private static final int TEST_SIM_SLOT_INDEX = 1; + private static final int TEST_SUBSCRIPTION_ID_1 = 2; + private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class); + private static final int TEST_SUBSCRIPTION_ID_2 = 3; + private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class); + private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP; + + static { + final Map<Integer, ParcelUuid> subIdToGroupMap = new HashMap<>(); + subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_PARCEL_UUID); + subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_PARCEL_UUID); + TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap); + } + + @NonNull private final Context mContext; + @NonNull private final TestLooper mTestLooper; + @NonNull private final VcnNetworkProvider mVcnNetworkProvider; + @NonNull private final VcnGatewayConnection.Dependencies mDeps; + + public VcnGatewayConnectionTest() { + mContext = mock(Context.class); + mTestLooper = new TestLooper(); + mVcnNetworkProvider = mock(VcnNetworkProvider.class); + mDeps = mock(VcnGatewayConnection.Dependencies.class); + } + + @Test + public void testBuildNetworkCapabilities() throws Exception { + final NetworkCapabilities caps = + VcnGatewayConnection.buildNetworkCapabilities( + VcnGatewayConnectionConfigTest.buildTestConfig()); + + for (int exposedCapability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) { + assertTrue(caps.hasCapability(exposedCapability)); + } + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java new file mode 100644 index 000000000000..b4d39bf74a4b --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -0,0 +1,128 @@ +/* + * 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 com.android.server.vcn; + +import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; +import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; +import static com.android.server.vcn.VcnTestUtils.setupIpSecManager; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.IpSecManager; +import android.net.IpSecTunnelInterfaceResponse; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.ipsec.ike.ChildSessionCallback; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.vcn.VcnGatewayConnectionConfig; +import android.net.vcn.VcnGatewayConnectionConfigTest; +import android.os.ParcelUuid; +import android.os.test.TestLooper; + +import com.android.server.IpSecService; + +import org.junit.Before; +import org.mockito.ArgumentCaptor; + +import java.util.UUID; + +public class VcnGatewayConnectionTestBase { + protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID()); + protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1; + protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE"; + protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 = + new UnderlyingNetworkRecord( + new Network(0), + new NetworkCapabilities(), + new LinkProperties(), + false /* blocked */); + protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 = + new UnderlyingNetworkRecord( + new Network(1), + new NetworkCapabilities(), + new LinkProperties(), + false /* blocked */); + + @NonNull protected final Context mContext; + @NonNull protected final TestLooper mTestLooper; + @NonNull protected final VcnNetworkProvider mVcnNetworkProvider; + @NonNull protected final VcnContext mVcnContext; + @NonNull protected final VcnGatewayConnectionConfig mConfig; + @NonNull protected final VcnGatewayConnection.Dependencies mDeps; + @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker; + + @NonNull protected final IpSecService mIpSecSvc; + + protected VcnIkeSession mMockIkeSession; + protected VcnGatewayConnection mGatewayConnection; + + public VcnGatewayConnectionTestBase() { + mContext = mock(Context.class); + mTestLooper = new TestLooper(); + mVcnNetworkProvider = mock(VcnNetworkProvider.class); + mVcnContext = mock(VcnContext.class); + mConfig = VcnGatewayConnectionConfigTest.buildTestConfig(); + mDeps = mock(VcnGatewayConnection.Dependencies.class); + mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class); + + mIpSecSvc = mock(IpSecService.class); + setupIpSecManager(mContext, mIpSecSvc); + + doReturn(mContext).when(mVcnContext).getContext(); + doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); + doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); + + doReturn(mUnderlyingNetworkTracker) + .when(mDeps) + .newUnderlyingNetworkTracker(any(), any(), any()); + } + + @Before + public void setUp() throws Exception { + IpSecTunnelInterfaceResponse resp = + new IpSecTunnelInterfaceResponse( + IpSecManager.Status.OK, + TEST_IPSEC_TUNNEL_RESOURCE_ID, + TEST_IPSEC_TUNNEL_IFACE); + doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any()); + + mMockIkeSession = mock(VcnIkeSession.class); + doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any()); + + mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps); + } + + protected IkeSessionCallback getIkeSessionCallback() { + ArgumentCaptor<IkeSessionCallback> captor = + ArgumentCaptor.forClass(IkeSessionCallback.class); + verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any()); + return captor.getValue(); + } + + protected ChildSessionCallback getChildSessionCallback() { + ArgumentCaptor<ChildSessionCallback> captor = + ArgumentCaptor.forClass(ChildSessionCallback.class); + verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture()); + return captor.getValue(); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java b/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java new file mode 100644 index 000000000000..2b1080650d6d --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java @@ -0,0 +1,44 @@ +/* + * 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 com.android.server.vcn; + +import static org.mockito.Mockito.doReturn; + +import android.content.Context; +import android.net.IpSecManager; + +import com.android.server.IpSecService; + +public class VcnTestUtils { + /** Mock system services by directly mocking the *Manager interface. */ + public static void setupSystemService( + Context mockContext, Object service, String name, Class<?> serviceClass) { + doReturn(name).when(mockContext).getSystemServiceName(serviceClass); + doReturn(service).when(mockContext).getSystemService(name); + } + + /** Mock IpSecService by mocking the underlying service binder. */ + public static IpSecManager setupIpSecManager(Context mockContext, IpSecService service) { + doReturn(Context.IPSEC_SERVICE).when(mockContext).getSystemServiceName(IpSecManager.class); + + final IpSecManager ipSecMgr = new IpSecManager(mockContext, service); + doReturn(ipSecMgr).when(mockContext).getSystemService(Context.IPSEC_SERVICE); + + // Return to ensure this doesn't get reaped. + return ipSecMgr; + } +} diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py index afe91cda37b0..15088fc81e88 100644 --- a/tools/stringslint/stringslint.py +++ b/tools/stringslint/stringslint.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- # Copyright (C) 2018 The Android Open Source Project # @@ -33,9 +34,6 @@ In general: import re, sys, codecs import lxml.etree as ET -reload(sys) -sys.setdefaultencoding('utf8') - BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): @@ -118,7 +116,7 @@ def lint(path): raw = f.read() if len(raw.strip()) == 0: return warnings - tree = ET.fromstring(raw) + tree = ET.fromstring(bytes(raw, encoding='utf-8')) root = tree #tree.getroot() last_comment = None @@ -231,6 +229,6 @@ for b in before: if len(after) > 0: for a in sorted(after.keys()): - print after[a] - print + print(after[a]) + print() sys.exit(1) diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh index bd80bb4e6f3f..bd0569873197 100755 --- a/tools/stringslint/stringslint_sha.sh +++ b/tools/stringslint/stringslint_sha.sh @@ -1,5 +1,5 @@ #!/bin/bash LOCAL_DIR="$( dirname ${BASH_SOURCE} )" git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do - python $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file) + python3 $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file) done |