diff options
163 files changed, 5562 insertions, 1265 deletions
diff --git a/Android.bp b/Android.bp index c9ed7ede26ba..3fb03c245d25 100644 --- a/Android.bp +++ b/Android.bp @@ -306,8 +306,8 @@ filegroup { genrule { name: "statslog-telephony-common-java-gen", tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" - + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog", + cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" + + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog", out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"], } @@ -383,6 +383,7 @@ filegroup { ":libbluetooth-binder-aidl", ":libcamera_client_aidl", ":libcamera_client_framework_aidl", + ":packagemanager_aidl", ":libupdate_engine_aidl", ":resourcemanager_aidl", ":storaged_aidl", @@ -453,15 +454,6 @@ java_library { ], } -filegroup { - name: "framework-all-sources", - srcs: [ - ":framework-mime-sources", - ":framework-non-updatable-sources", - ":framework-updatable-sources", - ], -} - // AIDL files under these paths are mixture of public and private ones. // They shouldn't be exported across module boundaries. java_defaults { @@ -607,7 +599,6 @@ java_library { // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly. "gps_debug.conf", "icu4j-platform-compat-config", - "libcore-platform-compat-config", "protolog.conf.json.gz", "services-platform-compat-config", "documents-ui-compat-config", @@ -687,7 +678,7 @@ genrule { name: "statslog-framework-java-gen", tools: ["stats-log-api-gen"], cmd: "$(location stats-log-api-gen) --java $(out) --module framework" + - " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource", + " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource", out: ["com/android/internal/util/FrameworkStatsLog.java"], } @@ -764,13 +755,21 @@ gensrcs { filegroup { name: "framework-annotations", srcs: [ + "core/java/android/annotation/AnyThread.java", + "core/java/android/annotation/AppIdInt.java", + "core/java/android/annotation/BytesLong.java", "core/java/android/annotation/CallbackExecutor.java", + "core/java/android/annotation/CallSuper.java", "core/java/android/annotation/CheckResult.java", "core/java/android/annotation/CurrentTimeMillisLong.java", + "core/java/android/annotation/CurrentTimeSecondsLong.java", + "core/java/android/annotation/DrawableRes.java", + "core/java/android/annotation/DurationMillisLong.java", "core/java/android/annotation/Hide.java", "core/java/android/annotation/IntDef.java", "core/java/android/annotation/IntRange.java", "core/java/android/annotation/LongDef.java", + "core/java/android/annotation/MainThread.java", "core/java/android/annotation/NonNull.java", "core/java/android/annotation/Nullable.java", "core/java/android/annotation/RequiresPermission.java", @@ -779,16 +778,17 @@ filegroup { "core/java/android/annotation/SystemApi.java", "core/java/android/annotation/SystemService.java", "core/java/android/annotation/TestApi.java", + "core/java/android/annotation/UserIdInt.java", "core/java/android/annotation/WorkerThread.java", "core/java/com/android/internal/annotations/GuardedBy.java", - "core/java/com/android/internal/annotations/VisibleForTesting.java", "core/java/com/android/internal/annotations/Immutable.java", + "core/java/com/android/internal/annotations/VisibleForTesting.java", ], } java_library { name: "framework-annotations-lib", - srcs: [ ":framework-annotations" ], + srcs: [":framework-annotations"], sdk_version: "core_current", } @@ -796,7 +796,6 @@ filegroup { name: "framework-ike-shared-srcs", visibility: ["//packages/modules/IPsec"], srcs: [ - "core/java/android/annotation/StringDef.java", "core/java/android/net/annotations/PolicyDirection.java", "core/java/com/android/internal/util/HexDump.java", "core/java/com/android/internal/util/IState.java", @@ -1015,7 +1014,6 @@ cc_library { }, } - // This is the full proto version of libplatformprotos. It may only // be used by test code that is not shipped on the device. cc_library { @@ -1176,7 +1174,7 @@ java_library { "core/java/android/os/RemoteException.java", "core/java/android/util/AndroidException.java", ], - libs: [ "unsupportedappusage" ], + libs: ["unsupportedappusage"], dxflags: ["--core-library"], installable: false, @@ -1208,36 +1206,6 @@ python_binary_host { ], } -filegroup { - name: "framework-media-annotation-srcs", - srcs: [ - ":framework-annotations", - "core/java/android/annotation/CallbackExecutor.java", - "core/java/android/annotation/CallSuper.java", - "core/java/android/annotation/DrawableRes.java", - "core/java/android/annotation/LongDef.java", - "core/java/android/annotation/StringDef.java", - ], -} - -filegroup { - name: "framework-mediaprovider-annotation-sources", - srcs: [ - ":framework-annotations", - "core/java/android/annotation/BytesLong.java", - "core/java/android/annotation/CurrentTimeSecondsLong.java", - "core/java/android/annotation/DurationMillisLong.java", - ], -} - -// Creates an index of AIDL methods; used for adding UnsupportedAppUsage -// annotations to private apis -aidl_mapping { - name: "framework-aidl-mappings", - srcs: [":framework-all-sources"], - output: "framework-aidl-mappings.txt", -} - // Avoid including Parcelable classes as we don't want to have two copies of // Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony) // and TeleService app (packages/services/Telephony). @@ -1359,6 +1327,19 @@ metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.x "--api-lint-ignore-prefix junit. " + "--api-lint-ignore-prefix org. " +filegroup { + name: "framework-non-updatable-stub-sources", + srcs: [ + ":framework-mime-sources", // mimemap builds separately but has no separate droidstubs. + ":framework-non-updatable-sources", + ":opt-telephony-srcs", + ":opt-net-voip-srcs", + "core/java/**/*.logtags", + "**/package.html", + ], + visibility: ["//visibility:private"], +} + build = [ "StubLibraries.bp", "ApiDocs.bp", diff --git a/ApiDocs.bp b/ApiDocs.bp index ada80bbc9874..ff3dc8db1ee5 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -58,13 +58,9 @@ framework_docs_only_libs = [ stubs_defaults { name: "framework-doc-stubs-default", srcs: [ - ":framework-mime-sources", - ":framework-non-updatable-sources", + ":framework-non-updatable-stub-sources", ":framework-updatable-sources", - "core/java/**/*.logtags", "test-base/src/**/*.java", - ":opt-telephony-srcs", - ":opt-net-voip-srcs", ":art.module.public.api{.public.stubs.source}", ":conscrypt.module.public.api{.public.stubs.source}", ":i18n.module.public.api{.public.stubs.source}", diff --git a/StubLibraries.bp b/StubLibraries.bp index fd614a7e3dc3..1644a550aca5 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -42,13 +42,7 @@ packages_to_document = [ stubs_defaults { name: "metalava-non-updatable-api-stubs-default", - srcs: [ - ":framework-non-updatable-sources", - "core/java/**/*.logtags", - ":opt-telephony-srcs", - ":opt-net-voip-srcs", - "**/package.html", - ], + srcs: [":framework-non-updatable-stub-sources"], sdk_version: "none", system_modules: "none", java_version: "1.8", @@ -434,6 +428,7 @@ droidstubs { "core/java/android/os/RemoteException.java", "core/java/android/util/AndroidException.java", ], + libs: ["framework-annotations-lib"], installable: false, sdk_version: "core_platform", annotations_enabled: true, @@ -447,7 +442,7 @@ droidstubs { java_library_static { name: "hwbinder.stubs", sdk_version: "core_current", - libs: ["stub-annotations"], + libs: ["framework-annotations-lib"], srcs: [ ":hwbinder-stubs-docs", ], diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 24b8055b9a57..a3fa8ac9958c 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -1006,6 +1006,41 @@ public class JobInfo implements Parcelable { mJobId = jobId; } + /** + * Creates a new Builder of JobInfo from an existing instance. + * @hide + */ + public Builder(@NonNull JobInfo job) { + mJobId = job.getId(); + mJobService = job.getService(); + mExtras = job.getExtras(); + mTransientExtras = job.getTransientExtras(); + mClipData = job.getClipData(); + mClipGrantFlags = job.getClipGrantFlags(); + mPriority = job.getPriority(); + mFlags = job.getFlags(); + mConstraintFlags = job.getConstraintFlags(); + mNetworkRequest = job.getRequiredNetwork(); + mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes(); + mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes(); + mTriggerContentUris = job.getTriggerContentUris() != null + ? new ArrayList<>(Arrays.asList(job.getTriggerContentUris())) : null; + mTriggerContentUpdateDelay = job.getTriggerContentUpdateDelay(); + mTriggerContentMaxDelay = job.getTriggerContentMaxDelay(); + mIsPersisted = job.isPersisted(); + mMinLatencyMillis = job.getMinLatencyMillis(); + mMaxExecutionDelayMillis = job.getMaxExecutionDelayMillis(); + mIsPeriodic = job.isPeriodic(); + mHasEarlyConstraint = job.hasEarlyConstraint(); + mHasLateConstraint = job.hasLateConstraint(); + mIntervalMillis = job.getIntervalMillis(); + mFlexMillis = job.getFlexMillis(); + mInitialBackoffMillis = job.getInitialBackoffMillis(); + // mBackoffPolicySet isn't set but it's fine since this is copying from an already valid + // job. + mBackoffPolicy = job.getBackoffPolicy(); + } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Builder setPriority(int priority) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index 2f5f555817ec..26010ef6d55c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -532,18 +532,23 @@ public final class JobStore { /** * Write out a tag with data identifying this job's constraints. If the constraint isn't here * it doesn't apply. + * TODO: b/183455312 Update this code to use proper serialization for NetworkRequest, + * because currently store is not including everything (like, UIDs, bandwidth, + * signal strength etc. are lost). */ private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException { out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS); if (jobStatus.hasConnectivityConstraint()) { final NetworkRequest network = jobStatus.getJob().getRequiredNetwork(); + // STOPSHIP b/183071974: improve the scheme for backward compatibility and + // mainline cleanliness. out.attribute(null, "net-capabilities", Long.toString( - BitUtils.packBits(network.networkCapabilities.getCapabilities()))); + BitUtils.packBits(network.getCapabilities()))); out.attribute(null, "net-unwanted-capabilities", Long.toString( - BitUtils.packBits(network.networkCapabilities.getUnwantedCapabilities()))); + BitUtils.packBits(network.getUnwantedCapabilities()))); out.attribute(null, "net-transport-types", Long.toString( - BitUtils.packBits(network.networkCapabilities.getTransportTypes()))); + BitUtils.packBits(network.getTransportTypes()))); } if (jobStatus.hasIdleConstraint()) { out.attribute(null, "idle", Boolean.toString(true)); @@ -967,18 +972,23 @@ public final class JobStore { null, "net-unwanted-capabilities"); final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types"); if (netCapabilities != null && netTransportTypes != null) { - final NetworkRequest request = new NetworkRequest.Builder().build(); + final NetworkRequest.Builder builder = new NetworkRequest.Builder() + .clearCapabilities(); final long unwantedCapabilities = netUnwantedCapabilities != null ? Long.parseLong(netUnwantedCapabilities) - : BitUtils.packBits(request.networkCapabilities.getUnwantedCapabilities()); - + : BitUtils.packBits(builder.build().getUnwantedCapabilities()); // We're okay throwing NFE here; caught by caller - request.networkCapabilities.setCapabilities( - BitUtils.unpackBits(Long.parseLong(netCapabilities)), - BitUtils.unpackBits(unwantedCapabilities)); - request.networkCapabilities.setTransportTypes( - BitUtils.unpackBits(Long.parseLong(netTransportTypes))); - jobBuilder.setRequiredNetwork(request); + for (int capability : BitUtils.unpackBits(Long.parseLong(netCapabilities))) { + builder.addCapability(capability); + } + for (int unwantedCapability : BitUtils.unpackBits( + Long.parseLong(netUnwantedCapabilities))) { + builder.addUnwantedCapability(unwantedCapability); + } + for (int transport : BitUtils.unpackBits(Long.parseLong(netTransportTypes))) { + builder.addTransportType(transport); + } + jobBuilder.setRequiredNetwork(builder.build()); } else { // Read legacy values val = parser.getAttributeValue(null, "connectivity"); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 4f472dfc31dd..a52223792b7a 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -21,6 +21,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; import android.net.ConnectivityManager; @@ -373,15 +374,23 @@ public final class ConnectivityController extends RestrictingController implemen } } + private static NetworkCapabilities.Builder copyCapabilities( + @NonNull final NetworkRequest request) { + final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); + for (int transport : request.getTransportTypes()) builder.addTransportType(transport); + for (int capability : request.getCapabilities()) builder.addCapability(capability); + return builder; + } + private static boolean isStrictSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { // A restricted job that's out of quota MUST use an unmetered network. if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) { - final NetworkCapabilities required = new NetworkCapabilities.Builder( - jobStatus.getJob().getRequiredNetwork().networkCapabilities) - .addCapability(NET_CAPABILITY_NOT_METERED).build(); - return required.satisfiedByNetworkCapabilities(capabilities); + final NetworkCapabilities.Builder builder = + copyCapabilities(jobStatus.getJob().getRequiredNetwork()); + builder.addCapability(NET_CAPABILITY_NOT_METERED); + return builder.build().satisfiedByNetworkCapabilities(capabilities); } else { return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities); } @@ -395,10 +404,10 @@ public final class ConnectivityController extends RestrictingController implemen } // See if we match after relaxing any unmetered request - final NetworkCapabilities relaxed = new NetworkCapabilities.Builder( - jobStatus.getJob().getRequiredNetwork().networkCapabilities) - .removeCapability(NET_CAPABILITY_NOT_METERED).build(); - if (relaxed.satisfiedByNetworkCapabilities(capabilities)) { + final NetworkCapabilities.Builder builder = + copyCapabilities(jobStatus.getJob().getRequiredNetwork()); + builder.removeCapability(NET_CAPABILITY_NOT_METERED); + if (builder.build().satisfiedByNetworkCapabilities(capabilities)) { // TODO: treat this as "maybe" response; need to check quotas return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC; } else { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index d7be2595e88b..ea8e7bc4ab06 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -28,6 +28,7 @@ import android.app.job.JobWorkItem; import android.content.ClipData; import android.content.ComponentName; import android.net.Network; +import android.net.NetworkRequest; import android.net.Uri; import android.os.RemoteException; import android.os.UserHandle; @@ -35,6 +36,7 @@ import android.provider.MediaStore; import android.text.format.DateFormat; import android.util.ArraySet; import android.util.Pair; +import android.util.Range; import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -52,6 +54,7 @@ import com.android.server.job.JobStatusShortInfoProto; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.function.Predicate; /** @@ -486,8 +489,15 @@ public final class JobStatus { // Later, when we check if a given network satisfies the required // network, we need to know the UID that is requesting it, so push // our source UID into place. - job.getRequiredNetwork().networkCapabilities.setSingleUid(this.sourceUid); + final JobInfo.Builder builder = new JobInfo.Builder(job); + final NetworkRequest.Builder requestBuilder = + new NetworkRequest.Builder(job.getRequiredNetwork()); + requestBuilder.setUids( + Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid))); + builder.setRequiredNetwork(requestBuilder.build()); + job = builder.build(); } + final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); mHasMediaBackupExemption = !job.hasLateConstraint() && exemptedMediaUrisOnly && requiresNetwork && this.sourcePackageName.equals(jsi.getMediaBackupPackage()); diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 3dd0da75d1a8..0501a45ef231 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -41,9 +41,7 @@ java_library { installable: true, sdk_version: "module_current", - libs: [ - "framework_media_annotation", - ], + libs: ["framework-annotations-lib"], static_libs: [ "exoplayer2-extractor" ], @@ -107,20 +105,9 @@ java_sdk_library { srcs: [ ":updatable-media-srcs", ], - - libs: [ - "framework_media_annotation", - ], impl_library_visibility: ["//frameworks/av/apex:__subpackages__"], } -java_library { - name: "framework_media_annotation", - srcs: [":framework-media-annotation-srcs"], - installable: false, - sdk_version: "core_current", -} - cc_library_shared { name: "libmediaparser-jni", srcs: [ diff --git a/core/api/current.txt b/core/api/current.txt index 07bebbeb7859..ef067858c530 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12139,6 +12139,7 @@ package android.content.pm { field public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint"; field public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management"; field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad"; + field public static final String FEATURE_HARDWARE_KEYSTORE = "android.hardware.hardware_keystore"; 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"; @@ -20445,7 +20446,9 @@ package android.media { method @NonNull public android.media.MediaFormat getOutputFormat(int); method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int); method @Nullable public android.media.Image getOutputImage(int); + method @Nullable public android.media.MediaCodec.ParameterDescriptor getParameterDescriptor(@NonNull String); method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int); + method @NonNull public java.util.List<java.lang.String> getSupportedVendorParameters(); method @Nullable public static android.media.Image mapHardwareBuffer(@NonNull android.hardware.HardwareBuffer); method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException; method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException; @@ -20464,6 +20467,8 @@ package android.media { method public void signalEndOfInputStream(); method public void start(); method public void stop(); + method public void subscribeToVendorParameters(@NonNull java.util.List<java.lang.String>); + method public void unsubscribeFromVendorParameters(@NonNull java.util.List<java.lang.String>); field public static final int BUFFER_FLAG_CODEC_CONFIG = 2; // 0x2 field public static final int BUFFER_FLAG_END_OF_STREAM = 4; // 0x4 field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1 @@ -20585,6 +20590,11 @@ package android.media { method public long getPresentationTimeUs(); } + public static class MediaCodec.ParameterDescriptor { + method @NonNull public String getName(); + method public int getType(); + } + public final class MediaCodec.QueueRequest { method public void queue(); method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer); @@ -25723,7 +25733,7 @@ package android.net.vcn { public abstract static class VcnManager.VcnStatusCallback { ctor public VcnManager.VcnStatusCallback(); method public abstract void onGatewayConnectionError(@NonNull int[], int, @Nullable Throwable); - method public abstract void onVcnStatusChanged(int); + method public abstract void onStatusChanged(int); } } @@ -33591,7 +33601,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_ALL_SIM_PROFILES_SETTINGS = "android.settings.MANAGE_ALL_SIM_PROFILES_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"; @@ -34219,6 +34229,9 @@ package android.provider { method public static android.net.Uri getUriForSubscriptionIdAndField(int, String); field public static final String AUTHORITY = "service-state"; field public static final android.net.Uri CONTENT_URI; + field public static final String DATA_NETWORK_TYPE = "data_network_type"; + field public static final String DATA_REG_STATE = "data_reg_state"; + field public static final String DUPLEX_MODE = "duplex_mode"; field public static final String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection"; field public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric"; field public static final String VOICE_REG_STATE = "voice_reg_state"; @@ -39391,6 +39404,7 @@ package android.telephony { field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int"; field public static final String KEY_PREFIX = "ims."; field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool"; + field public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY = "ims.rcs_feature_tag_allowed_string_array"; field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int"; } @@ -40662,7 +40676,6 @@ package android.telephony { method public static int[] calculateLength(String, boolean); method @Deprecated public static android.telephony.SmsMessage createFromPdu(byte[]); method public static android.telephony.SmsMessage createFromPdu(byte[], String); - method @Nullable public static android.telephony.SmsMessage createSmsSubmitPdu(@NonNull byte[], boolean); method public String getDisplayMessageBody(); method public String getDisplayOriginatingAddress(); method public String getEmailBody(); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index f155a5f07fd7..3b7a617f2e16 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -50,19 +50,10 @@ package android.net { method @NonNull public static String blockedReasonsToString(int); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int); - method public static boolean isUidBlocked(int, boolean); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); - field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000 - field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000 - field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000 - field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4 - field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1 - field public static final int BLOCKED_REASON_DOZE = 2; // 0x2 - field public static final int BLOCKED_REASON_NONE = 0; // 0x0 - field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8 } public static interface NetworkPolicyManager.NetworkPolicyCallback { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 92c18c68c4ff..e0de9780a7a1 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -155,12 +155,14 @@ package android { field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP"; field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK"; field public static final String NETWORK_STATS_PROVIDER = "android.permission.NETWORK_STATS_PROVIDER"; + field public static final String NFC_SET_CONTROLLER_ALWAYS_ON = "android.permission.NFC_SET_CONTROLLER_ALWAYS_ON"; field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP"; field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS"; field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE"; field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY"; field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS"; field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS"; + field public static final String OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD = "android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"; field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG"; field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT"; field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD"; @@ -1125,6 +1127,21 @@ package android.app.compat { method public static boolean isChangeEnabled(long); method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int); + method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void setPackageOverride(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>); + } + + public final class PackageOverride { + method public long getMaxVersionCode(); + method public long getMinVersionCode(); + method public boolean isEnabled(); + } + + public static final class PackageOverride.Builder { + ctor public PackageOverride.Builder(); + method @NonNull public android.app.compat.PackageOverride build(); + method @NonNull public android.app.compat.PackageOverride.Builder setEnabled(boolean); + method @NonNull public android.app.compat.PackageOverride.Builder setMaxVersionCode(long); + method @NonNull public android.app.compat.PackageOverride.Builder setMinVersionCode(long); } } @@ -1533,7 +1550,6 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); - method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int); } public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile { @@ -6427,9 +6443,11 @@ package android.net.netstats.provider { method public void notifyAlertReached(); method public void notifyLimitReached(); method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats); + method public void notifyWarningReached(); method public abstract void onRequestStatsUpdate(int); method public abstract void onSetAlert(long); method public abstract void onSetLimit(@NonNull String, long); + method public void onSetWarningAndLimit(@NonNull String, long, long); field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff } @@ -6708,10 +6726,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.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn(); + method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported(); 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 @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean); method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int); field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1 } @@ -9416,7 +9434,14 @@ package android.telephony { method @NonNull public static android.os.PersistableBundle getDefaultConfig(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void updateConfigForPhoneId(int, String); + field public static final int GBA_DIGEST = 3; // 0x3 + field public static final int GBA_ME = 1; // 0x1 + field public static final int GBA_U = 2; // 0x2 field public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string"; + field public static final String KEY_GBA_MODE_INT = "gba_mode_int"; + field public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = "gba_ua_security_organization_int"; + field public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = "gba_ua_security_protocol_int"; + field public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = "gba_ua_tls_cipher_suite_int"; field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool"; } @@ -10687,16 +10712,30 @@ package android.telephony.data { public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes { 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 public long getGuaranteedDownlinkBitRateKbps(); + method public long getGuaranteedUplinkBitRateKbps(); + method public long getMaxDownlinkBitRateKbps(); + method public long getMaxUplinkBitRateKbps(); + method public int getQosIdentifier(); 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 final class NrQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes { + method public int describeContents(); + method @NonNull public java.time.Duration getBitRateWindowDuration(); + method public long getGuaranteedDownlinkBitRateKbps(); + method public long getGuaranteedUplinkBitRateKbps(); + method public long getMaxDownlinkBitRateKbps(); + method public long getMaxUplinkBitRateKbps(); + method @IntRange(from=1, to=63) public int getQosFlowIdentifier(); + method public int getQosIdentifier(); + 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.NrQosSessionAttributes> CREATOR; + } + public abstract class QualifiedNetworksService extends android.app.Service { ctor public QualifiedNetworksService(); method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int); @@ -11745,6 +11784,7 @@ package android.telephony.ims { 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 @NonNull public java.util.Set<java.lang.String> getFeatureTags(); method public int getRequestResult(); method public int getSourceType(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -11759,6 +11799,14 @@ package android.telephony.ims { field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0 } + public static final class RcsContactUceCapability.OptionsBuilder { + ctor public RcsContactUceCapability.OptionsBuilder(@NonNull android.net.Uri); + method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder addFeatureTag(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder addFeatureTags(@NonNull java.util.Set<java.lang.String>); + method @NonNull public android.telephony.ims.RcsContactUceCapability build(); + method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder setRequestResult(int); + } + 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); @@ -12028,13 +12076,13 @@ package android.telephony.ims.feature { 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 @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.CapabilityExchangeEventListener); + method public void destroyCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase); method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); method public void onFeatureReady(); method public void onFeatureRemoved(); method public boolean queryCapabilityConfiguration(int, int); method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus(); - method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase); } public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities { @@ -12052,7 +12100,7 @@ package android.telephony.ims.feature { package android.telephony.ims.stub { public interface CapabilityExchangeEventListener { - method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException; + method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException; method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException; method public void onUnpublish() throws android.telephony.ims.ImsException; } @@ -12246,9 +12294,9 @@ package android.telephony.ims.stub { } public class RcsCapabilityExchangeImplBase { - ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor); + ctor public RcsCapabilityExchangeImplBase(); method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback); - method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback); + method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback); method public void subscribeForCapabilities(@NonNull java.util.Collection<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 @@ -12417,7 +12465,7 @@ package android.util { package android.uwb { public final class AngleMeasurement implements android.os.Parcelable { - ctor public AngleMeasurement(double, double, double); + ctor public AngleMeasurement(@FloatRange(from=-3.141592653589793, to=3.141592653589793) double, @FloatRange(from=0.0, to=3.141592653589793) double, @FloatRange(from=0.0, to=1.0) double); 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(); diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt index 0c02c43b1084..8895494e01d3 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -48,6 +48,14 @@ package android.app.prediction { } +package android.bluetooth { + + public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile { + method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int); + } + +} + package android.content { public class Intent implements java.lang.Cloneable android.os.Parcelable { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 11df05812e21..0c57f4e3b557 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -9,8 +9,10 @@ package android { field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; + field public static final String CLEAR_FREEZE_PERIOD = "android.permission.CLEAR_FREEZE_PERIOD"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; + field public static final String FORCE_DEVICE_POLICY_MANAGER_LOGS = "android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String KEEP_UNINSTALLED_PACKAGES = "android.permission.KEEP_UNINSTALLED_PACKAGES"; field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; @@ -19,10 +21,11 @@ package android { field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS"; field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK"; field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"; - field public static final String QUERY_USERS = "android.permission.QUERY_USERS"; + field public static final String QUERY_AUDIO_STATE = "android.permission.QUERY_AUDIO_STATE"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; + field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"; field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS"; field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS"; @@ -1064,6 +1067,7 @@ package android.os { field public static final int FIRST_ISOLATED_UID = 99000; // 0x182b8 field public static final int LAST_APP_ZYGOTE_ISOLATED_UID = 98999; // 0x182b7 field public static final int LAST_ISOLATED_UID = 99999; // 0x1869f + field public static final int NFC_UID = 1027; // 0x403 field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64 } @@ -1652,6 +1656,7 @@ package android.telephony { public class ServiceState implements android.os.Parcelable { method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo); method public int getDataNetworkType(); + method public int getDataRegState(); method public void setCdmaSystemAndNetworkId(int, int); method public void setCellBandwidths(int[]); method public void setChannelNumber(int); diff --git a/core/java/android/annotation/MainThread.java b/core/java/android/annotation/MainThread.java index 556fdb4e7742..a070246e5ef3 100644 --- a/core/java/android/annotation/MainThread.java +++ b/core/java/android/annotation/MainThread.java @@ -21,8 +21,6 @@ import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.os.Looper; - import java.lang.annotation.Retention; import java.lang.annotation.Target; @@ -40,8 +38,7 @@ import java.lang.annotation.Target; * </code> * </pre> * - * @memberDoc This method must be called from the - * {@linkplain Looper#getMainLooper() main thread} of your app. + * @memberDoc This method must be called from the main thread of your app. * @hide */ @Retention(SOURCE) diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java index ab38832458d6..74e1ece3fa89 100644 --- a/core/java/android/app/compat/CompatChanges.java +++ b/core/java/android/app/compat/CompatChanges.java @@ -104,16 +104,15 @@ public final class CompatChanges { * * @param packageName The package name of the app in question. * @param overrides A map from changeId to the override applied for this change id. - * @hide */ - @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG) - public static void setPackageOverride(String packageName, - Map<Long, PackageOverride> overrides) { + @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) + public static void setPackageOverride(@NonNull String packageName, + @NonNull Map<Long, PackageOverride> overrides) { IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides); try { - platformCompat.setOverridesFromInstaller(config, packageName); + platformCompat.setOverridesOnReleaseBuilds(config, packageName); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java index 9f97cd41128a..59b355523a30 100644 --- a/core/java/android/app/compat/PackageOverride.java +++ b/core/java/android/app/compat/PackageOverride.java @@ -17,8 +17,9 @@ package android.app.compat; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; -import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -32,15 +33,16 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ -public class PackageOverride implements Parcelable { +@SystemApi +public final class PackageOverride { + /** @hide */ @IntDef({ VALUE_UNDEFINED, VALUE_ENABLED, VALUE_DISABLED }) @Retention(RetentionPolicy.SOURCE) - /** @hide */ public @interface EvaluatedOverride { } @@ -75,10 +77,6 @@ public class PackageOverride implements Parcelable { this.mEnabled = enabled; } - private PackageOverride(Parcel in) { - this(in.readLong(), in.readLong(), in.readBoolean()); - } - /** * Evaluate the override for the given {@code versionCode}. If no override is defined for * the specified version code, {@link #VALUE_UNDEFINED} is returned. @@ -114,25 +112,23 @@ public class PackageOverride implements Parcelable { } /** Returns the enabled value for the override. */ - public boolean getEnabled() { + public boolean isEnabled() { return mEnabled; } /** @hide */ - @Override - public int describeContents() { - return 0; - } - - /** @hide */ - @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(Parcel dest) { dest.writeLong(mMinVersionCode); dest.writeLong(mMaxVersionCode); dest.writeBoolean(mEnabled); } /** @hide */ + public static PackageOverride createFromParcel(Parcel in) { + return new PackageOverride(in.readLong(), in.readLong(), in.readBoolean()); + } + + /** @hide */ @Override public String toString() { if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) { @@ -141,25 +137,10 @@ public class PackageOverride implements Parcelable { return String.format("[%d,%d,%b]", mMinVersionCode, mMaxVersionCode, mEnabled); } - /** @hide */ - public static final Creator<PackageOverride> CREATOR = - new Creator<PackageOverride>() { - - @Override - public PackageOverride createFromParcel(Parcel in) { - return new PackageOverride(in); - } - - @Override - public PackageOverride[] newArray(int size) { - return new PackageOverride[size]; - } - }; - /** * Builder to construct a PackageOverride. */ - public static class Builder { + public static final class Builder { private long mMinVersionCode = Long.MIN_VALUE; private long mMaxVersionCode = Long.MAX_VALUE; private boolean mEnabled; @@ -169,6 +150,7 @@ public class PackageOverride implements Parcelable { * * default value: {@code Long.MIN_VALUE}. */ + @NonNull public Builder setMinVersionCode(long minVersionCode) { mMinVersionCode = minVersionCode; return this; @@ -179,6 +161,7 @@ public class PackageOverride implements Parcelable { * * default value: {@code Long.MAX_VALUE}. */ + @NonNull public Builder setMaxVersionCode(long maxVersionCode) { mMaxVersionCode = maxVersionCode; return this; @@ -189,6 +172,7 @@ public class PackageOverride implements Parcelable { * * default value: {@code false}. */ + @NonNull public Builder setEnabled(boolean enabled) { mEnabled = enabled; return this; @@ -200,6 +184,7 @@ public class PackageOverride implements Parcelable { * @throws IllegalArgumentException if {@code minVersionCode} is larger than * {@code maxVersionCode}. */ + @NonNull public PackageOverride build() { if (mMinVersionCode > mMaxVersionCode) { throw new IllegalArgumentException("minVersionCode must not be larger than " diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 4fb557780d04..632572dea3c6 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -567,6 +567,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @return true if priority is set, false on error * @hide * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)} + * @removed */ @Deprecated @SystemApi diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java index 86721502d409..3c20dcac8ca3 100644 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ b/core/java/android/bluetooth/le/ScanFilter.java @@ -587,7 +587,7 @@ public final class ScanFilter implements Parcelable { * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. */ public Builder setDeviceAddress(String deviceAddress) { - return setDeviceAddress(mDeviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC); + return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC); } /** diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS index d0d406a0c9e6..01b554a56720 100644 --- a/core/java/android/content/OWNERS +++ b/core/java/android/content/OWNERS @@ -8,3 +8,5 @@ per-file Intent.java = patb@google.com per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS +per-file ComponentCallbacksController = file:/services/core/java/com/android/server/wm/OWNERS +per-file ComponentCallbacksController = charlesccchen@google.com diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 6ec11693d69b..ad9e31b6cbf4 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -2167,7 +2167,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0; } - /** @hide */ + /** + * True if the application is pre-installed on the OEM partition of the system image. + * @hide + */ @SystemApi public boolean isOem() { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0; @@ -2215,13 +2218,19 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } - /** @hide */ + /** + * True if the application is pre-installed on the vendor partition of the system image. + * @hide + */ @SystemApi public boolean isVendor() { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0; } - /** @hide */ + /** + * True if the application is pre-installed on the product partition of the system image. + * @hide + */ @SystemApi public boolean isProduct() { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 7e17256f0229..23f5e3ab6c1f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3167,8 +3167,57 @@ public abstract class PackageManager { public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking"; /** - * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: - * The device has a StrongBox hardware-backed Keystore. + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device implements + * the Android Keystore backed by an isolated execution environment. The version indicates + * which features are implemented in the isolated execution environment: + * <ul> + * <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support + * for app-generated attestation keys (see {@link + * android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}). + * <li>41: Hardware enforcement of device-unlocked keys (see {@link + * android.security.keystore.KeyGenParameterSpec.Builder#setUnlockedDeviceRequired(boolean)}). + * <li>40: Support for wrapped key import (see {@link + * android.security.keystore.WrappedKeyEntry}), optional support for ID attestation (see {@link + * android.security.keystore.KeyGenParameterSpec.Builder#setDevicePropertiesAttestationIncluded(boolean)}), + * attestation (see {@link + * android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])}), + * AES, HMAC, ECDSA and RSA support where the secret or private key never leaves secure + * hardware, and support for requiring user authentication before a key can be used. + * </ul> + * This feature version is guaranteed to be set for all devices launching with Android 12 and + * may be set on devices launching with an earlier version. If the feature version is set, it + * will at least have the value 40. If it's not set the device may have a version of + * hardware-backed keystore but it may not support all features listed above. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_HARDWARE_KEYSTORE = "android.hardware.hardware_keystore"; + + /** + * Feature for {@link #getSystemAvailableFeatures}, {@link #hasSystemFeature(String)}, and + * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device implements + * the Android Keystore backed by a dedicated secure processor referred to as + * <a href="https://source.android.com/security/best-practices/hardware#strongbox-keymaster"> + * StrongBox</a>. If this feature has a version, the version number indicates which features are + * implemented in StrongBox: + * <ul> + * <li>100: Hardware support for ECDH (see {@link javax.crypto.KeyAgreement}) and support + * for app-generated attestation keys (see {@link + * android.security.keystore.KeyGenParameterSpec.Builder#setAttestKeyAlias(String)}). + * <li>41: Hardware enforcement of device-unlocked keys (see {@link + * android.security.keystore.KeyGenParameterSpec.Builder#setUnlockedDeviceRequired(boolean)}). + * <li>40: Support for wrapped key import (see {@link + * android.security.keystore.WrappedKeyEntry}), optional support for ID attestation (see {@link + * android.security.keystore.KeyGenParameterSpec.Builder#setDevicePropertiesAttestationIncluded(boolean)}), + * attestation (see {@link + * android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])}), + * AES, HMAC, ECDSA and RSA support where the secret or private key never leaves secure + * hardware, and support for requiring user authentication before a key can be used. + * </ul> + * If a device has StrongBox, this feature version number is guaranteed to be set for all + * devices launching with Android 12 and may be set on devices launching with an earlier + * version. If the feature version is set, it will at least have the value 40. If it's not + * set the device may have StrongBox but it may not support all features listed above. */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_STRONGBOX_KEYSTORE = diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 10be02c414cd..68606ec90dc9 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -204,81 +204,6 @@ public class NetworkPolicyManager { public @interface SubscriptionOverrideMask {} /** - * Flag to indicate that an app is not subject to any restrictions that could result in its - * network access blocked. - * - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static final int BLOCKED_REASON_NONE = 0; - - /** - * Flag to indicate that an app is subject to Battery saver restrictions that would - * result in its network access being blocked. - * - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static final int BLOCKED_REASON_BATTERY_SAVER = 1 << 0; - - /** - * Flag to indicate that an app is subject to Doze restrictions that would - * result in its network access being blocked. - * - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static final int BLOCKED_REASON_DOZE = 1 << 1; - - /** - * Flag to indicate that an app is subject to App Standby restrictions that would - * result in its network access being blocked. - * - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static final int BLOCKED_REASON_APP_STANDBY = 1 << 2; - - /** - * Flag to indicate that an app is subject to Restricted mode restrictions that would - * result in its network access being blocked. - * - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static final int BLOCKED_REASON_RESTRICTED_MODE = 1 << 3; - - /** - * Flag to indicate that an app is subject to Data saver restrictions that would - * result in its metered network access being blocked. - * - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static final int BLOCKED_METERED_REASON_DATA_SAVER = 1 << 16; - - /** - * Flag to indicate that an app is subject to user restrictions that would - * result in its metered network access being blocked. - * - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 1 << 17; - - /** - * Flag to indicate that an app is subject to Device admin restrictions that would - * result in its metered network access being blocked. - * - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 1 << 18; - - /** @hide */ - public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000; - - /** * Flag to indicate that app is not exempt from any network restrictions. * * @hide @@ -344,22 +269,6 @@ public class NetworkPolicyManager { /** @hide */ public static final int ALLOWED_METERED_REASON_MASK = 0xffff0000; - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = {"BLOCKED_"}, value = { - BLOCKED_REASON_NONE, - BLOCKED_REASON_BATTERY_SAVER, - BLOCKED_REASON_DOZE, - BLOCKED_REASON_APP_STANDBY, - BLOCKED_REASON_RESTRICTED_MODE, - BLOCKED_METERED_REASON_DATA_SAVER, - BLOCKED_METERED_REASON_USER_RESTRICTED, - BLOCKED_METERED_REASON_ADMIN_DISABLED, - }) - public @interface BlockedReason {} - private final Context mContext; @UnsupportedAppUsage private INetworkPolicyManager mService; @@ -876,35 +785,6 @@ public class NetworkPolicyManager { } /** - * Returns whether network access of an UID is blocked or not based on {@code blockedReasons} - * corresponding to it. - * - * {@code blockedReasons} would be a bitwise {@code OR} combination of the - * {@code BLOCKED_REASON_*} and/or {@code BLOCKED_METERED_REASON_*} constants. - * - * @param blockedReasons Value indicating the reasons for why the network access of an UID is - * blocked. If the value is equal to {@link #BLOCKED_REASON_NONE}, then - * it indicates that an app's network access is not blocked. - * @param meteredNetwork Value indicating whether the network is metered or not. - * @return Whether network access is blocked or not. - * @hide - */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public static boolean isUidBlocked(@BlockedReason int blockedReasons, boolean meteredNetwork) { - if (blockedReasons == BLOCKED_REASON_NONE) { - return false; - } - final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK); - if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) { - return true; - } - if (meteredNetwork) { - return blockedReasons != BLOCKED_REASON_NONE; - } - return false; - } - - /** * Returns the {@code string} representation of {@code blockedReasons} argument. * * @param blockedReasons Value indicating the reasons for why the network access of an UID is @@ -913,7 +793,7 @@ public class NetworkPolicyManager { */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull - public static String blockedReasonsToString(@BlockedReason int blockedReasons) { + public static String blockedReasonsToString(int blockedReasons) { return DebugUtils.flagsToString(NetworkPolicyManager.class, "BLOCKED_", blockedReasons); } @@ -977,7 +857,7 @@ public class NetworkPolicyManager { * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - default void onUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {} + default void onUidBlockedReasonChanged(int uid, int blockedReasons) {} } /** @hide */ @@ -992,8 +872,7 @@ public class NetworkPolicyManager { } @Override - public void onBlockedReasonChanged(int uid, @BlockedReason int oldBlockedReasons, - @BlockedReason int newBlockedReasons) { + public void onBlockedReasonChanged(int uid, int oldBlockedReasons, int newBlockedReasons) { if (oldBlockedReasons != newBlockedReasons) { dispatchOnUidBlockedReasonChanged(mExecutor, mCallback, uid, newBlockedReasons); } @@ -1001,7 +880,7 @@ public class NetworkPolicyManager { } private static void dispatchOnUidBlockedReasonChanged(@Nullable Executor executor, - @NonNull NetworkPolicyCallback callback, int uid, @BlockedReason int blockedReasons) { + @NonNull NetworkPolicyCallback callback, int uid, int blockedReasons) { if (executor == null) { callback.onUidBlockedReasonChanged(uid, blockedReasons); } else { diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl index 4078b249218c..74c3ba44b69e 100644 --- a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl +++ b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl @@ -23,6 +23,6 @@ package android.net.netstats.provider; */ oneway interface INetworkStatsProvider { void onRequestStatsUpdate(int token); - void onSetLimit(String iface, long quotaBytes); void onSetAlert(long quotaBytes); + void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes); } diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl index bd336dd348fe..7eaa01e262fe 100644 --- a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl +++ b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl @@ -26,6 +26,6 @@ import android.net.NetworkStats; oneway interface INetworkStatsProviderCallback { void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats); void notifyAlertReached(); - void notifyLimitReached(); + void notifyWarningOrLimitReached(); void unregister(); } diff --git a/core/java/android/net/netstats/provider/NetworkStatsProvider.java b/core/java/android/net/netstats/provider/NetworkStatsProvider.java index 7639d2244cfe..23fc06927ef9 100644 --- a/core/java/android/net/netstats/provider/NetworkStatsProvider.java +++ b/core/java/android/net/netstats/provider/NetworkStatsProvider.java @@ -29,7 +29,8 @@ import android.os.RemoteException; @SystemApi public abstract class NetworkStatsProvider { /** - * A value used by {@link #onSetLimit} and {@link #onSetAlert} indicates there is no limit. + * A value used by {@link #onSetLimit}, {@link #onSetAlert} and {@link #onSetWarningAndLimit} + * indicates there is no limit. */ public static final int QUOTA_UNLIMITED = -1; @@ -42,13 +43,13 @@ public abstract class NetworkStatsProvider { } @Override - public void onSetLimit(String iface, long quotaBytes) { - NetworkStatsProvider.this.onSetLimit(iface, quotaBytes); + public void onSetAlert(long quotaBytes) { + NetworkStatsProvider.this.onSetAlert(quotaBytes); } @Override - public void onSetAlert(long quotaBytes) { - NetworkStatsProvider.this.onSetAlert(quotaBytes); + public void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes) { + NetworkStatsProvider.this.onSetWarningAndLimit(iface, warningBytes, limitBytes); } }; @@ -145,11 +146,25 @@ public abstract class NetworkStatsProvider { } /** - * Notify system that the quota set by {@code onSetLimit} has been reached. + * Notify system that the warning set by {@link #onSetWarningAndLimit} has been reached. + */ + public void notifyWarningReached() { + try { + // Reuse the code path to notify warning reached with limit reached + // since framework handles them in the same way. + getProviderCallbackBinderOrThrow().notifyWarningOrLimitReached(); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** + * Notify system that the quota set by {@link #onSetLimit} or limit set by + * {@link #onSetWarningAndLimit} has been reached. */ public void notifyLimitReached() { try { - getProviderCallbackBinderOrThrow().notifyLimitReached(); + getProviderCallbackBinderOrThrow().notifyWarningOrLimitReached(); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } @@ -183,6 +198,28 @@ public abstract class NetworkStatsProvider { public abstract void onSetLimit(@NonNull String iface, long quotaBytes); /** + * Called by {@code NetworkStatsService} when setting the interface quotas for the specified + * upstream interface. If a provider implements {@link #onSetWarningAndLimit}, the system + * will not call {@link #onSetLimit}. When this method is called, the implementation + * should behave as follows: + * 1. If {@code warningBytes} is reached on {@code iface}, block all further traffic on + * {@code iface} and call {@link NetworkStatsProvider@notifyWarningReached()}. + * 2. If {@code limitBytes} is reached on {@code iface}, block all further traffic on + * {@code iface} and call {@link NetworkStatsProvider#notifyLimitReached()}. + * + * @param iface the interface requiring the operation. + * @param warningBytes the warning defined as the number of bytes, starting from zero and + * counting from now. A value of {@link #QUOTA_UNLIMITED} indicates + * there is no warning. + * @param limitBytes the limit defined as the number of bytes, starting from zero and counting + * from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit. + */ + public void onSetWarningAndLimit(@NonNull String iface, long warningBytes, long limitBytes) { + // Backward compatibility for those who didn't override this function. + onSetLimit(iface, limitBytes); + } + + /** * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations * MUST call {@link NetworkStatsProvider#notifyAlertReached()} when {@code quotaBytes} bytes * have been reached. Unlike {@link #onSetLimit(String, long)}, the custom implementation should diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl index 6a3cb42ed75d..5b79f7311b6d 100644 --- a/core/java/android/net/vcn/IVcnManagementService.aidl +++ b/core/java/android/net/vcn/IVcnManagementService.aidl @@ -29,7 +29,7 @@ import android.os.ParcelUuid; */ interface IVcnManagementService { void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName); - void clearVcnConfig(in ParcelUuid subscriptionGroup); + void clearVcnConfig(in ParcelUuid subscriptionGroup, in String opPkgName); void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index b73fdbff8ef3..abd41dacdeb6 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -154,7 +154,7 @@ public class VcnManager { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); try { - mService.clearVcnConfig(subscriptionGroup); + mService.clearVcnConfig(subscriptionGroup, mContext.getOpPackageName()); } catch (ServiceSpecificException e) { throw new IOException(e); } catch (RemoteException e) { @@ -439,7 +439,7 @@ public class VcnManager { * @param statusCode the code for the status change encountered by this {@link * VcnStatusCallback}'s subscription group. */ - public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode); + public abstract void onStatusChanged(@VcnStatusCode int statusCode); /** * Invoked when a VCN Gateway Connection corresponding to this callback's subscription group @@ -476,7 +476,7 @@ public class VcnManager { * and there is a VCN active for its specified subscription group (this may happen after the * callback is registered). * - * <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the + * <p>{@link VcnStatusCallback#onStatusChanged(int)} will be invoked on registration with the * current status for the specified subscription group's VCN. If the registrant is not * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be * returned. @@ -580,7 +580,7 @@ public class VcnManager { @Override public void onVcnStatusChanged(@VcnStatusCode int statusCode) { Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode))); + () -> mExecutor.execute(() -> mCallback.onStatusChanged(statusCode))); } // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling' diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index bc3d5c4ab1ac..11445e9aec5a 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -72,7 +72,7 @@ interface INfcAdapter boolean deviceSupportsNfcSecure(); boolean setNfcSecure(boolean enable); - boolean setAlwaysOn(boolean value); - boolean isAlwaysOnEnabled(); - boolean isAlwaysOnSupported(); + boolean setControllerAlwaysOn(boolean value); + boolean isControllerAlwaysOn(); + boolean isControllerAlwaysOnSupported(); } diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index e85eb935a8e7..eed2c77b06b9 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -2254,13 +2254,13 @@ public final class NfcAdapter { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean setAlwaysOn(boolean value) { + @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) + public boolean setControllerAlwaysOn(boolean value) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } try { - return sService.setAlwaysOn(value); + return sService.setControllerAlwaysOn(value); } catch (RemoteException e) { attemptDeadServiceRecovery(e); // Try one more time @@ -2269,7 +2269,7 @@ public final class NfcAdapter { return false; } try { - return sService.setAlwaysOn(value); + return sService.setControllerAlwaysOn(value); } catch (RemoteException ee) { Log.e(TAG, "Failed to recover NFC Service."); } @@ -2286,10 +2286,10 @@ public final class NfcAdapter { */ @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean isAlwaysOnEnabled() { + @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) + public boolean isControllerAlwaysOn() { try { - return sService.isAlwaysOnEnabled(); + return sService.isControllerAlwaysOn(); } catch (RemoteException e) { attemptDeadServiceRecovery(e); // Try one more time @@ -2298,7 +2298,7 @@ public final class NfcAdapter { return false; } try { - return sService.isAlwaysOnEnabled(); + return sService.isControllerAlwaysOn(); } catch (RemoteException ee) { Log.e(TAG, "Failed to recover NFC Service."); } @@ -2315,13 +2315,13 @@ public final class NfcAdapter { */ @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean isAlwaysOnSupported() { + @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) + public boolean isControllerAlwaysOnSupported() { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } try { - return sService.isAlwaysOnSupported(); + return sService.isControllerAlwaysOnSupported(); } catch (RemoteException e) { attemptDeadServiceRecovery(e); // Try one more time @@ -2330,7 +2330,7 @@ public final class NfcAdapter { return false; } try { - return sService.isAlwaysOnSupported(); + return sService.isControllerAlwaysOnSupported(); } catch (RemoteException ee) { Log.e(TAG, "Failed to recover NFC Service."); } diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 6425c250f317..f5130bcf3088 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -543,6 +543,16 @@ public class Binder implements IBinder { public final native void markVintfStability(); /** + * Use a VINTF-stability binder w/o VINTF requirements. Should be called + * on a binder before it is sent out of process. + * + * This must be called before the object is sent to another process. + * + * @hide + */ + public final native void forceDowngradeToSystemStability(); + + /** * Flush any Binder commands pending in the current thread to the kernel * driver. This can be * useful to call before performing an operation that may block for a long diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 9d16f18ab848..9b29fb1dfaac 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -127,6 +127,7 @@ public class Process { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @TestApi public static final int NFC_UID = 1027; /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6d3b58c7db20..385a815798dc 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1009,8 +1009,8 @@ public final class Settings { * Output: Nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_MANAGE_ALL_SUBSCRIPTIONS_SETTINGS = - "android.settings.MANAGE_ALL_SUBSCRIPTIONS_SETTINGS"; + public static final String ACTION_MANAGE_ALL_SIM_PROFILES_SETTINGS = + "android.settings.MANAGE_ALL_SIM_PROFILES_SETTINGS"; /** * Activity Action: Show screen for controlling which apps can draw on top of other apps. diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 753abea2cfe2..a3a910acae63 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4557,6 +4557,15 @@ public final class Telephony { public static final String VOICE_REG_STATE = "voice_reg_state"; /** + * An integer value indicating the current data service state. + * <p> + * Valid values: {@link ServiceState#STATE_IN_SERVICE}, + * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY}, + * {@link ServiceState#STATE_POWER_OFF}. + */ + public static final String DATA_REG_STATE = "data_reg_state"; + + /** * The current registered operator numeric id. * <p> * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit @@ -4572,6 +4581,24 @@ public final class Telephony { * This is the same as {@link ServiceState#getIsManualSelection()}. */ public static final String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection"; + + /** + * The current data network type. + * <p> + * This is the same as {@link TelephonyManager#getDataNetworkType()}. + */ + public static final String DATA_NETWORK_TYPE = "data_network_type"; + + /** + * An integer value indicating the current duplex mode if the radio technology is LTE, + * LTE-CA or NR. + * <p> + * Valid values: {@link ServiceState#DUPLEX_MODE_UNKNOWN}, + * {@link ServiceState#DUPLEX_MODE_FDD}, {@link ServiceState#DUPLEX_MODE_TDD}. + * <p> + * This is the same as {@link ServiceState#getDuplexMode()}. + */ + public static final String DUPLEX_MODE = "duplex_mode"; } /** diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java index 8c771baaea37..3d603737c48c 100644 --- a/core/java/android/uwb/AngleMeasurement.java +++ b/core/java/android/uwb/AngleMeasurement.java @@ -48,7 +48,10 @@ public final class AngleMeasurement implements Parcelable { * @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of * allowed range */ - public AngleMeasurement(double radians, double errorRadians, double confidenceLevel) { + public AngleMeasurement( + @FloatRange(from = -Math.PI, to = +Math.PI) double radians, + @FloatRange(from = 0.0, to = +Math.PI) double errorRadians, + @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) { if (radians < -Math.PI || radians > Math.PI) { throw new IllegalArgumentException("Invalid radians: " + radians); } diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index 31f6f6afdd53..4a1d68547f70 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -9,6 +9,7 @@ sumir@google.com ogunwale@google.com jjaggi@google.com roosa@google.com +jreck@google.com # Display per-file Display*.java = file:/services/core/java/com/android/server/display/OWNERS diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java index 1c222a73eabc..9a02b7b7aae9 100644 --- a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java +++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java @@ -40,8 +40,7 @@ public final class CompatibilityOverrideConfig implements Parcelable { overrides = new HashMap<>(); for (int i = 0; i < keyCount; i++) { long key = in.readLong(); - PackageOverride override = in.readParcelable(PackageOverride.class.getClassLoader()); - overrides.put(key, override); + overrides.put(key, PackageOverride.createFromParcel(in)); } } @@ -55,7 +54,7 @@ public final class CompatibilityOverrideConfig implements Parcelable { dest.writeInt(overrides.size()); for (Long key : overrides.keySet()) { dest.writeLong(key); - dest.writeParcelable(overrides.get(key), 0); + overrides.get(key).writeToParcel(dest); } } diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 60213e4411c3..78d1d22c401e 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -151,15 +151,23 @@ interface IPlatformCompat { void setOverrides(in CompatibilityChangeConfig overrides, in String packageName); /** - * Adds overrides to compatibility changes. + * Adds overrides to compatibility changes on release builds. * - * <p>Kills the app to allow the changes to take effect. + * <p>The caller to this API needs to hold + * {@code android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD} and all change ids + * in {@code overrides} need to annotated with {@link android.compat.annotation.Overridable}. + * + * A release build in this definition means that {@link android.os.Build#IS_DEBUGGABLE} needs to + * be {@code false}. + * + * <p>Note that this does not kill the app, and therefore overrides read from the app process + * will not be updated. Overrides read from the system process do 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 setOverridesFromInstaller(in CompatibilityOverrideConfig overrides, in String packageName); + void setOverridesOnReleaseBuilds(in CompatibilityOverrideConfig overrides, in String packageName); /** * Adds overrides to compatibility changes. diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 0e35c84fa226..965971d18241 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -60,7 +60,6 @@ interface ITelephonyRegistry { void notifyDataConnectionForSubscriber( int phoneId, int subId, in PreciseDataConnectionState preciseState); // Uses CellIdentity which is Parcelable here; will convert to CellLocation in client. - void notifyCellLocation(in CellIdentity cellLocation); void notifyCellLocationForSubscriber(in int subId, in CellIdentity cellLocation); @UnsupportedAppUsage void notifyCellInfo(in List<CellInfo> cellInfo); diff --git a/core/jni/android_net_NetworkUtils.cpp b/core/jni/android_net_NetworkUtils.cpp index a781a377694b..1cee8955a7a2 100644 --- a/core/jni/android_net_NetworkUtils.cpp +++ b/core/jni/android_net_NetworkUtils.cpp @@ -102,11 +102,6 @@ static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, job return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd)); } -static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId) -{ - return (jboolean) !queryUserAccess(uid, netId); -} - static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst) { if (env->GetArrayLength(addr) != len) { @@ -246,7 +241,6 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess }, { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution }, { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork }, - { "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 }, { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow }, diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 249950419441..e4dddd277b0b 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -486,9 +486,15 @@ public: } void markVintf() { + AutoMutex _l(mLock); mVintf = true; } + void forceDowngradeToSystemStability() { + AutoMutex _l(mLock); + mVintf = false; + } + sp<IBinder> getExtension() { AutoMutex _l(mLock); sp<JavaBBinder> b = mBinder.promote(); @@ -1013,6 +1019,12 @@ static void android_os_Binder_markVintfStability(JNIEnv* env, jobject clazz) { jbh->markVintf(); } +static void android_os_Binder_forceDowngradeToSystemStability(JNIEnv* env, jobject clazz) { + JavaBBinderHolder* jbh = + (JavaBBinderHolder*) env->GetLongField(clazz, gBinderOffsets.mObject); + jbh->forceDowngradeToSystemStability(); +} + static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz) { IPCThreadState::self()->flushCommands(); @@ -1076,6 +1088,7 @@ static const JNINativeMethod gBinderMethods[] = { { "clearCallingWorkSource", "()J", (void*)android_os_Binder_clearCallingWorkSource }, { "restoreCallingWorkSource", "(J)V", (void*)android_os_Binder_restoreCallingWorkSource }, { "markVintfStability", "()V", (void*)android_os_Binder_markVintfStability}, + { "forceDowngradeToSystemStability", "()V", (void*)android_os_Binder_forceDowngradeToSystemStability}, { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands }, { "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder }, { "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 61e7d0ad9a73..1f7695eb7fc0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1882,6 +1882,12 @@ android:label="@string/permlab_preferredPaymentInfo" android:protectionLevel="normal" /> + <!-- @SystemApi Allows access to set NFC controller always on states. + <p>Protection level: signature|privileged + @hide --> + <permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs. Applications holding this permission can access OMAPI reset system API and bypass OMAPI AccessControlEnforcer. @@ -2545,10 +2551,6 @@ <permission android:name="android.permission.CREATE_USERS" android:protectionLevel="signature" /> - <!-- @TestApi @hide Allows an application to query user info for all users on the device. --> - <permission android:name="android.permission.QUERY_USERS" - android:protectionLevel="signature" /> - <!-- @hide Allows an application to set the profile owners and the device owner. This permission is not available to third party applications.--> <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" @@ -2556,6 +2558,16 @@ android:label="@string/permlab_manageProfileAndDeviceOwners" android:description="@string/permdesc_manageProfileAndDeviceOwners" /> + <!-- @TestApi @hide Allows an application to reset the record of previous system update freeze + periods. --> + <permission android:name="android.permission.CLEAR_FREEZE_PERIOD" + android:protectionLevel="signature" /> + + <!-- @TestApi @hide Allows an application to force available DevicePolicyManager logs to + DPC. --> + <permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" + android:protectionLevel="signature" /> + <!-- Allows an application to get full detailed information about recently running tasks, with full fidelity to the real state. @hide --> @@ -4101,6 +4113,11 @@ <permission android:name="android.permission.MODIFY_AUDIO_ROUTING" android:protectionLevel="signature|privileged" /> + <!-- @TestApi Allows an application to query audio related state. + @hide --> + <permission android:name="android.permission.QUERY_AUDIO_STATE" + android:protectionLevel="signature" /> + <!-- Allows an application to modify what effects are applied to all audio (matching certain criteria) from any application. <p>Not for use by third-party applications.</p> @@ -4578,6 +4595,11 @@ <permission android:name="android.permission.SET_INITIAL_LOCK" android:protectionLevel="signature|setup"/> + <!-- @TestApi Allows applications to set and verify lockscreen credentials. + @hide --> + <permission android:name="android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS" + android:protectionLevel="signature"/> + <!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide --> <permission android:name="android.permission.MANAGE_FINGERPRINT" android:protectionLevel="signature|privileged" /> @@ -5123,9 +5145,16 @@ <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" android:protectionLevel="signature|privileged" /> <!-- Allows an app to override compat change config. + This permission only allows to override config on debuggable builds or test-apks and is + therefore a less powerful version of OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD. @hide <p>Not for use by third-party applications.</p> --> <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an app to override compat change config on release builds. + Only ChangeIds that are annotated as @Overridable can be overridden on release builds. + @hide --> + <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD" + android:protectionLevel="signature|privileged" /> <!-- Allows input events to be monitored. Very dangerous! @hide --> <permission android:name="android.permission.MONITOR_INPUT" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0f6f15de6458..bfe7802726a3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3415,10 +3415,6 @@ <!-- True if assistant app should be pinned via Pinner Service --> <bool name="config_pinnerAssistantApp">false</bool> - <!-- List of files pinned by the Pinner Service with the JIT Zygote boot image b/119800099 --> - <string-array translatable="false" name="config_jitzygoteBootImagePinnerServiceFiles"> - </string-array> - <!-- Number of days preloaded file cache should be preserved on a device before it can be deleted --> <integer name="config_keepPreloadsMinDays">7</integer> @@ -4343,6 +4339,11 @@ check after reboot or airplane mode toggling --> <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool> + <!-- Boolean indicating whether all CB messages should be disabled on this device. This config + is intended to be used by OEMs who need to disable CB messages for regulatory requirements, + (e.g. the device is a tablet in a country where tablets should not receive CB messages) --> + <bool translatable="false" name="config_disable_all_cb_messages">false</bool> + <!-- Screen Wake Keys Determines whether the specified key groups can be used to wake up the device. --> <bool name="config_wakeOnDpadKeyPress">true</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ad24b79779d4..2901de5b57a2 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3062,7 +3062,6 @@ <java-symbol type="bool" name="config_pinnerCameraApp" /> <java-symbol type="bool" name="config_pinnerHomeApp" /> <java-symbol type="bool" name="config_pinnerAssistantApp" /> - <java-symbol type="array" name="config_jitzygoteBootImagePinnerServiceFiles" /> <java-symbol type="string" name="config_doubleTouchGestureEnableFile" /> @@ -3848,6 +3847,7 @@ <java-symbol type="layout" name="chooser_action_button" /> <java-symbol type="dimen" name="chooser_action_button_icon_size" /> <java-symbol type="string" name="config_defaultNearbySharingComponent" /> + <java-symbol type="bool" name="config_disable_all_cb_messages" /> <java-symbol type="drawable" name="ic_close" /> <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" /> diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS index c61a4b538a44..0b945895ad7f 100644 --- a/core/tests/coretests/src/android/content/OWNERS +++ b/core/tests/coretests/src/android/content/OWNERS @@ -2,3 +2,5 @@ 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 *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS +per-file ComponentCallbacksControllerTest = file:/services/core/java/com/android/server/wm/OWNERS +per-file ComponentCallbacksControllerTest = charlesccchen@google.com diff --git a/data/etc/OWNERS b/data/etc/OWNERS index 549e074d297c..5aacfddab28c 100644 --- a/data/etc/OWNERS +++ b/data/etc/OWNERS @@ -13,3 +13,4 @@ toddke@google.com yamasani@google.com per-file preinstalled-packages* = file:/MULTIUSER_OWNERS +per-file services.core.protolog.json = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 653e690506bc..009421b05b34 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -444,6 +444,8 @@ applications that come with the platform <permission name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" /> <!-- Permissions required for CTS test - CtsHdmiCecHostTestCases --> <permission name="android.permission.HDMI_CEC"/> + <!-- Permission required for CTS test - MediaPlayerTest --> + <permission name="android.permission.BIND_IMS_SERVICE" /> <!-- Permission needed for CTS test - WifiManagerTest --> <permission name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" /> <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" /> diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index fe5c1be05927..d048efcfd12c 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -1196,13 +1196,11 @@ public class Typeface { /** @hide */ public boolean isSupportedAxes(int axis) { - if (mSupportedAxes == null) { - synchronized (this) { + synchronized (this) { + if (mSupportedAxes == null) { + mSupportedAxes = nativeGetSupportedAxes(native_instance); if (mSupportedAxes == null) { - mSupportedAxes = nativeGetSupportedAxes(native_instance); - if (mSupportedAxes == null) { - mSupportedAxes = EMPTY_AXES; - } + mSupportedAxes = EMPTY_AXES; } } } diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java index 35b1c169f283..72cea0cacd12 100644 --- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -139,4 +139,18 @@ public class AndroidKeyStoreMaintenance { return SYSTEM_ERROR; } } + + /** + * Informs Keystore 2.0 that an off body event was detected. + */ + public static void onDeviceOffBody() { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return; + try { + getService().onDeviceOffBody(); + } catch (Exception e) { + // TODO This fails open. This is not a regression with respect to keystore1 but it + // should get fixed. + Log.e(TAG, "Error while reporting device off body event.", e); + } + } } diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java new file mode 100644 index 000000000000..a1a7aa85519f --- /dev/null +++ b/keystore/java/android/security/GenerateRkpKey.java @@ -0,0 +1,99 @@ +/* + * 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; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * GenerateKey is a helper class to handle interactions between Keystore and the RemoteProvisioner + * app. There are two cases where Keystore should use this class. + * + * (1) : An app generates a new attested key pair, so Keystore calls notifyKeyGenerated to let the + * RemoteProvisioner app check if the state of the attestation key pool is getting low enough + * to warrant provisioning more attestation certificates early. + * + * (2) : An app attempts to generate a new key pair, but the keystore service discovers it is out of + * attestation key pairs and cannot provide one for the given application. Keystore can then + * make a blocking call on notifyEmpty to allow the RemoteProvisioner app to get another + * attestation certificate chain provisioned. + * + * In most cases, the proper usage of (1) should preclude the need for (2). + * + * @hide + */ +public class GenerateRkpKey { + + private IGenerateRkpKeyService mBinder; + private Context mContext; + + private ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mBinder = IGenerateRkpKeyService.Stub.asInterface(service); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + mBinder = null; + } + }; + + /** + * Constructor which takes a Context object. + */ + public GenerateRkpKey(Context context) { + mContext = context; + } + + /** + * Fulfills the use case of (2) described in the class documentation. Blocks until the + * RemoteProvisioner application can get new attestation keys signed by the server. + */ + public void notifyEmpty(int securityLevel) throws RemoteException { + Intent intent = new Intent(IGenerateRkpKeyService.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) { + throw new RemoteException("Failed to bind to GenerateKeyService"); + } + if (mBinder != null) { + mBinder.generateKey(securityLevel); + } + mContext.unbindService(mConnection); + } + + /** + * FUlfills the use case of (1) described in the class documentation. Non blocking call. + */ + public void notifyKeyGenerated(int securityLevel) throws RemoteException { + Intent intent = new Intent(IGenerateRkpKeyService.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) { + throw new RemoteException("Failed to bind to GenerateKeyService"); + } + if (mBinder != null) { + mBinder.notifyKeyGenerated(securityLevel); + } + mContext.unbindService(mConnection); + } +} diff --git a/keystore/java/android/security/GenerateRkpKeyException.java b/keystore/java/android/security/GenerateRkpKeyException.java new file mode 100644 index 000000000000..a2d65e4e7119 --- /dev/null +++ b/keystore/java/android/security/GenerateRkpKeyException.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * Thrown on problems in attempting to attest to a key using a remotely provisioned key. + * + * @hide + */ +public class GenerateRkpKeyException extends Exception { + + /** + * Constructs a new {@code GenerateRkpKeyException}. + */ + public GenerateRkpKeyException() { + } +} diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl new file mode 100644 index 000000000000..5f1d6693c23a --- /dev/null +++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl @@ -0,0 +1,36 @@ +/** + * 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; + +/** + * Interface to allow the framework to notify the RemoteProvisioner app when keys are empty. This + * will be used if Keystore replies with an error code NO_KEYS_AVAILABLE in response to an + * attestation request. The framework can then synchronously call generateKey() to get more + * attestation keys generated and signed. Upon return, the caller can be certain an attestation key + * is available. + * + * @hide + */ +interface IGenerateRkpKeyService { + /** + * Ping the provisioner service to let it know an app generated a key. This may or may not have + * consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check. + */ + oneway void notifyKeyGenerated(in int securityLevel); + /** Ping the provisioner service to indicate there are no remaining attestation keys left. */ + void generateKey(in int securityLevel); +} diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index a08f390c9fd3..b05149ef75bc 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -1204,6 +1204,7 @@ public class KeyStore { * Notify keystore that the device went off-body. */ public void onDeviceOffBody() { + AndroidKeyStoreMaintenance.onDeviceOffBody(); try { mBinder.onDeviceOffBody(); } catch (RemoteException e) { diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index 6ac3821d0f9c..df579bba9dc2 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -18,8 +18,7 @@ package android.security; import android.annotation.NonNull; import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; -import android.os.Build; +import android.compat.annotation.Disabled; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; @@ -86,7 +85,7 @@ public class KeyStore2 { * successfully conclude an operation. */ @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + @Disabled // See b/180133780 static final long KEYSTORE_OPERATION_CREATION_MAY_FAIL = 169897160L; // Never use mBinder directly, use KeyStore2.getService() instead or better yet @@ -126,6 +125,8 @@ public class KeyStore2 { } } + private static final String KEYSTORE2_SERVICE_NAME = + "android.system.keystore2.IKeystoreService/default"; private KeyStore2() { mBinder = null; @@ -138,7 +139,7 @@ public class KeyStore2 { private synchronized IKeystoreService getService(boolean retryLookup) { if (mBinder == null || retryLookup) { mBinder = IKeystoreService.Stub.asInterface(ServiceManager - .getService("android.system.keystore2")); + .getService(KEYSTORE2_SERVICE_NAME)); } return mBinder; } diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 5cb2c3b41517..9ca551b26aab 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -288,7 +288,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048 private final String mKeystoreAlias; - private final int mNamespace; + private final @KeyProperties.Namespace int mNamespace; private final int mKeySize; private final AlgorithmParameterSpec mSpec; private final X500Principal mCertificateSubject; @@ -331,7 +331,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu */ public KeyGenParameterSpec( String keyStoreAlias, - int namespace, + @KeyProperties.Namespace int namespace, int keySize, AlgorithmParameterSpec spec, X500Principal certificateSubject, @@ -472,7 +472,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * @hide */ @SystemApi - public int getNamespace() { + public @KeyProperties.Namespace int getNamespace() { return mNamespace; } @@ -896,7 +896,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final String mKeystoreAlias; private @KeyProperties.PurposeEnum int mPurposes; - private int mNamespace = KeyProperties.NAMESPACE_APPLICATION; + private @KeyProperties.Namespace int mNamespace = KeyProperties.NAMESPACE_APPLICATION; private int mKeySize = -1; private AlgorithmParameterSpec mSpec; private X500Principal mCertificateSubject; @@ -1051,7 +1051,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu */ @SystemApi @NonNull - public Builder setNamespace(int namespace) { + public Builder setNamespace(@KeyProperties.Namespace int namespace) { mNamespace = namespace; return this; } diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index 7b0fa91380e1..682d12a65ea3 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -892,6 +892,22 @@ public abstract class KeyProperties { } /** + * Namespaces provide system developers and vendors with a way to use keystore without + * requiring an applications uid. Namespaces can be configured using SEPolicy. + * See <a href="https://source.android.com/security/keystore#access-control"> + * Keystore 2.0 access-control</a> + * {@See KeyGenParameterSpec.Builder#setNamespace} + * {@See android.security.keystore2.AndroidKeyStoreLoadStoreParameter} + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "NAMESPACE_" }, value = { + NAMESPACE_APPLICATION, + NAMESPACE_WIFI, + }) + public @interface Namespace {} + + /** * This value indicates the implicit keystore namespace of the calling application. * It is used by default. Only select system components can choose a different namespace * which it must be configured in SEPolicy. @@ -912,14 +928,12 @@ public abstract class KeyProperties { * For legacy support, translate namespaces into known UIDs. * @hide */ - public static int namespaceToLegacyUid(int namespace) { + public static int namespaceToLegacyUid(@Namespace int namespace) { switch (namespace) { case NAMESPACE_APPLICATION: return KeyStore.UID_SELF; case NAMESPACE_WIFI: return Process.WIFI_UID; - // TODO Translate WIFI and VPN UIDs once the namespaces are defined. - // b/171305388 and b/171305607 default: throw new IllegalArgumentException("No UID corresponding to namespace " + namespace); @@ -930,14 +944,12 @@ public abstract class KeyProperties { * For legacy support, translate namespaces into known UIDs. * @hide */ - public static int legacyUidToNamespace(int uid) { + public static @Namespace int legacyUidToNamespace(int uid) { switch (uid) { case KeyStore.UID_SELF: return NAMESPACE_APPLICATION; case Process.WIFI_UID: return NAMESPACE_WIFI; - // TODO Translate WIFI and VPN UIDs once the namespaces are defined. - // b/171305388 and b/171305607 default: throw new IllegalArgumentException("No namespace corresponding to uid " + uid); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index e401add9ece7..2d8901a37c05 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -24,6 +24,9 @@ import android.hardware.security.keymint.KeyPurpose; import android.hardware.security.keymint.SecurityLevel; import android.hardware.security.keymint.Tag; import android.os.Build; +import android.os.RemoteException; +import android.security.GenerateRkpKey; +import android.security.GenerateRkpKeyException; import android.security.KeyPairGeneratorSpec; import android.security.KeyStore; import android.security.KeyStore2; @@ -520,6 +523,18 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato @Override public KeyPair generateKeyPair() { + try { + return generateKeyPairHelper(); + } catch (GenerateRkpKeyException e) { + try { + return generateKeyPairHelper(); + } catch (GenerateRkpKeyException f) { + throw new ProviderException("Failed to provision new attestation keys."); + } + } + } + + private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException { if (mKeyStore == null || mSpec == null) { throw new IllegalStateException("Not initialized"); } @@ -557,13 +572,30 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato AndroidKeyStorePublicKey publicKey = AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse( descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm); - + GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext()); + try { + if (mSpec.getAttestationChallenge() != null) { + keyGen.notifyKeyGenerated(securityLevel); + } + } catch (RemoteException e) { + // This is not really an error state, and necessarily does not apply to non RKP + // systems or hybrid systems where RKP is not currently turned on. + Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend."); + } success = true; return new KeyPair(publicKey, publicKey.getPrivateKey()); } catch (android.security.KeyStoreException e) { switch(e.getErrorCode()) { case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE: throw new StrongBoxUnavailableException("Failed to generated key pair.", e); + case ResponseCode.OUT_OF_KEYS: + GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext()); + try { + keyGen.notifyEmpty(securityLevel); + } catch (RemoteException f) { + throw new ProviderException("Failed to talk to RemoteProvisioner", f); + } + throw new GenerateRkpKeyException(); default: ProviderException p = new ProviderException("Failed to generate key pair.", e); if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java index 0c6744f9822c..25b1c864b5d1 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreLoadStoreParameter.java @@ -16,6 +16,8 @@ package android.security.keystore2; +import android.security.keystore.KeyProperties; + import java.security.KeyStore; import java.security.KeyStore.ProtectionParameter; @@ -24,9 +26,9 @@ import java.security.KeyStore.ProtectionParameter; */ public class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter { - private final int mNamespace; + private final @KeyProperties.Namespace int mNamespace; - public AndroidKeyStoreLoadStoreParameter(int namespace) { + public AndroidKeyStoreLoadStoreParameter(@KeyProperties.Namespace int namespace) { mNamespace = namespace; } @@ -35,7 +37,7 @@ public class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStorePara return null; } - int getNamespace() { + @KeyProperties.Namespace int getNamespace() { return mNamespace; } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 39607aeb3852..32f98a2538f3 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -100,7 +100,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { public static final String NAME = "AndroidKeyStore"; private KeyStore2 mKeyStore; - private int mNamespace = KeyProperties.NAMESPACE_APPLICATION; + private @KeyProperties.Namespace int mNamespace = KeyProperties.NAMESPACE_APPLICATION; @Override public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, @@ -1125,7 +1125,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { @Override public void engineLoad(LoadStoreParameter param) throws IOException, NoSuchAlgorithmException, CertificateException { - int namespace = KeyProperties.NAMESPACE_APPLICATION; + @KeyProperties.Namespace int namespace = KeyProperties.NAMESPACE_APPLICATION; if (param != null) { if (param instanceof AndroidKeyStoreLoadStoreParameter) { namespace = ((AndroidKeyStoreLoadStoreParameter) param).getNamespace(); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 0780c6875eb5..5633236a122e 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -45,6 +45,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -4411,14 +4412,25 @@ final public class MediaCodec { int i = 0; for (final String key: params.keySet()) { - keys[i] = key; - Object value = params.get(key); - - // Bundle's byte array is a byte[], JNI layer only takes ByteBuffer - if (value instanceof byte[]) { - values[i] = ByteBuffer.wrap((byte[])value); + if (key.equals(MediaFormat.KEY_AUDIO_SESSION_ID)) { + int sessionId = 0; + try { + sessionId = (Integer)params.get(key); + } catch (Exception e) { + throw new IllegalArgumentException("Wrong Session ID Parameter!"); + } + keys[i] = "audio-hw-sync"; + values[i] = AudioSystem.getAudioHwSyncForSession(sessionId); } else { - values[i] = value; + keys[i] = key; + Object value = params.get(key); + + // Bundle's byte array is a byte[], JNI layer only takes ByteBuffer + if (value instanceof byte[]) { + values[i] = ByteBuffer.wrap((byte[])value); + } else { + values[i] = value; + } } ++i; } @@ -4554,6 +4566,128 @@ final public class MediaCodec { private native void native_enableOnFrameRenderedListener(boolean enable); + /** + * Returns a list of vendor parameter names. + * <p> + * This method can be called in any codec state except for released state. + * + * @return a list containing supported vendor parameters; an empty + * list if no vendor parameters are supported. The order of the + * parameters is arbitrary. + * @throws IllegalStateException if in the Released state. + */ + @NonNull + public List<String> getSupportedVendorParameters() { + return native_getSupportedVendorParameters(); + } + + @NonNull + private native List<String> native_getSupportedVendorParameters(); + + /** + * Contains description of a parameter. + */ + public static class ParameterDescriptor { + private ParameterDescriptor() {} + + /** + * Returns the name of the parameter. + */ + @NonNull + public String getName() { + return mName; + } + + /** + * Returns the type of the parameter. + * {@link MediaFormat#TYPE_NULL} is never returned. + */ + @MediaFormat.Type + public int getType() { + return mType; + } + + private String mName; + private @MediaFormat.Type int mType; + } + + /** + * Describe a parameter with the name. + * <p> + * This method can be called in any codec state except for released state. + * + * @param name name of the parameter to describe, typically one from + * {@link #getSupportedVendorParameters}. + * @return {@link ParameterDescriptor} object that describes the parameter. + * {@code null} if unrecognized / not able to describe. + * @throws IllegalStateException if in the Released state. + */ + @Nullable + public ParameterDescriptor getParameterDescriptor(@NonNull String name) { + return native_getParameterDescriptor(name); + } + + @Nullable + private native ParameterDescriptor native_getParameterDescriptor(@NonNull String name); + + /** + * Subscribe to vendor parameters, so that changes to these parameters generate + * output format change event. + * <p> + * Unrecognized parameter names or standard (non-vendor) parameter names will be ignored. + * {@link #reset} also resets the list of subscribed parameters. + * If a parameter in {@code names} is already subscribed, it will remain subscribed. + * <p> + * This method can be called in any codec state except for released state. When called in + * running state with newly subscribed parameters, it takes effect no later than the + * processing of the subsequently queued buffer. For the new parameters, the codec will generate + * output format change event. + * <p> + * Note that any vendor parameters set in a {@link #configure} or + * {@link #setParameters} call are automatically subscribed. + * <p> + * See also {@link #INFO_OUTPUT_FORMAT_CHANGED} or {@link Callback#onOutputFormatChanged} + * for output format change events. + * + * @param names names of the vendor parameters to subscribe. This may be an empty list, + * and in that case this method will not change the list of subscribed parameters. + * @throws IllegalStateException if in the Released state. + */ + public void subscribeToVendorParameters(@NonNull List<String> names) { + native_subscribeToVendorParameters(names); + } + + private native void native_subscribeToVendorParameters(@NonNull List<String> names); + + /** + * Unsubscribe from vendor parameters, so that changes to these parameters + * no longer generate output format change event. + * <p> + * Unrecognized parameter names, standard (non-vendor) parameter names will be ignored. + * {@link #reset} also resets the list of subscribed parameters. + * If a parameter in {@code names} is already unsubscribed, it will remain unsubscribed. + * <p> + * This method can be called in any codec state except for released state. When called in + * running state with newly unsubscribed parameters, it takes effect no later than the + * processing of the subsequently queued buffer. + * <p> + * Note that any vendor parameters set in a {@link #configure} or + * {@link #setParameters} call are automatically subscribed, and with this method + * they can be unsubscribed. + * <p> + * See also {@link #INFO_OUTPUT_FORMAT_CHANGED} or {@link Callback#onOutputFormatChanged} + * for output format change events. + * + * @param names names of the vendor parameters to unsubscribe. This may be an empty list, + * and in that case this method will not change the list of subscribed parameters. + * @throws IllegalStateException if in the Released state. + */ + public void unsubscribeFromVendorParameters(@NonNull List<String> names) { + native_unsubscribeFromVendorParameters(names); + } + + private native void native_unsubscribeFromVendorParameters(@NonNull List<String> names); + private EventHandler getEventHandlerOn( @Nullable Handler handler, @NonNull EventHandler lastHandler) { if (handler == null) { diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 0b0e162d4faf..b3eb8ba77639 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -53,6 +53,7 @@ #include <media/MediaCodecBuffer.h> #include <media/hardware/VideoAPI.h> +#include <media/stagefright/CodecBase.h> #include <media/stagefright/MediaCodec.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> @@ -82,6 +83,16 @@ enum { EVENT_FRAME_RENDERED = 3, }; +// From MediaFormat.java +enum { + TYPE_NULL = 0, + TYPE_INTEGER = 1, + TYPE_LONG = 2, + TYPE_FLOAT = 3, + TYPE_STRING = 4, + TYPE_BYTE_BUFFER = 5, +}; + static struct CryptoErrorCodes { jint cryptoErrorNoKey; jint cryptoErrorKeyExpired; @@ -138,6 +149,8 @@ static struct { } gByteBufferInfo; static struct { + jclass clazz; + jmethodID ctorId; jmethodID sizeId; jmethodID getId; jmethodID addId; @@ -152,6 +165,13 @@ static struct { jfieldID lockId; } gLinearBlockInfo; +static struct { + jclass clazz; + jmethodID ctorId; + jfieldID nameId; + jfieldID typeId; +} gDescriptorInfo; + struct fields_t { jmethodID postEventFromNativeID; jmethodID lockAndGetContextID; @@ -922,6 +942,74 @@ void JMediaCodec::selectAudioPresentation(const int32_t presentationId, const in (void)mCodec->setParameters(msg); } +status_t JMediaCodec::querySupportedVendorParameters(JNIEnv *env, jobject *namesObj) { + std::vector<std::string> names; + status_t status = mCodec->querySupportedVendorParameters(&names); + if (status != OK) { + return status; + } + *namesObj = env->NewObject(gArrayListInfo.clazz, gArrayListInfo.ctorId); + for (const std::string &name : names) { + ScopedLocalRef<jstring> nameStr{env, env->NewStringUTF(name.c_str())}; + (void)env->CallBooleanMethod(*namesObj, gArrayListInfo.addId, nameStr.get()); + } + return OK; +} + +status_t JMediaCodec::describeParameter(JNIEnv *env, jstring name, jobject *descObj) { + const char *tmp = env->GetStringUTFChars(name, nullptr); + CodecParameterDescriptor desc; + status_t status = mCodec->describeParameter(tmp, &desc); + env->ReleaseStringUTFChars(name, tmp); + if (status != OK) { + return status; + } + jint type = TYPE_NULL; + switch (desc.type) { + case AMessage::kTypeInt32: type = TYPE_INTEGER; break; + case AMessage::kTypeSize: + case AMessage::kTypeInt64: type = TYPE_LONG; break; + case AMessage::kTypeFloat: type = TYPE_FLOAT; break; + case AMessage::kTypeString: type = TYPE_STRING; break; + case AMessage::kTypeBuffer: type = TYPE_BYTE_BUFFER; break; + default: type = TYPE_NULL; break; + } + if (type == TYPE_NULL) { + return BAD_VALUE; + } + *descObj = env->NewObject(gDescriptorInfo.clazz, gDescriptorInfo.ctorId); + env->SetObjectField(*descObj, gDescriptorInfo.nameId, name); + env->SetIntField(*descObj, gDescriptorInfo.typeId, type); + return OK; +} + +static void BuildVectorFromList(JNIEnv *env, jobject list, std::vector<std::string> *vec) { + ScopedLocalRef<jclass> listClazz{env, env->FindClass("java/util/List")}; + ScopedLocalRef<jclass> iterClazz{env, env->FindClass("java/util/Iterator")}; + jmethodID hasNextID = env->GetMethodID(iterClazz.get(), "hasNext", "()Z"); + jmethodID nextID = env->GetMethodID(iterClazz.get(), "next", "()Ljava/lang/Object;"); + jobject it = env->CallObjectMethod( + list, env->GetMethodID(listClazz.get(), "iterator", "()Ljava/util/Iterator;")); + while (env->CallBooleanMethod(it, hasNextID)) { + jstring name = (jstring)env->CallObjectMethod(it, nextID); + const char *tmp = env->GetStringUTFChars(name, nullptr); + vec->push_back(tmp); + env->ReleaseStringUTFChars(name, tmp); + } +} + +status_t JMediaCodec::subscribeToVendorParameters(JNIEnv *env, jobject namesObj) { + std::vector<std::string> names; + BuildVectorFromList(env, namesObj, &names); + return mCodec->subscribeToVendorParameters(names); +} + +status_t JMediaCodec::unsubscribeFromVendorParameters(JNIEnv *env, jobject namesObj) { + std::vector<std::string> names; + BuildVectorFromList(env, namesObj, &names); + return mCodec->unsubscribeFromVendorParameters(names); +} + static jthrowable createCodecException( JNIEnv *env, status_t err, int32_t actionCode, const char *msg = NULL) { ScopedLocalRef<jclass> clazz( @@ -2602,6 +2690,73 @@ static void android_media_MediaCodec_setAudioPresentation( codec->selectAudioPresentation((int32_t)presentationId, (int32_t)programId); } +static jobject android_media_MediaCodec_getSupportedVendorParameters( + JNIEnv *env, jobject thiz) { + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL || codec->initCheck() != OK) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return NULL; + } + + jobject ret = NULL; + status_t status = codec->querySupportedVendorParameters(env, &ret); + if (status != OK) { + throwExceptionAsNecessary(env, status); + } + + return ret; +} + +static jobject android_media_MediaCodec_getParameterDescriptor( + JNIEnv *env, jobject thiz, jstring name) { + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL || codec->initCheck() != OK) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return NULL; + } + + jobject ret = NULL; + status_t status = codec->describeParameter(env, name, &ret); + if (status != OK) { + ret = NULL; + } + return ret; +} + +static void android_media_MediaCodec_subscribeToVendorParameters( + JNIEnv *env, jobject thiz, jobject names) { + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL || codec->initCheck() != OK) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; + } + + status_t status = codec->subscribeToVendorParameters(env, names); + if (status != OK) { + throwExceptionAsNecessary(env, status); + } + return; +} + +static void android_media_MediaCodec_unsubscribeFromVendorParameters( + JNIEnv *env, jobject thiz, jobject names) { + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL || codec->initCheck() != OK) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return; + } + + status_t status = codec->unsubscribeFromVendorParameters(env, names); + if (status != OK) { + throwExceptionAsNecessary(env, status); + } + return; +} + static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { ScopedLocalRef<jclass> clazz( env, env->FindClass("android/media/MediaCodec")); @@ -2861,6 +3016,10 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { clazz.reset(env->FindClass("java/util/ArrayList")); CHECK(clazz.get() != NULL); + gArrayListInfo.clazz = (jclass)env->NewGlobalRef(clazz.get()); + + gArrayListInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V"); + CHECK(gArrayListInfo.ctorId != NULL); gArrayListInfo.sizeId = env->GetMethodID(clazz.get(), "size", "()I"); CHECK(gArrayListInfo.sizeId != NULL); @@ -2891,6 +3050,19 @@ static void android_media_MediaCodec_native_init(JNIEnv *env, jclass) { gLinearBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;"); CHECK(gLinearBlockInfo.lockId != NULL); + + clazz.reset(env->FindClass("android/media/MediaCodec$ParameterDescriptor")); + CHECK(clazz.get() != NULL); + gDescriptorInfo.clazz = (jclass)env->NewGlobalRef(clazz.get()); + + gDescriptorInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V"); + CHECK(gDescriptorInfo.ctorId != NULL); + + gDescriptorInfo.nameId = env->GetFieldID(clazz.get(), "mName", "Ljava/lang/String;"); + CHECK(gDescriptorInfo.nameId != NULL); + + gDescriptorInfo.typeId = env->GetFieldID(clazz.get(), "mType", "I"); + CHECK(gDescriptorInfo.typeId != NULL); } static void android_media_MediaCodec_native_setup( @@ -3217,6 +3389,21 @@ static const JNINativeMethod gMethods[] = { { "native_setAudioPresentation", "(II)V", (void *)android_media_MediaCodec_setAudioPresentation }, + { "native_getSupportedVendorParameters", "()Ljava/util/List;", + (void *)android_media_MediaCodec_getSupportedVendorParameters }, + + { "native_getParameterDescriptor", + "(Ljava/lang/String;)Landroid/media/MediaCodec$ParameterDescriptor;", + (void *)android_media_MediaCodec_getParameterDescriptor }, + + { "native_subscribeToVendorParameters", + "(Ljava/util/List;)V", + (void *)android_media_MediaCodec_subscribeToVendorParameters}, + + { "native_unsubscribeFromVendorParameters", + "(Ljava/util/List;)V", + (void *)android_media_MediaCodec_unsubscribeFromVendorParameters}, + { "native_init", "()V", (void *)android_media_MediaCodec_native_init }, { "native_setup", "(Ljava/lang/String;ZZ)V", diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index a58f9a74b563..33f481d746c1 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -162,6 +162,14 @@ struct JMediaCodec : public AHandler { void selectAudioPresentation(const int32_t presentationId, const int32_t programId); + status_t querySupportedVendorParameters(JNIEnv *env, jobject *names); + + status_t describeParameter(JNIEnv *env, jstring name, jobject *desc); + + status_t subscribeToVendorParameters(JNIEnv *env, jobject names); + + status_t unsubscribeFromVendorParameters(JNIEnv *env, jobject names); + bool hasCryptoOrDescrambler() { return mHasCryptoOrDescrambler; } protected: diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt index e415e01fea3a..0a9560a5c56d 100644 --- a/packages/Connectivity/framework/api/current.txt +++ b/packages/Connectivity/framework/api/current.txt @@ -68,6 +68,7 @@ package android.net { method public boolean bindProcessToNetwork(@Nullable android.net.Network); method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork(); + method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public android.net.Network getActiveNetworkForUid(int); method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo(); method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo(); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks(); @@ -387,7 +388,9 @@ package android.net { public class NetworkRequest implements android.os.Parcelable { method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities); method public int describeContents(); + method @NonNull public int[] getCapabilities(); method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); + method @NonNull public int[] getTransportTypes(); method public boolean hasCapability(int); method public boolean hasTransport(int); method public void writeToParcel(android.os.Parcel, int); @@ -396,6 +399,7 @@ package android.net { public static class NetworkRequest.Builder { ctor public NetworkRequest.Builder(); + ctor public NetworkRequest.Builder(@NonNull android.net.NetworkRequest); method public android.net.NetworkRequest.Builder addCapability(int); method public android.net.NetworkRequest.Builder addTransportType(int); method public android.net.NetworkRequest build(); diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index aa7a0ac46598..cd96a1b481b6 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -11,16 +11,31 @@ package android.net { method @Nullable public android.net.ProxyInfo getGlobalProxy(); method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange(); method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackAsUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); 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, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @Deprecated public boolean requestRouteToHostAddress(int, java.net.InetAddress); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptPartialConnectivity(@NonNull android.net.Network, boolean, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptUnvalidated(@NonNull android.net.Network, boolean, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network); method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setLegacyLockdownVpnEnabled(boolean); + method public static void setPrivateDnsMode(@NonNull android.content.Context, @NonNull String); method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>); 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); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network); method public void systemReady(); + field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000 + field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000 + field public static final int BLOCKED_METERED_REASON_MASK = -65536; // 0xffff0000 + field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000 + field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4 + field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1 + field public static final int BLOCKED_REASON_DOZE = 2; // 0x2 + field public static final int BLOCKED_REASON_LOCKDOWN_VPN = 16; // 0x10 + field public static final int BLOCKED_REASON_NONE = 0; // 0x0 + field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8 field public static final String PRIVATE_DNS_MODE_OFF = "off"; field public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic"; field public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname"; @@ -28,17 +43,70 @@ package android.net { field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1 } + public static class ConnectivityManager.NetworkCallback { + method public void onBlockedStatusChanged(@NonNull android.net.Network, int); + } + + public class ConnectivitySettingsManager { + method public static void clearGlobalProxy(@NonNull android.content.Context); + method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context); + method public static int getCaptivePortalMode(@NonNull android.content.Context, int); + method @NonNull public static java.time.Duration getConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method @NonNull public static android.util.Range<java.lang.Integer> getDnsResolverSampleRanges(@NonNull android.content.Context); + method @NonNull public static java.time.Duration getDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static int getDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, int); + method @Nullable public static android.net.ProxyInfo getGlobalProxy(@NonNull android.content.Context); + method @NonNull public static java.time.Duration getMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static boolean getMobileDataAlwaysOn(@NonNull android.content.Context, boolean); + method @Nullable public static String getMobileDataPreferredApps(@NonNull android.content.Context); + method public static int getNetworkAvoidBadWifi(@NonNull android.content.Context); + method @Nullable public static String getNetworkMeteredMultipathPreference(@NonNull android.content.Context); + method public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, int); + method @NonNull public static java.time.Duration getNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method @NonNull public static String getPrivateDnsDefaultMode(@NonNull android.content.Context); + method @Nullable public static String getPrivateDnsHostname(@NonNull android.content.Context); + method public static boolean getWifiAlwaysRequested(@NonNull android.content.Context, boolean); + method @NonNull public static java.time.Duration getWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setCaptivePortalHttpUrl(@NonNull android.content.Context, @Nullable String); + method public static void setCaptivePortalMode(@NonNull android.content.Context, int); + method public static void setConnectivityKeepPendingIntentDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setDnsResolverSampleRanges(@NonNull android.content.Context, @NonNull android.util.Range<java.lang.Integer>); + method public static void setDnsResolverSampleValidityDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setDnsResolverSuccessThresholdPercent(@NonNull android.content.Context, @IntRange(from=0, to=100) int); + method public static void setGlobalProxy(@NonNull android.content.Context, @NonNull android.net.ProxyInfo); + method public static void setMobileDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setMobileDataAlwaysOn(@NonNull android.content.Context, boolean); + method public static void setMobileDataPreferredApps(@NonNull android.content.Context, @Nullable String); + method public static void setNetworkAvoidBadWifi(@NonNull android.content.Context, int); + method public static void setNetworkMeteredMultipathPreference(@NonNull android.content.Context, @NonNull String); + method public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull android.content.Context, @IntRange(from=0) int); + method public static void setNetworkSwitchNotificationRateDuration(@NonNull android.content.Context, @NonNull java.time.Duration); + method public static void setPrivateDnsDefaultMode(@NonNull android.content.Context, @NonNull String); + method public static void setPrivateDnsHostname(@NonNull android.content.Context, @Nullable String); + method public static void setWifiAlwaysRequested(@NonNull android.content.Context, boolean); + method public static void setWifiDataActivityTimeout(@NonNull android.content.Context, @NonNull java.time.Duration); + field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2 + field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0 + field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1 + field public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2; // 0x2 + field public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0; // 0x0 + field public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1; // 0x1 + } + public final class NetworkAgentConfig implements android.os.Parcelable { method @Nullable public String getSubscriberId(); + method public boolean isBypassableVpn(); } public static final class NetworkAgentConfig.Builder { + method @NonNull public android.net.NetworkAgentConfig.Builder setBypassableVpn(boolean); method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String); } public final class NetworkCapabilities implements android.os.Parcelable { ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, long); method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids(); + method public boolean hasUnwantedCapability(int); field public static final long REDACT_ALL = -1L; // 0xffffffffffffffffL field public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1L; // 0x1L field public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 2L; // 0x2L @@ -51,7 +119,14 @@ package android.net { method @NonNull public android.net.NetworkCapabilities.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>); } + public class NetworkRequest implements android.os.Parcelable { + method @NonNull public int[] getUnwantedCapabilities(); + method public boolean hasUnwantedCapability(int); + } + public static class NetworkRequest.Builder { + method @NonNull public android.net.NetworkRequest.Builder addUnwantedCapability(int); + method @NonNull public android.net.NetworkRequest.Builder removeUnwantedCapability(int); method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>); } diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt index 703fca408f7a..95ad694293f2 100644 --- a/packages/Connectivity/framework/api/system-current.txt +++ b/packages/Connectivity/framework/api/system-current.txt @@ -212,10 +212,14 @@ package android.net { public abstract class NetworkAgent { ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider); + ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkScore, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider); method @Nullable public android.net.Network getNetwork(); method public void markConnected(); method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData); method public void onAutomaticReconnectDisabled(); + method public void onBandwidthUpdateRequested(); + method public void onNetworkCreated(); + method public void onNetworkDestroyed(); method public void onNetworkUnwanted(); method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter); method public void onQosCallbackUnregistered(int); @@ -230,9 +234,11 @@ package android.net { 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 sendQosSessionAvailable(int, int, @NonNull android.net.QosSessionAttributes); + method public final void sendQosSessionLost(int, int, int); method public final void sendSocketKeepaliveEvent(int, int); + method @Deprecated public void setLegacySubtype(int, @NonNull String); + method public void setTeardownDelayMs(@IntRange(from=0, to=0x1388) int); method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); method public void unregister(); field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 @@ -253,7 +259,12 @@ package android.net { public static final class NetworkAgentConfig.Builder { ctor public NetworkAgentConfig.Builder(); method @NonNull public android.net.NetworkAgentConfig build(); + method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection(); + method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification(); method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyExtraInfo(@NonNull String); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacySubType(int); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacySubTypeName(@NonNull String); method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int); method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String); method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean); @@ -316,6 +327,19 @@ package android.net { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int); } + public final class NetworkScore implements android.os.Parcelable { + method public int describeContents(); + method public int getLegacyInt(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR; + } + + public static final class NetworkScore.Builder { + ctor public NetworkScore.Builder(); + method @NonNull public android.net.NetworkScore build(); + method @NonNull public android.net.NetworkScore.Builder setLegacyInt(int); + } + public final class OemNetworkPreferences implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getNetworkPreferences(); @@ -363,6 +387,7 @@ package android.net { 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 + field public static final int TYPE_NR_BEARER = 2; // 0x2 } public interface QosSessionAttributes { @@ -388,6 +413,7 @@ package android.net { } public abstract class SocketKeepalive implements java.lang.AutoCloseable { + field public static final int ERROR_NO_SUCH_SLOT = -33; // 0xffffffdf field public static final int SUCCESS = 0; // 0x0 } diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 30ccb206db3d..c6f4e0b354c4 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -38,7 +38,9 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -826,6 +828,114 @@ public class ConnectivityManager { }) public @interface PrivateDnsMode {} + /** + * Flag to indicate that an app is not subject to any restrictions that could result in its + * network access blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_NONE = 0; + + /** + * Flag to indicate that an app is subject to Battery saver restrictions that would + * result in its network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_BATTERY_SAVER = 1 << 0; + + /** + * Flag to indicate that an app is subject to Doze restrictions that would + * result in its network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_DOZE = 1 << 1; + + /** + * Flag to indicate that an app is subject to App Standby restrictions that would + * result in its network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_APP_STANDBY = 1 << 2; + + /** + * Flag to indicate that an app is subject to Restricted mode restrictions that would + * result in its network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_RESTRICTED_MODE = 1 << 3; + + /** + * Flag to indicate that an app is blocked because it is subject to an always-on VPN but the VPN + * is not currently connected. + * + * @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean) + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_REASON_LOCKDOWN_VPN = 1 << 4; + + /** + * Flag to indicate that an app is subject to Data saver restrictions that would + * result in its metered network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_METERED_REASON_DATA_SAVER = 1 << 16; + + /** + * Flag to indicate that an app is subject to user restrictions that would + * result in its metered network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 1 << 17; + + /** + * Flag to indicate that an app is subject to Device admin restrictions that would + * result in its metered network access being blocked. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 1 << 18; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = {"BLOCKED_"}, value = { + BLOCKED_REASON_NONE, + BLOCKED_REASON_BATTERY_SAVER, + BLOCKED_REASON_DOZE, + BLOCKED_REASON_APP_STANDBY, + BLOCKED_REASON_RESTRICTED_MODE, + BLOCKED_REASON_LOCKDOWN_VPN, + BLOCKED_METERED_REASON_DATA_SAVER, + BLOCKED_METERED_REASON_USER_RESTRICTED, + BLOCKED_METERED_REASON_ADMIN_DISABLED, + }) + public @interface BlockedReason {} + + /** + * Set of blocked reasons that are only applicable on metered networks. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) private final IConnectivityManager mService; @@ -1080,8 +1190,7 @@ public class ConnectivityManager { * * @return a {@link Network} object for the current default network for the * given UID or {@code null} if no default network is currently active - * - * @hide + * TODO: b/183465229 Cleanup getActiveNetworkForUid once b/165835257 is fixed */ @RequiresPermission(android.Manifest.permission.NETWORK_STACK) @Nullable @@ -1122,12 +1231,13 @@ public class ConnectivityManager { * @param ranges the UID ranges to restrict * @param requireVpn whether the specified UID ranges must use a VPN * - * TODO: expose as @SystemApi. * @hide */ @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - android.Manifest.permission.NETWORK_STACK}) + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.NETWORK_SETTINGS}) + @SystemApi(client = MODULE_LIBRARIES) public void setRequireVpnForUids(boolean requireVpn, @NonNull Collection<Range<Integer>> ranges) { Objects.requireNonNull(ranges); @@ -1171,13 +1281,13 @@ public class ConnectivityManager { * * @param enabled whether legacy lockdown VPN is enabled or disabled * - * TODO: @SystemApi(client = MODULE_LIBRARIES) - * * @hide */ @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) + @SystemApi(client = MODULE_LIBRARIES) public void setLegacyLockdownVpnEnabled(boolean enabled) { try { mService.setLegacyLockdownVpnEnabled(enabled); @@ -2124,6 +2234,7 @@ public class ConnectivityManager { */ @Deprecated @UnsupportedAppUsage + @SystemApi(client = MODULE_LIBRARIES) public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) { checkLegacyRoutingApiAccess(); try { @@ -3352,12 +3463,30 @@ public class ConnectivityManager { * @param blocked Whether access to the {@link Network} is blocked due to system policy. * @hide */ - public void onAvailable(@NonNull Network network, + public final void onAvailable(@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities, - @NonNull LinkProperties linkProperties, boolean blocked) { + @NonNull LinkProperties linkProperties, @BlockedReason int blocked) { // Internally only this method is called when a new network is available, and // it calls the callback in the same way and order that older versions used // to call so as not to change the behavior. + onAvailable(network, networkCapabilities, linkProperties, blocked != 0); + onBlockedStatusChanged(network, blocked); + } + + /** + * Legacy variant of onAvailable that takes a boolean blocked reason. + * + * This method has never been public API, but it's not final, so there may be apps that + * implemented it and rely on it being called. Do our best not to break them. + * Note: such apps will also get a second call to onBlockedStatusChanged immediately after + * this method is called. There does not seem to be a way to avoid this. + * TODO: add a compat check to move apps off this method, and eventually stop calling it. + * + * @hide + */ + public void onAvailable(@NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties, boolean blocked) { onAvailable(network); if (!networkCapabilities.hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) { @@ -3365,7 +3494,7 @@ public class ConnectivityManager { } onCapabilitiesChanged(network, networkCapabilities); onLinkPropertiesChanged(network, linkProperties); - onBlockedStatusChanged(network, blocked); + // No call to onBlockedStatusChanged here. That is done by the caller. } /** @@ -3529,6 +3658,27 @@ public class ConnectivityManager { */ public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {} + /** + * Called when access to the specified network is blocked or unblocked, or the reason for + * access being blocked changes. + * + * If a NetworkCallback object implements this method, + * {@link #onBlockedStatusChanged(Network, boolean)} will not be called. + * + * <p>Do NOT call {@link #getNetworkCapabilities(Network)} or + * {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in + * this callback as this is prone to race conditions : calling these methods while in a + * callback may return an outdated or even a null object. + * + * @param network The {@link Network} whose blocked status has changed. + * @param blocked The blocked status of this {@link Network}. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public void onBlockedStatusChanged(@NonNull Network network, @BlockedReason int blocked) { + onBlockedStatusChanged(network, blocked != 0); + } + private NetworkRequest networkRequest; private final int mFlags; } @@ -3643,7 +3793,7 @@ public class ConnectivityManager { case CALLBACK_AVAILABLE: { NetworkCapabilities cap = getObject(message, NetworkCapabilities.class); LinkProperties lp = getObject(message, LinkProperties.class); - callback.onAvailable(network, cap, lp, message.arg1 != 0); + callback.onAvailable(network, cap, lp, message.arg1); break; } case CALLBACK_LOSING: { @@ -3677,8 +3827,7 @@ public class ConnectivityManager { break; } case CALLBACK_BLK_CHANGED: { - boolean blocked = message.arg1 != 0; - callback.onBlockedStatusChanged(network, blocked); + callback.onBlockedStatusChanged(network, message.arg1); } } } @@ -3700,8 +3849,9 @@ public class ConnectivityManager { private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>(); private static CallbackHandler sCallbackHandler; - private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, - int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) { + private NetworkRequest sendRequestForNetwork(int asUid, NetworkCapabilities need, + NetworkCallback callback, int timeoutMs, NetworkRequest.Type reqType, int legacyType, + CallbackHandler handler) { printStackTrace(); checkCallbackNotNull(callback); if (reqType != TRACK_DEFAULT && reqType != TRACK_SYSTEM_DEFAULT && need == null) { @@ -3726,8 +3876,8 @@ public class ConnectivityManager { getAttributionTag()); } else { request = mService.requestNetwork( - need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType, - callbackFlags, callingPackageName, getAttributionTag()); + asUid, need, reqType.ordinal(), messenger, timeoutMs, binder, + legacyType, callbackFlags, callingPackageName, getAttributionTag()); } if (request != null) { sCallbacks.put(request, callback); @@ -3742,6 +3892,12 @@ public class ConnectivityManager { return request; } + private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, + int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) { + return sendRequestForNetwork(Process.INVALID_UID, need, callback, timeoutMs, reqType, + legacyType, handler); + } + /** * Helper function to request a network with a particular legacy type. * @@ -4227,8 +4383,40 @@ public class ConnectivityManager { @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, @NonNull Handler handler) { + registerDefaultNetworkCallbackAsUid(Process.INVALID_UID, networkCallback, handler); + } + + /** + * Registers to receive notifications about changes in the default network for the specified + * UID. This may be a physical network or a virtual network, such as a VPN that applies to the + * UID. The callbacks will continue to be called until either the application exits or + * {@link #unregisterNetworkCallback(NetworkCallback)} is called. + * + * <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 #requestNetwork} 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 uid the UID for which to track default network changes. + * @param networkCallback The {@link NetworkCallback} that the system will call as the + * UID's default network changes. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @throws RuntimeException if the app already has too many callbacks registered. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @SuppressLint({"ExecutorRegistration", "PairedRegistration"}) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_SETTINGS}) + public void registerDefaultNetworkCallbackAsUid(int uid, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { CallbackHandler cbHandler = new CallbackHandler(handler); - sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0, + sendRequestForNetwork(uid, null /* need */, networkCallback, 0 /* timeoutMs */, TRACK_DEFAULT, TYPE_NONE, cbHandler); } @@ -5242,4 +5430,23 @@ public class ConnectivityManager { if (TextUtils.isEmpty(mode)) mode = PRIVATE_DNS_MODE_OPPORTUNISTIC; return mode; } + + /** + * Set private DNS mode to settings. + * + * @param context The {@link Context} to set the private DNS mode. + * @param mode The private dns mode. This should be one of the PRIVATE_DNS_MODE_* constants. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static void setPrivateDnsMode(@NonNull Context context, + @NonNull @PrivateDnsMode String mode) { + if (!(mode == PRIVATE_DNS_MODE_OFF + || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC + || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { + throw new IllegalArgumentException("Invalid private dns mode"); + } + Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_MODE, mode); + } } diff --git a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java index bbd83931ee0d..9a00055e0079 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivitySettingsManager.java @@ -16,16 +16,38 @@ package android.net; +import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER; +import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE; +import static android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY; +import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; +import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; +import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; + import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.content.Context; +import android.net.ConnectivityManager.MultipathPreference; +import android.net.ConnectivityManager.PrivateDnsMode; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Range; + +import com.android.net.module.util.ProxyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.time.Duration; +import java.util.List; /** * A manager class for connectivity module settings. * * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public class ConnectivitySettingsManager { private ConnectivitySettingsManager() {} @@ -45,12 +67,16 @@ public class ConnectivitySettingsManager { * Network activity refers to transmitting or receiving data on the network interfaces. * * Tracking is disabled if set to zero or negative value. + * + * @hide */ public static final String DATA_ACTIVITY_TIMEOUT_MOBILE = "data_activity_timeout_mobile"; /** * Timeout to tracking Wifi data activity. Same as {@code DATA_ACTIVITY_TIMEOUT_MOBILE} * but for Wifi network. + * + * @hide */ public static final String DATA_ACTIVITY_TIMEOUT_WIFI = "data_activity_timeout_wifi"; @@ -58,12 +84,16 @@ public class ConnectivitySettingsManager { /** * Sample validity in seconds to configure for the system DNS resolver. + * + * @hide */ public static final String DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS = "dns_resolver_sample_validity_seconds"; /** * Success threshold in percent for use with the system DNS resolver. + * + * @hide */ public static final String DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT = "dns_resolver_success_threshold_percent"; @@ -71,24 +101,35 @@ public class ConnectivitySettingsManager { /** * Minimum number of samples needed for statistics to be considered meaningful in the * system DNS resolver. + * + * @hide */ public static final String DNS_RESOLVER_MIN_SAMPLES = "dns_resolver_min_samples"; /** * Maximum number taken into account for statistics purposes in the system DNS resolver. + * + * @hide */ public static final String DNS_RESOLVER_MAX_SAMPLES = "dns_resolver_max_samples"; + private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8; + private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64; + /** Network switch notification settings */ /** * The maximum number of notifications shown in 24 hours when switching networks. + * + * @hide */ public static final String NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT = "network_switch_notification_daily_limit"; /** * The minimum time in milliseconds between notifications when switching networks. + * + * @hide */ public static final String NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS = "network_switch_notification_rate_limit_millis"; @@ -98,14 +139,18 @@ public class ConnectivitySettingsManager { /** * The URL used for HTTP captive portal detection upon a new connection. * A 204 response code from the server is used for validation. + * + * @hide */ public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; /** * What to do when connecting a network that presents a captive portal. - * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. + * Must be one of the CAPTIVE_PORTAL_MODE_* constants below. * * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. + * + * @hide */ public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; @@ -139,11 +184,15 @@ public class ConnectivitySettingsManager { /** * Host name for global http proxy. Set via ConnectivityManager. + * + * @hide */ public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host"; /** * Integer host port for global http proxy. Set via ConnectivityManager. + * + * @hide */ public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port"; @@ -153,12 +202,16 @@ public class ConnectivitySettingsManager { * Domains should be listed in a comma- separated list. Example of * acceptable formats: ".domain1.com,my.domain2.com" Use * ConnectivityManager to set/get. + * + * @hide */ public static final String GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list"; /** * The location PAC File for the proxy. + * + * @hide */ public static final String GLOBAL_HTTP_PROXY_PAC = "global_proxy_pac_url"; @@ -171,11 +224,15 @@ public class ConnectivitySettingsManager { * a specific provider. It may be used to store the provider name even when the * mode changes so that temporarily disabling and re-enabling the specific * provider mode does not necessitate retyping the provider hostname. + * + * @hide */ public static final String PRIVATE_DNS_MODE = "private_dns_mode"; /** * The specific Private DNS provider name. + * + * @hide */ public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier"; @@ -185,6 +242,8 @@ public class ConnectivitySettingsManager { * all of which require explicit user action to enable/configure. See also b/79719289. * * Value is a string, suitable for assignment to PRIVATE_DNS_MODE above. + * + * @hide */ public static final String PRIVATE_DNS_DEFAULT_MODE = "private_dns_default_mode"; @@ -194,6 +253,8 @@ public class ConnectivitySettingsManager { * The number of milliseconds to hold on to a PendingIntent based request. This delay gives * the receivers of the PendingIntent an opportunity to make a new network request before * the Network satisfying the request is potentially removed. + * + * @hide */ public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS = "connectivity_release_pending_intent_delay_ms"; @@ -205,6 +266,8 @@ public class ConnectivitySettingsManager { * See ConnectivityService for more info. * * (0 = disabled, 1 = enabled) + * + * @hide */ public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; @@ -217,6 +280,8 @@ public class ConnectivitySettingsManager { * See ConnectivityService for more info. * * (0 = disabled, 1 = enabled) + * + * @hide */ public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested"; @@ -228,14 +293,637 @@ public class ConnectivitySettingsManager { * 0: Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013. * null: Ask the user whether to switch away from bad wifi. * 1: Avoid bad wifi. + * + * @hide */ public static final String NETWORK_AVOID_BAD_WIFI = "network_avoid_bad_wifi"; /** + * Don't avoid bad wifi, don't prompt the user. Get stuck on bad wifi like it's 2013. + */ + public static final int NETWORK_AVOID_BAD_WIFI_IGNORE = 0; + + /** + * Ask the user whether to switch away from bad wifi. + */ + public static final int NETWORK_AVOID_BAD_WIFI_PROMPT = 1; + + /** + * Avoid bad wifi. + */ + public static final int NETWORK_AVOID_BAD_WIFI_AVOID = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + NETWORK_AVOID_BAD_WIFI_IGNORE, + NETWORK_AVOID_BAD_WIFI_PROMPT, + NETWORK_AVOID_BAD_WIFI_AVOID, + }) + public @interface NetworkAvoidBadWifi {} + + /** * User setting for ConnectivityManager.getMeteredMultipathPreference(). This value may be * overridden by the system based on device or application state. If null, the value * specified by config_networkMeteredMultipathPreference is used. + * + * @hide */ public static final String NETWORK_METERED_MULTIPATH_PREFERENCE = "network_metered_multipath_preference"; + + /** + * A list of apps that should go on cellular networks in preference even when higher-priority + * networks are connected. + * + * @hide + */ + public static final String MOBILE_DATA_PREFERRED_APPS = "mobile_data_preferred_apps"; + + /** + * Get mobile data activity timeout from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @param def The default timeout if no setting value. + * @return The {@link Duration} of timeout to track mobile data activity. + */ + @NonNull + public static Duration getMobileDataActivityTimeout(@NonNull Context context, + @NonNull Duration def) { + final int timeout = Settings.Global.getInt( + context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, (int) def.getSeconds()); + return Duration.ofSeconds(timeout); + } + + /** + * Set mobile data activity timeout to {@link Settings}. + * Tracking is disabled if set to zero or negative value. + * + * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be + * ignored. + * + * @param context The {@link Context} to set the setting. + * @param timeout The mobile data activity timeout. + */ + public static void setMobileDataActivityTimeout(@NonNull Context context, + @NonNull Duration timeout) { + Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_MOBILE, + (int) timeout.getSeconds()); + } + + /** + * Get wifi data activity timeout from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @param def The default timeout if no setting value. + * @return The {@link Duration} of timeout to track wifi data activity. + */ + @NonNull + public static Duration getWifiDataActivityTimeout(@NonNull Context context, + @NonNull Duration def) { + final int timeout = Settings.Global.getInt( + context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, (int) def.getSeconds()); + return Duration.ofSeconds(timeout); + } + + /** + * Set wifi data activity timeout to {@link Settings}. + * Tracking is disabled if set to zero or negative value. + * + * Note: Only use the number of seconds in this duration, lower second(nanoseconds) will be + * ignored. + * + * @param context The {@link Context} to set the setting. + * @param timeout The wifi data activity timeout. + */ + public static void setWifiDataActivityTimeout(@NonNull Context context, + @NonNull Duration timeout) { + Settings.Global.putInt(context.getContentResolver(), DATA_ACTIVITY_TIMEOUT_WIFI, + (int) timeout.getSeconds()); + } + + /** + * Get dns resolver sample validity duration from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @param def The default duration if no setting value. + * @return The {@link Duration} of sample validity duration to configure for the system DNS + * resolver. + */ + @NonNull + public static Duration getDnsResolverSampleValidityDuration(@NonNull Context context, + @NonNull Duration def) { + final int duration = Settings.Global.getInt(context.getContentResolver(), + DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, (int) def.getSeconds()); + return Duration.ofSeconds(duration); + } + + /** + * Set dns resolver sample validity duration to {@link Settings}. The duration must be a + * positive number of seconds. + * + * @param context The {@link Context} to set the setting. + * @param duration The sample validity duration. + */ + public static void setDnsResolverSampleValidityDuration(@NonNull Context context, + @NonNull Duration duration) { + final int time = (int) duration.getSeconds(); + if (time <= 0) { + throw new IllegalArgumentException("Invalid duration"); + } + Settings.Global.putInt( + context.getContentResolver(), DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, time); + } + + /** + * Get dns resolver success threshold percent from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @param def The default value if no setting value. + * @return The success threshold in percent for use with the system DNS resolver. + */ + public static int getDnsResolverSuccessThresholdPercent(@NonNull Context context, int def) { + return Settings.Global.getInt( + context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, def); + } + + /** + * Set dns resolver success threshold percent to {@link Settings}. The threshold percent must + * be 0~100. + * + * @param context The {@link Context} to set the setting. + * @param percent The success threshold percent. + */ + public static void setDnsResolverSuccessThresholdPercent(@NonNull Context context, + @IntRange(from = 0, to = 100) int percent) { + if (percent < 0 || percent > 100) { + throw new IllegalArgumentException("Percent must be 0~100"); + } + Settings.Global.putInt( + context.getContentResolver(), DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, percent); + } + + /** + * Get dns resolver samples range from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The {@link Range<Integer>} of samples needed for statistics to be considered + * meaningful in the system DNS resolver. + */ + @NonNull + public static Range<Integer> getDnsResolverSampleRanges(@NonNull Context context) { + final int minSamples = Settings.Global.getInt(context.getContentResolver(), + DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES); + final int maxSamples = Settings.Global.getInt(context.getContentResolver(), + DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES); + return new Range<>(minSamples, maxSamples); + } + + /** + * Set dns resolver samples range to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param range The samples range. The minimum number should be more than 0 and the maximum + * number should be less that 64. + */ + public static void setDnsResolverSampleRanges(@NonNull Context context, + @NonNull Range<Integer> range) { + if (range.getLower() < 0 || range.getUpper() > 64) { + throw new IllegalArgumentException("Argument must be 0~64"); + } + Settings.Global.putInt( + context.getContentResolver(), DNS_RESOLVER_MIN_SAMPLES, range.getLower()); + Settings.Global.putInt( + context.getContentResolver(), DNS_RESOLVER_MAX_SAMPLES, range.getUpper()); + } + + /** + * Get maximum count (from {@link Settings}) of switching network notifications shown in 24 + * hours. + * + * @param context The {@link Context} to query the setting. + * @param def The default value if no setting value. + * @return The maximum count of notifications shown in 24 hours when switching networks. + */ + public static int getNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context, + int def) { + return Settings.Global.getInt( + context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, def); + } + + /** + * Set maximum count (to {@link Settings}) of switching network notifications shown in 24 hours. + * The count must be at least 0. + * + * @param context The {@link Context} to set the setting. + * @param count The maximum count of switching network notifications shown in 24 hours. + */ + public static void setNetworkSwitchNotificationMaximumDailyCount(@NonNull Context context, + @IntRange(from = 0) int count) { + if (count < 0) { + throw new IllegalArgumentException("Count must be 0~10."); + } + Settings.Global.putInt( + context.getContentResolver(), NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT, count); + } + + /** + * Get minimum duration (from {@link Settings}) between each switching network notifications. + * + * @param context The {@link Context} to query the setting. + * @param def The default time if no setting value. + * @return The minimum duration between notifications when switching networks. + */ + @NonNull + public static Duration getNetworkSwitchNotificationRateDuration(@NonNull Context context, + @NonNull Duration def) { + final int duration = Settings.Global.getInt(context.getContentResolver(), + NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, (int) def.toMillis()); + return Duration.ofMillis(duration); + } + + /** + * Set minimum duration (to {@link Settings}) between each switching network notifications. + * + * @param context The {@link Context} to set the setting. + * @param duration The minimum duration between notifications when switching networks. + */ + public static void setNetworkSwitchNotificationRateDuration(@NonNull Context context, + @NonNull Duration duration) { + final int time = (int) duration.toMillis(); + if (time < 0) { + throw new IllegalArgumentException("Invalid duration."); + } + Settings.Global.putInt(context.getContentResolver(), + NETWORK_SWITCH_NOTIFICATION_RATE_LIMIT_MILLIS, time); + } + + /** + * Get URL (from {@link Settings}) used for HTTP captive portal detection upon a new connection. + * + * @param context The {@link Context} to query the setting. + * @return The URL used for HTTP captive portal detection upon a new connection. + */ + @Nullable + public static String getCaptivePortalHttpUrl(@NonNull Context context) { + return Settings.Global.getString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL); + } + + /** + * Set URL (to {@link Settings}) used for HTTP captive portal detection upon a new connection. + * This URL should respond with a 204 response to a GET request to indicate no captive portal is + * present. And this URL must be HTTP as redirect responses are used to find captive portal + * sign-in pages. If the URL set to null or be incorrect, it will result in captive portal + * detection failed and lost the connection. + * + * @param context The {@link Context} to set the setting. + * @param url The URL used for HTTP captive portal detection upon a new connection. + */ + public static void setCaptivePortalHttpUrl(@NonNull Context context, @Nullable String url) { + Settings.Global.putString(context.getContentResolver(), CAPTIVE_PORTAL_HTTP_URL, url); + } + + /** + * Get mode (from {@link Settings}) when connecting a network that presents a captive portal. + * + * @param context The {@link Context} to query the setting. + * @param def The default mode if no setting value. + * @return The mode when connecting a network that presents a captive portal. + */ + @CaptivePortalMode + public static int getCaptivePortalMode(@NonNull Context context, + @CaptivePortalMode int def) { + return Settings.Global.getInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, def); + } + + /** + * Set mode (to {@link Settings}) when connecting a network that presents a captive portal. + * + * @param context The {@link Context} to set the setting. + * @param mode The mode when connecting a network that presents a captive portal. + */ + public static void setCaptivePortalMode(@NonNull Context context, @CaptivePortalMode int mode) { + if (!(mode == CAPTIVE_PORTAL_MODE_IGNORE + || mode == CAPTIVE_PORTAL_MODE_PROMPT + || mode == CAPTIVE_PORTAL_MODE_AVOID)) { + throw new IllegalArgumentException("Invalid captive portal mode"); + } + Settings.Global.putInt(context.getContentResolver(), CAPTIVE_PORTAL_MODE, mode); + } + + /** + * Get the global HTTP proxy applied to the device, or null if none. + * + * @param context The {@link Context} to query the setting. + * @return The {@link ProxyInfo} which build from global http proxy settings. + */ + @Nullable + public static ProxyInfo getGlobalProxy(@NonNull Context context) { + final String host = Settings.Global.getString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST); + final int port = Settings.Global.getInt( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* def */); + final String exclusionList = Settings.Global.getString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST); + final String pacFileUrl = Settings.Global.getString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC); + + if (TextUtils.isEmpty(host) && TextUtils.isEmpty(pacFileUrl)) { + return null; // No global proxy. + } + + if (TextUtils.isEmpty(pacFileUrl)) { + return ProxyInfo.buildDirectProxy( + host, port, ProxyUtils.exclusionStringAsList(exclusionList)); + } else { + return ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl)); + } + } + + /** + * Set global http proxy settings from given {@link ProxyInfo}. + * + * @param context The {@link Context} to set the setting. + * @param proxyInfo The {@link ProxyInfo} for global http proxy settings which build from + * {@link ProxyInfo#buildPacProxy(Uri)} or + * {@link ProxyInfo#buildDirectProxy(String, int, List)} + */ + public static void setGlobalProxy(@NonNull Context context, @NonNull ProxyInfo proxyInfo) { + final String host = proxyInfo.getHost(); + final int port = proxyInfo.getPort(); + final String exclusionList = proxyInfo.getExclusionListAsString(); + final String pacFileUrl = proxyInfo.getPacFileUrl().toString(); + + if (TextUtils.isEmpty(pacFileUrl)) { + Settings.Global.putString(context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, host); + Settings.Global.putInt(context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, port); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */); + } else { + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, pacFileUrl); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */); + Settings.Global.putInt( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */); + } + } + + /** + * Clear all global http proxy settings. + * + * @param context The {@link Context} to set the setting. + */ + public static void clearGlobalProxy(@NonNull Context context) { + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_HOST, "" /* value */); + Settings.Global.putInt( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PORT, 0 /* value */); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_EXCLUSION_LIST, "" /* value */); + Settings.Global.putString( + context.getContentResolver(), GLOBAL_HTTP_PROXY_PAC, "" /* value */); + } + + /** + * Get specific private dns provider name from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The specific private dns provider name, or null if no setting value. + */ + @Nullable + public static String getPrivateDnsHostname(@NonNull Context context) { + return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER); + } + + /** + * Set specific private dns provider name to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param specifier The specific private dns provider name. + */ + public static void setPrivateDnsHostname(@NonNull Context context, + @Nullable String specifier) { + Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_SPECIFIER, specifier); + } + + /** + * Get default private dns mode from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The default private dns mode. + */ + @PrivateDnsMode + @NonNull + public static String getPrivateDnsDefaultMode(@NonNull Context context) { + return Settings.Global.getString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE); + } + + /** + * Set default private dns mode to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param mode The default private dns mode. This should be one of the PRIVATE_DNS_MODE_* + * constants. + */ + public static void setPrivateDnsDefaultMode(@NonNull Context context, + @NonNull @PrivateDnsMode String mode) { + if (!(mode == PRIVATE_DNS_MODE_OFF + || mode == PRIVATE_DNS_MODE_OPPORTUNISTIC + || mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { + throw new IllegalArgumentException("Invalid private dns mode"); + } + Settings.Global.putString(context.getContentResolver(), PRIVATE_DNS_DEFAULT_MODE, mode); + } + + /** + * Get duration (from {@link Settings}) to keep a PendingIntent-based request. + * + * @param context The {@link Context} to query the setting. + * @param def The default duration if no setting value. + * @return The duration to keep a PendingIntent-based request. + */ + @NonNull + public static Duration getConnectivityKeepPendingIntentDuration(@NonNull Context context, + @NonNull Duration def) { + final int duration = Settings.Secure.getInt(context.getContentResolver(), + CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, (int) def.toMillis()); + return Duration.ofMillis(duration); + } + + /** + * Set duration (to {@link Settings}) to keep a PendingIntent-based request. + * + * @param context The {@link Context} to set the setting. + * @param duration The duration to keep a PendingIntent-based request. + */ + public static void setConnectivityKeepPendingIntentDuration(@NonNull Context context, + @NonNull Duration duration) { + final int time = (int) duration.toMillis(); + if (time < 0) { + throw new IllegalArgumentException("Invalid duration."); + } + Settings.Secure.putInt( + context.getContentResolver(), CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, time); + } + + /** + * Read from {@link Settings} whether the mobile data connection should remain active + * even when higher priority networks are active. + * + * @param context The {@link Context} to query the setting. + * @param def The default value if no setting value. + * @return Whether the mobile data connection should remain active even when higher + * priority networks are active. + */ + public static boolean getMobileDataAlwaysOn(@NonNull Context context, boolean def) { + final int enable = Settings.Global.getInt( + context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (def ? 1 : 0)); + return (enable != 0) ? true : false; + } + + /** + * Write into {@link Settings} whether the mobile data connection should remain active + * even when higher priority networks are active. + * + * @param context The {@link Context} to set the setting. + * @param enable Whether the mobile data connection should remain active even when higher + * priority networks are active. + */ + public static void setMobileDataAlwaysOn(@NonNull Context context, boolean enable) { + Settings.Global.putInt( + context.getContentResolver(), MOBILE_DATA_ALWAYS_ON, (enable ? 1 : 0)); + } + + /** + * Read from {@link Settings} whether the wifi data connection should remain active + * even when higher priority networks are active. + * + * @param context The {@link Context} to query the setting. + * @param def The default value if no setting value. + * @return Whether the wifi data connection should remain active even when higher + * priority networks are active. + */ + public static boolean getWifiAlwaysRequested(@NonNull Context context, boolean def) { + final int enable = Settings.Global.getInt( + context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (def ? 1 : 0)); + return (enable != 0) ? true : false; + } + + /** + * Write into {@link Settings} whether the wifi data connection should remain active + * even when higher priority networks are active. + * + * @param context The {@link Context} to set the setting. + * @param enable Whether the wifi data connection should remain active even when higher + * priority networks are active + */ + public static void setWifiAlwaysRequested(@NonNull Context context, boolean enable) { + Settings.Global.putInt( + context.getContentResolver(), WIFI_ALWAYS_REQUESTED, (enable ? 1 : 0)); + } + + /** + * Get avoid bad wifi setting from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The setting whether to automatically switch away from wifi networks that lose + * internet access. + */ + @NetworkAvoidBadWifi + public static int getNetworkAvoidBadWifi(@NonNull Context context) { + final String setting = + Settings.Global.getString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI); + if ("0".equals(setting)) { + return NETWORK_AVOID_BAD_WIFI_IGNORE; + } else if ("1".equals(setting)) { + return NETWORK_AVOID_BAD_WIFI_AVOID; + } else { + return NETWORK_AVOID_BAD_WIFI_PROMPT; + } + } + + /** + * Set avoid bad wifi setting to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param value Whether to automatically switch away from wifi networks that lose internet + * access. + */ + public static void setNetworkAvoidBadWifi(@NonNull Context context, + @NetworkAvoidBadWifi int value) { + final String setting; + if (value == NETWORK_AVOID_BAD_WIFI_IGNORE) { + setting = "0"; + } else if (value == NETWORK_AVOID_BAD_WIFI_AVOID) { + setting = "1"; + } else if (value == NETWORK_AVOID_BAD_WIFI_PROMPT) { + setting = null; + } else { + throw new IllegalArgumentException("Invalid avoid bad wifi setting"); + } + Settings.Global.putString(context.getContentResolver(), NETWORK_AVOID_BAD_WIFI, setting); + } + + /** + * Get network metered multipath preference from {@link Settings}. + * + * @param context The {@link Context} to query the setting. + * @return The network metered multipath preference which should be one of + * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value specified + * by config_networkMeteredMultipathPreference is used. + */ + @Nullable + public static String getNetworkMeteredMultipathPreference(@NonNull Context context) { + return Settings.Global.getString( + context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE); + } + + /** + * Set network metered multipath preference to {@link Settings}. + * + * @param context The {@link Context} to set the setting. + * @param preference The network metered multipath preference which should be one of + * ConnectivityManager#MULTIPATH_PREFERENCE_* value or null if the value + * specified by config_networkMeteredMultipathPreference is used. + */ + public static void setNetworkMeteredMultipathPreference(@NonNull Context context, + @NonNull @MultipathPreference String preference) { + if (!(Integer.valueOf(preference) == MULTIPATH_PREFERENCE_HANDOVER + || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_RELIABILITY + || Integer.valueOf(preference) == MULTIPATH_PREFERENCE_PERFORMANCE)) { + throw new IllegalArgumentException("Invalid private dns mode"); + } + Settings.Global.putString( + context.getContentResolver(), NETWORK_METERED_MULTIPATH_PREFERENCE, preference); + } + + /** + * Get the list of apps(from {@link Settings}) that should go on cellular networks in preference + * even when higher-priority networks are connected. + * + * @param context The {@link Context} to query the setting. + * @return A list of apps that should go on cellular networks in preference even when + * higher-priority networks are connected or null if no setting value. + */ + @Nullable + public static String getMobileDataPreferredApps(@NonNull Context context) { + return Settings.Secure.getString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS); + } + + /** + * Set the list of apps(to {@link Settings}) that should go on cellular networks in preference + * even when higher-priority networks are connected. + * + * @param context The {@link Context} to set the setting. + * @param list A list of apps that should go on cellular networks in preference even when + * higher-priority networks are connected. + */ + public static void setMobileDataPreferredApps(@NonNull Context context, @Nullable String list) { + Settings.Secure.putString(context.getContentResolver(), MOBILE_DATA_PREFERRED_APPS, list); + } } diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index 3300fa8fd12a..0826922e2165 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -142,7 +142,7 @@ interface IConnectivityManager in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config, in int factorySerialNumber); - NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType, + NetworkRequest requestNetwork(int uid, in NetworkCapabilities networkCapabilities, int reqType, in Messenger messenger, int timeoutSec, in IBinder binder, int legacy, int callbackFlags, String callingPackageName, String callingAttributionTag); diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl index 1f66e18717d8..d941d4b95b56 100644 --- a/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl +++ b/packages/Connectivity/framework/src/android/net/INetworkAgent.aidl @@ -46,4 +46,6 @@ oneway interface INetworkAgent { void onRemoveKeepalivePacketFilter(int slot); void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel); void onQosCallbackUnregistered(int qosCallbackId); + void onNetworkCreated(); + void onNetworkDestroyed(); } diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl index c5464d32412b..26cb1ed6b4b4 100644 --- a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl +++ b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl @@ -22,6 +22,7 @@ import android.net.NetworkInfo; import android.net.NetworkScore; import android.net.QosSession; import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; /** * Interface for NetworkAgents to send network properties. @@ -37,6 +38,8 @@ oneway interface INetworkAgentRegistry { void sendSocketKeepaliveEvent(int slot, int reason); void sendUnderlyingNetworks(in @nullable List<Network> networks); void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes); + void sendNrQosSessionAvailable(int callbackId, in QosSession session, in NrQosSessionAttributes attributes); void sendQosSessionLost(int qosCallbackId, in QosSession session); void sendQosCallbackError(int qosCallbackId, int exceptionType); + void sendTeardownDelayMs(int teardownDelayMs); } diff --git a/packages/Connectivity/framework/src/android/net/IQosCallback.aidl b/packages/Connectivity/framework/src/android/net/IQosCallback.aidl index 91c75759f85c..c9735419f7dd 100644 --- a/packages/Connectivity/framework/src/android/net/IQosCallback.aidl +++ b/packages/Connectivity/framework/src/android/net/IQosCallback.aidl @@ -19,6 +19,7 @@ package android.net; import android.os.Bundle; import android.net.QosSession; import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; /** * AIDL interface for QosCallback @@ -29,6 +30,8 @@ oneway interface IQosCallback { void onQosEpsBearerSessionAvailable(in QosSession session, in EpsBearerQosSessionAttributes attributes); + void onNrQosSessionAvailable(in QosSession session, + in NrQosSessionAttributes attributes); void onQosSessionLost(in QosSession session); void onError(in int type); } diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java index 3863ed1113f3..c57da53f289d 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java @@ -32,6 +32,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -184,6 +185,20 @@ public abstract class NetworkAgent { public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5; /** + * Sent by the NetworkAgent to ConnectivityService to pass the current value of the teardown + * delay. + * arg1 = teardown delay in milliseconds + * @hide + */ + public static final int EVENT_TEARDOWN_DELAY_CHANGED = BASE + 6; + + /** + * The maximum value for the teardown delay, in milliseconds. + * @hide + */ + public static final int MAX_TEARDOWN_DELAY_MS = 5000; + + /** * Sent by ConnectivityService to the NetworkAgent to inform the agent of the * networks status - whether we could use the network or could not, due to * either a bad network configuration (no internet link) or captive portal. @@ -196,7 +211,6 @@ public abstract class NetworkAgent { */ public static final int CMD_REPORT_NETWORK_STATUS = BASE + 7; - /** * Network validation suceeded. * Corresponds to {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED}. @@ -361,10 +375,25 @@ public abstract class NetworkAgent { */ public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21; + /** + * Sent by ConnectivityService to {@link NetworkAgent} to inform the agent that its native + * network was created and the Network object is now valid. + * + * @hide + */ + public static final int CMD_NETWORK_CREATED = BASE + 22; + + /** + * Sent by ConnectivityService to {@link NetworkAgent} to inform the agent that its native + * network was destroyed. + * + * @hide + */ + public static final int CMD_NETWORK_DESTROYED = BASE + 23; + 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. - final NetworkInfo ni = new NetworkInfo(config.legacyType, 0, config.legacyTypeName, ""); + final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType, + config.legacyTypeName, config.legacySubTypeName); ni.setIsAvailable(true); ni.setDetailedState(NetworkInfo.DetailedState.CONNECTING, null /* reason */, config.getLegacyExtraInfo()); @@ -390,7 +419,6 @@ public abstract class NetworkAgent { * @param score the initial score of this network. Update with sendNetworkScore. * @param config an immutable {@link NetworkAgentConfig} for this agent. * @param provider the {@link NetworkProvider} managing this agent. - * @hide TODO : unhide when impl is complete */ public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag, @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, @@ -562,6 +590,14 @@ public abstract class NetworkAgent { msg.arg1 /* QoS callback id */); break; } + case CMD_NETWORK_CREATED: { + onNetworkCreated(); + break; + } + case CMD_NETWORK_DESTROYED: { + onNetworkDestroyed(); + break; + } } } } @@ -702,6 +738,16 @@ public abstract class NetworkAgent { mHandler.sendMessage(mHandler.obtainMessage( CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null)); } + + @Override + public void onNetworkCreated() { + mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_CREATED)); + } + + @Override + public void onNetworkDestroyed() { + mHandler.sendMessage(mHandler.obtainMessage(CMD_NETWORK_DESTROYED)); + } } /** @@ -818,6 +864,29 @@ public abstract class NetworkAgent { } /** + * Sets the value of the teardown delay. + * + * The teardown delay is the time between when the network disconnects and when the native + * network corresponding to this {@code NetworkAgent} is destroyed. By default, the native + * network is destroyed immediately. If {@code teardownDelayMs} is non-zero, then when this + * network disconnects, the system will instead immediately mark the network as restricted + * and unavailable to unprivileged apps, but will defer destroying the native network until the + * teardown delay timer expires. + * + * The interfaces in use by this network will remain in use until the native network is + * destroyed and cannot be reused until {@link #onNetworkDestroyed()} is called. + * + * This method may be called at any time while the network is connected. It has no effect if + * the network is already disconnected and the teardown delay timer is running. + * + * @param teardownDelayMs the teardown delay to set, or 0 to disable teardown delay. + */ + public void setTeardownDelayMs( + @IntRange(from = 0, to = MAX_TEARDOWN_DELAY_MS) int teardownDelayMs) { + queueOrSendMessage(reg -> reg.sendTeardownDelayMs(teardownDelayMs)); + } + + /** * Change the legacy subtype of this network agent. * * This is only for backward compatibility and should not be used by non-legacy network agents, @@ -829,6 +898,7 @@ public abstract class NetworkAgent { * @hide */ @Deprecated + @SystemApi public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) { mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName); queueOrSendNetworkInfo(mNetworkInfo); @@ -962,6 +1032,7 @@ public abstract class NetworkAgent { * shall try to overwrite this method and produce a bandwidth update if capable. * @hide */ + @SystemApi public void onBandwidthUpdateRequested() { pollLceData(); } @@ -1010,6 +1081,17 @@ public abstract class NetworkAgent { } /** + * Called when ConnectivityService has successfully created this NetworkAgent's native network. + */ + public void onNetworkCreated() {} + + + /** + * Called when ConnectivityService has successfully destroy this NetworkAgent's native network. + */ + public void onNetworkDestroyed() {} + + /** * Requests that the network hardware send the specified packet at the specified interval. * * @param slot the hardware slot on which to start the keepalive. @@ -1160,29 +1242,37 @@ public abstract class NetworkAgent { /** - * Sends the attributes of Eps Bearer Qos Session back to the Application + * Sends the attributes of 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 + * @param sessionId the unique session id across all Qos Sessions + * @param attributes the attributes of the Qos Session */ public final void sendQosSessionAvailable(final int qosCallbackId, final int sessionId, - @NonNull final EpsBearerQosSessionAttributes attributes) { + @NonNull final QosSessionAttributes attributes) { Objects.requireNonNull(attributes, "The attributes must be non-null"); - queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId, - new QosSession(sessionId, QosSession.TYPE_EPS_BEARER), - attributes)); + if (attributes instanceof EpsBearerQosSessionAttributes) { + queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId, + new QosSession(sessionId, QosSession.TYPE_EPS_BEARER), + (EpsBearerQosSessionAttributes)attributes)); + } else if (attributes instanceof NrQosSessionAttributes) { + queueOrSendMessage(ra -> ra.sendNrQosSessionAvailable(qosCallbackId, + new QosSession(sessionId, QosSession.TYPE_NR_BEARER), + (NrQosSessionAttributes)attributes)); + } } /** - * Sends event that the Eps Qos Session was lost. + * Sends event that the 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 + * @param sessionId the unique session id across all Qos Sessions + * @param qosSessionType the session type {@code QosSesson#QosSessionType} */ - public final void sendQosSessionLost(final int qosCallbackId, final int sessionId) { + public final void sendQosSessionLost(final int qosCallbackId, + final int sessionId, final int qosSessionType) { queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId, - new QosSession(sessionId, QosSession.TYPE_EPS_BEARER))); + new QosSession(sessionId, qosSessionType))); } /** diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java index 5e50a6404acb..3f058d8cbf12 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java @@ -64,6 +64,16 @@ public final class NetworkAgentConfig implements Parcelable { } /** + * @return whether this VPN connection can be bypassed by the apps. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public boolean isBypassableVpn() { + return allowBypass; + } + + /** * Set if the user desires to use this network even if it is unvalidated. This field has meaning * only if {@link explicitlySelected} is true. If it is, this field must also be set to the * appropriate value based on previous user choice. @@ -165,6 +175,12 @@ public final class NetworkAgentConfig implements Parcelable { } /** + * The legacy Sub type of this network agent, or TYPE_NONE if unset. + * @hide + */ + public int legacySubType = ConnectivityManager.TYPE_NONE; + + /** * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network. * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode. * @@ -190,6 +206,13 @@ public final class NetworkAgentConfig implements Parcelable { } /** + * The name of the legacy Sub network type. It's a free-form string. + * @hide + */ + @NonNull + public String legacySubTypeName = ""; + + /** * The legacy extra info of the agent. The extra info should only be : * <ul> * <li>For cellular agents, the APN name.</li> @@ -225,6 +248,8 @@ public final class NetworkAgentConfig implements Parcelable { skip464xlat = nac.skip464xlat; legacyType = nac.legacyType; legacyTypeName = nac.legacyTypeName; + legacySubType = nac.legacySubType; + legacySubTypeName = nac.legacySubTypeName; mLegacyExtraInfo = nac.mLegacyExtraInfo; } } @@ -290,7 +315,6 @@ public final class NetworkAgentConfig implements Parcelable { * and reduce idle traffic on networks that are known to be IPv6-only without a NAT64. * * @return this builder, to facilitate chaining. - * @hide */ @NonNull public Builder disableNat64Detection() { @@ -303,7 +327,6 @@ public final class NetworkAgentConfig implements Parcelable { * perform its own carrier-specific provisioning procedure. * * @return this builder, to facilitate chaining. - * @hide */ @NonNull public Builder disableProvisioningNotification() { @@ -324,6 +347,18 @@ public final class NetworkAgentConfig implements Parcelable { } /** + * Sets the legacy sub-type for this network. + * + * @param legacySubType the type + * @return this builder, to facilitate chaining. + */ + @NonNull + public Builder setLegacySubType(final int legacySubType) { + mConfig.legacySubType = legacySubType; + return this; + } + + /** * Sets the name of the legacy type of the agent. It's a free-form string used in logging. * @param legacyTypeName the name * @return this builder, to facilitate chaining. @@ -335,10 +370,20 @@ public final class NetworkAgentConfig implements Parcelable { } /** + * Sets the name of the legacy Sub-type of the agent. It's a free-form string. + * @param legacySubTypeName the name + * @return this builder, to facilitate chaining. + */ + @NonNull + public Builder setLegacySubTypeName(@NonNull String legacySubTypeName) { + mConfig.legacySubTypeName = legacySubTypeName; + return this; + } + + /** * Sets the legacy extra info of the agent. * @param legacyExtraInfo the legacy extra info. * @return this builder, to facilitate chaining. - * @hide */ @NonNull public Builder setLegacyExtraInfo(@NonNull String legacyExtraInfo) { @@ -347,6 +392,19 @@ public final class NetworkAgentConfig implements Parcelable { } /** + * Sets whether the apps can bypass the VPN connection. + * + * @return this builder, to facilitate chaining. + * @hide + */ + @NonNull + @SystemApi(client = MODULE_LIBRARIES) + public Builder setBypassableVpn(boolean allowBypass) { + mConfig.allowBypass = allowBypass; + return this; + } + + /** * Returns the constructed {@link NetworkAgentConfig} object. */ @NonNull @@ -412,6 +470,8 @@ public final class NetworkAgentConfig implements Parcelable { out.writeInt(skip464xlat ? 1 : 0); out.writeInt(legacyType); out.writeString(legacyTypeName); + out.writeInt(legacySubType); + out.writeString(legacySubTypeName); out.writeString(mLegacyExtraInfo); } @@ -429,6 +489,8 @@ public final class NetworkAgentConfig implements Parcelable { networkAgentConfig.skip464xlat = in.readInt() != 0; networkAgentConfig.legacyType = in.readInt(); networkAgentConfig.legacyTypeName = in.readString(); + networkAgentConfig.legacySubType = in.readInt(); + networkAgentConfig.legacySubTypeName = in.readString(); networkAgentConfig.mLegacyExtraInfo = in.readString(); return networkAgentConfig; } diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index c9c0940dfdf5..881fa8c2702c 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -639,19 +639,31 @@ public final class NetworkCapabilities implements Parcelable { } /** - * Removes (if found) the given capability from this {@code NetworkCapability} instance. + * Removes (if found) the given capability from this {@code NetworkCapability} + * instance that were added via addCapability(int) or setCapabilities(int[], int[]). * * @param capability the capability to be removed. * @return This NetworkCapabilities instance, to facilitate chaining. * @hide */ public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) { - // Note that this method removes capabilities that were added via addCapability(int), - // addUnwantedCapability(int) or setCapabilities(int[], int[]). checkValidCapability(capability); final long mask = ~(1 << capability); mNetworkCapabilities &= mask; - mUnwantedNetworkCapabilities &= mask; + return this; + } + + /** + * Removes (if found) the given unwanted capability from this {@code NetworkCapability} + * instance that were added via addUnwantedCapability(int) or setCapabilities(int[], int[]). + * + * @param capability the capability to be removed. + * @return This NetworkCapabilities instance, to facilitate chaining. + * @hide + */ + public @NonNull NetworkCapabilities removeUnwantedCapability(@NetCapability int capability) { + checkValidCapability(capability); + mUnwantedNetworkCapabilities &= ~(1 << capability); return this; } @@ -723,6 +735,7 @@ public final class NetworkCapabilities implements Parcelable { } /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public boolean hasUnwantedCapability(@NetCapability int capability) { return isValidCapability(capability) && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0); @@ -736,10 +749,16 @@ public final class NetworkCapabilities implements Parcelable { return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0); } - /** Note this method may result in having the same capability in wanted and unwanted lists. */ private void combineNetCapabilities(@NonNull NetworkCapabilities nc) { - this.mNetworkCapabilities |= nc.mNetworkCapabilities; - this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities; + final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities; + final long unwantedCaps = + this.mUnwantedNetworkCapabilities | nc.mUnwantedNetworkCapabilities; + if ((wantedCaps & unwantedCaps) != 0) { + throw new IllegalArgumentException( + "Cannot have the same capability in wanted and unwanted lists."); + } + this.mNetworkCapabilities = wantedCaps; + this.mUnwantedNetworkCapabilities = unwantedCaps; } /** diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index cf131f0df607..38691ef5cb39 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -216,6 +216,14 @@ public class NetworkRequest implements Parcelable { } /** + * Creates a new Builder of NetworkRequest from an existing instance. + */ + public Builder(@NonNull final NetworkRequest request) { + Objects.requireNonNull(request); + mNetworkCapabilities = request.networkCapabilities; + } + + /** * Build {@link NetworkRequest} give the current set of capabilities. */ public NetworkRequest build() { @@ -305,12 +313,31 @@ public class NetworkRequest implements Parcelable { * * @hide */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.addUnwantedCapability(capability); return this; } /** + * Removes (if found) the given unwanted capability from this builder instance. + * + * @param capability The unwanted capability to remove. + * @return The builder to facilitate chaining. + * + * @hide + */ + @NonNull + @SuppressLint("BuilderSetStyle") + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public Builder removeUnwantedCapability(@NetworkCapabilities.NetCapability int capability) { + mNetworkCapabilities.removeUnwantedCapability(capability); + return this; + } + + /** * Completely clears all the {@code NetworkCapabilities} from this builder instance, * removing even the capabilities that are set by default when the object is constructed. * @@ -567,6 +594,7 @@ public class NetworkRequest implements Parcelable { * * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public boolean hasUnwantedCapability(@NetCapability int capability) { return networkCapabilities.hasUnwantedCapability(capability); } @@ -671,4 +699,43 @@ public class NetworkRequest implements Parcelable { public int hashCode() { return Objects.hash(requestId, legacyType, networkCapabilities, type); } + + /** + * Gets all the capabilities set on this {@code NetworkRequest} instance. + * + * @return an array of capability values for this instance. + */ + @NonNull + public @NetCapability int[] getCapabilities() { + // No need to make a defensive copy here as NC#getCapabilities() already returns + // a new array. + return networkCapabilities.getCapabilities(); + } + + /** + * Gets all the unwanted capabilities set on this {@code NetworkRequest} instance. + * + * @return an array of unwanted capability values for this instance. + * + * @hide + */ + @NonNull + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public @NetCapability int[] getUnwantedCapabilities() { + // No need to make a defensive copy here as NC#getUnwantedCapabilities() already returns + // a new array. + return networkCapabilities.getUnwantedCapabilities(); + } + + /** + * Gets all the transports set on this {@code NetworkRequest} instance. + * + * @return an array of transport type values for this instance. + */ + @NonNull + public @Transport int[] getTransportTypes() { + // No need to make a defensive copy here as NC#getTransportTypes() already returns + // a new array. + return networkCapabilities.getTransportTypes(); + } } diff --git a/packages/Connectivity/framework/src/android/net/NetworkScore.java b/packages/Connectivity/framework/src/android/net/NetworkScore.java index eadcb2d0a7f4..65849930fa4a 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkScore.java +++ b/packages/Connectivity/framework/src/android/net/NetworkScore.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -29,7 +30,7 @@ import com.android.internal.annotations.VisibleForTesting; * network is considered for a particular use. * @hide */ -// TODO : @SystemApi when the implementation is complete +@SystemApi public final class NetworkScore implements Parcelable { // This will be removed soon. Do *NOT* depend on it for any new code that is not part of // a migration. @@ -62,6 +63,8 @@ public final class NetworkScore implements Parcelable { /** * @return whether this score has a particular policy. + * + * @hide */ @VisibleForTesting public boolean hasPolicy(final int policy) { diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java index c4bebc0a982e..a92fda1cde46 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java +++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java @@ -92,7 +92,10 @@ public class NetworkUtils { * Determine if {@code uid} can access network designated by {@code netId}. * @return {@code true} if {@code uid} can access network, {@code false} otherwise. */ - public native static boolean queryUserAccess(int uid, int netId); + public static boolean queryUserAccess(int uid, int netId) { + // TODO (b/183485986): remove this method + return false; + } /** * DNS resolver series jni method. diff --git a/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java b/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java index bdb4ad68cd7b..de0fc243b43b 100644 --- a/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java +++ b/packages/Connectivity/framework/src/android/net/QosCallbackConnection.java @@ -19,6 +19,7 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; import com.android.internal.annotations.VisibleForTesting; @@ -84,6 +85,25 @@ class QosCallbackConnection extends android.net.IQosCallback.Stub { } /** + * Called when either the {@link NrQosSessionAttributes} 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 onNrQosSessionAvailable(@NonNull final QosSession session, + @NonNull final NrQosSessionAttributes 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 diff --git a/packages/Connectivity/framework/src/android/net/QosSession.java b/packages/Connectivity/framework/src/android/net/QosSession.java index 4f3bb77c5877..93f2ff2bf724 100644 --- a/packages/Connectivity/framework/src/android/net/QosSession.java +++ b/packages/Connectivity/framework/src/android/net/QosSession.java @@ -36,6 +36,11 @@ public final class QosSession implements Parcelable { */ public static final int TYPE_EPS_BEARER = 1; + /** + * The {@link QosSession} is a NR Session. + */ + public static final int TYPE_NR_BEARER = 2; + private final int mSessionId; private final int mSessionType; @@ -100,6 +105,7 @@ public final class QosSession implements Parcelable { */ @IntDef(value = { TYPE_EPS_BEARER, + TYPE_NR_BEARER, }) @interface QosSessionType {} diff --git a/packages/Connectivity/framework/src/android/net/SocketKeepalive.java b/packages/Connectivity/framework/src/android/net/SocketKeepalive.java index d007a9520cb5..f6cae7251609 100644 --- a/packages/Connectivity/framework/src/android/net/SocketKeepalive.java +++ b/packages/Connectivity/framework/src/android/net/SocketKeepalive.java @@ -55,36 +55,68 @@ public abstract class SocketKeepalive implements AutoCloseable { static final String TAG = "SocketKeepalive"; /** - * No errors. + * Success. It indicates there is no error. * @hide */ @SystemApi public static final int SUCCESS = 0; - /** @hide */ + /** + * No keepalive. This should only be internally as it indicates There is no keepalive. + * It should not propagate to applications. + * @hide + */ public static final int NO_KEEPALIVE = -1; - /** @hide */ + /** + * Data received. + * @hide + */ public static final int DATA_RECEIVED = -2; - /** @hide */ + /** + * The binder died. + * @hide + */ public static final int BINDER_DIED = -10; - /** The specified {@code Network} is not connected. */ + /** + * The invalid network. It indicates the specified {@code Network} is not connected. + */ public static final int ERROR_INVALID_NETWORK = -20; - /** The specified IP addresses are invalid. For example, the specified source IP address is - * not configured on the specified {@code Network}. */ + + /** + * The invalid IP addresses. Indicates the specified IP addresses are invalid. + * For example, the specified source IP address is not configured on the + * specified {@code Network}. + */ public static final int ERROR_INVALID_IP_ADDRESS = -21; - /** The requested port is invalid. */ + + /** + * The port is invalid. + */ public static final int ERROR_INVALID_PORT = -22; - /** The packet length is invalid (e.g., too long). */ + + /** + * The length is invalid (e.g. too long). + */ public static final int ERROR_INVALID_LENGTH = -23; - /** The packet transmission interval is invalid (e.g., too short). */ + + /** + * The interval is invalid (e.g. too short). + */ public static final int ERROR_INVALID_INTERVAL = -24; - /** The target socket is invalid. */ + + /** + * The socket is invalid. + */ public static final int ERROR_INVALID_SOCKET = -25; - /** The target socket is not idle. */ + + /** + * The socket is not idle. + */ public static final int ERROR_SOCKET_NOT_IDLE = -26; + /** * The stop reason is uninitialized. This should only be internally used as initial state * of stop reason, instead of propagating to application. @@ -92,15 +124,29 @@ public abstract class SocketKeepalive implements AutoCloseable { */ public static final int ERROR_STOP_REASON_UNINITIALIZED = -27; - /** The device does not support this request. */ + /** + * The request is unsupported. + */ public static final int ERROR_UNSUPPORTED = -30; - /** @hide TODO: delete when telephony code has been updated. */ - public static final int ERROR_HARDWARE_UNSUPPORTED = ERROR_UNSUPPORTED; - /** The hardware returned an error. */ + + /** + * There was a hardware error. + */ public static final int ERROR_HARDWARE_ERROR = -31; - /** The limitation of resource is reached. */ + + /** + * Resources are insufficient (e.g. all hardware slots are in use). + */ public static final int ERROR_INSUFFICIENT_RESOURCES = -32; + /** + * There was no such slot. This should only be internally as it indicates + * a programming error in the system server. It should not propagate to + * applications. + * @hide + */ + @SystemApi + public static final int ERROR_NO_SUCH_SLOT = -33; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -111,7 +157,8 @@ public abstract class SocketKeepalive implements AutoCloseable { ERROR_INVALID_LENGTH, ERROR_INVALID_INTERVAL, ERROR_INVALID_SOCKET, - ERROR_SOCKET_NOT_IDLE + ERROR_SOCKET_NOT_IDLE, + ERROR_NO_SUCH_SLOT }) public @interface ErrorCode {} @@ -122,7 +169,6 @@ public abstract class SocketKeepalive implements AutoCloseable { ERROR_INVALID_LENGTH, ERROR_UNSUPPORTED, ERROR_INSUFFICIENT_RESOURCES, - ERROR_HARDWARE_UNSUPPORTED }) public @interface KeepaliveEvent {} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index b7ce2a30931b..413e8e3a0727 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -122,7 +122,8 @@ <uses-permission android:name="android.permission.CREATE_USERS" /> <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" /> - <uses-permission android:name="android.permission.QUERY_USERS" /> + <uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" /> + <uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" /> <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" /> <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/> <uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/> @@ -182,6 +183,7 @@ <uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" /> <uses-permission android:name="android.permission.MANAGE_SENSORS" /> <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" /> + <uses-permission android:name="android.permission.QUERY_AUDIO_STATE" /> <uses-permission android:name="android.permission.MANAGE_CAMERA" /> <!-- Permissions needed to test system only camera devices --> <uses-permission android:name="android.permission.CAMERA" /> @@ -230,6 +232,9 @@ <!-- Permission needed to run keyguard manager tests in CTS --> <uses-permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS" /> + <!-- Permission needed to set/clear/verify lockscreen credentials in CTS tests --> + <uses-permission android:name="android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS" /> + <!-- Permission needed to test wallpaper component --> <uses-permission android:name="android.permission.SET_WALLPAPER" /> <uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" /> diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index f884270eaba8..835471d5cb94 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -9,6 +9,7 @@ asc@google.com awickham@google.com beverlyt@google.com brockman@google.com +ccassidy@google.com cinek@google.com cwren@google.com dupin@google.com @@ -19,10 +20,10 @@ hwwang@google.com hyunyoungs@google.com jaggies@google.com jamesoleary@google.com +jdemeulenaere@google.com jeffdq@google.com jjaggi@google.com jonmiranda@google.com -joshmcgrath@google.com joshtrask@google.com juliacr@google.com juliatuttle@google.com @@ -37,7 +38,6 @@ mkephart@google.com mpietal@google.com mrcasey@google.com mrenouf@google.com -nbenbernou@google.com nesciosquid@google.com ogunwale@google.com peanutbutter@google.com @@ -45,6 +45,7 @@ pinyaoting@google.com pixel@google.com roosa@google.com santie@google.com +shanh@google.com snoeberger@google.com sreyasr@google.com steell@google.com @@ -59,6 +60,7 @@ twickham@google.com vadimt@google.com victortulias@google.com winsonc@google.com +yurilin@google.com xuqiu@google.com zakcohen@google.com diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml index 443a9bc33b90..f971a0916837 100644 --- a/packages/VpnDialogs/res/values/strings.xml +++ b/packages/VpnDialogs/res/values/strings.xml @@ -28,6 +28,17 @@ ]]> appears at the top of your screen when VPN is active. </string> + <!-- TV specific dialog message to warn about the risk of using a VPN application. [CHAR LIMIT=NONE] --> + <string name="warning" product="tv"> + <xliff:g id="app">%s</xliff:g> wants to set up a VPN connection + that allows it to monitor network traffic. Only accept if you trust the source. + <![CDATA[ + <br /> + <br /> + <img src="vpn_icon" /> + ]]> appears on your screen when VPN is active. + </string> + <!-- Dialog title for built-in VPN. [CHAR LIMIT=40] --> <string name="legacy_title">VPN is connected</string> <!-- Label for the name of the current VPN session. [CHAR LIMIT=20] --> diff --git a/services/api/current.txt b/services/api/current.txt index 17ca369c62ab..7c5c01ef6868 100644 --- a/services/api/current.txt +++ b/services/api/current.txt @@ -80,6 +80,14 @@ package com.android.server { } +package com.android.server.stats { + + public final class StatsHelper { + method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context); + } + +} + package com.android.server.wifi { public class SupplicantManager { diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt index 647739f34cf2..6419b7088fdb 100644 --- a/services/api/non-updatable-current.txt +++ b/services/api/non-updatable-current.txt @@ -35,6 +35,14 @@ package com.android.server { } +package com.android.server.stats { + + public final class StatsHelper { + method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context); + } + +} + package com.android.server.wifi { public class SupplicantManager { diff --git a/services/core/Android.bp b/services/core/Android.bp index c40afbfe4f97..0ac8f74ff831 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -210,6 +210,7 @@ filegroup { "java/com/android/server/connectivity/AutodestructReference.java", "java/com/android/server/connectivity/ConnectivityConstants.java", "java/com/android/server/connectivity/DnsManager.java", + "java/com/android/server/connectivity/FullScore.java", "java/com/android/server/connectivity/KeepaliveTracker.java", "java/com/android/server/connectivity/LingerMonitor.java", "java/com/android/server/connectivity/MockableSystemProperties.java", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 6fcb3f74c757..e3ce7a31b97e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -30,6 +30,9 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTI import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS; 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.BLOCKED_METERED_REASON_MASK; +import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN; +import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.TYPE_BLUETOOTH; @@ -75,7 +78,6 @@ import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE; import static android.net.NetworkPolicyManager.blockedReasonsToString; import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST; import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired; @@ -108,6 +110,7 @@ import android.net.ConnectionInfo; import android.net.ConnectivityDiagnosticsManager.ConnectivityReport; import android.net.ConnectivityDiagnosticsManager.DataStallReport; import android.net.ConnectivityManager; +import android.net.ConnectivityManager.BlockedReason; import android.net.ConnectivityManager.NetworkCallback; import android.net.ConnectivityManager.RestrictBackgroundStatus; import android.net.ConnectivityResources; @@ -315,6 +318,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // The maximum number of network request allowed per uid before an exception is thrown. private static final int MAX_NETWORK_REQUESTS_PER_UID = 100; + // The maximum number of network request allowed for system UIDs before an exception is thrown. + private static final int MAX_NETWORK_REQUESTS_PER_SYSTEM_UID = 250; + @VisibleForTesting protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it. @VisibleForTesting @@ -330,6 +336,7 @@ public class ConnectivityService extends IConnectivityManager.Stub protected final PermissionMonitor mPermissionMonitor; private final PerUidCounter mNetworkRequestCounter; + private final PerUidCounter mSystemNetworkRequestCounter; private volatile boolean mLockdownEnabled; @@ -1146,8 +1153,8 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * @see NetworkUtils#queryUserAccess(int, int) */ - public boolean queryUserAccess(int uid, int netId) { - return NetworkUtils.queryUserAccess(uid, netId); + public boolean queryUserAccess(int uid, Network network, ConnectivityService cs) { + return cs.queryUserAccess(uid, network); } /** @@ -1198,12 +1205,13 @@ public class ConnectivityService extends IConnectivityManager.Stub mContext = Objects.requireNonNull(context, "missing Context"); mResources = deps.getResources(mContext); mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID); + mSystemNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_SYSTEM_UID); mMetricsLog = logger; mNetworkRanker = new NetworkRanker(); final NetworkRequest defaultInternetRequest = createDefaultRequest(); mDefaultRequest = new NetworkRequestInfo( - defaultInternetRequest, null, + Process.myUid(), defaultInternetRequest, null, new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO, null /* attributionTags */); mNetworkRequests.put(defaultInternetRequest, mDefaultRequest); @@ -1410,8 +1418,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - networkRequest, null, - new Binder(), + Process.myUid(), networkRequest, null, new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO, null /* attributionTags */)); } else { @@ -1549,16 +1556,16 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkInfoBlockingLogs.log(action + " " + uid); } - private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net, - boolean blocked) { + private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net, int blocked) { if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) { return; } - final String action = blocked ? "BLOCKED" : "UNBLOCKED"; + final String action = (blocked != 0) ? "BLOCKED" : "UNBLOCKED"; 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())); + "%s %d(%d) on netId %d: %s", action, nri.mAsUid, requestId, net.getNetId(), + blockedReasonsToString(blocked))); } /** @@ -2073,6 +2080,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc, int callerUid, String callerPackageName) { if (!checkSettingsPermission()) { + // There is no need to track the effective UID of the request here. If the caller lacks + // the settings permission, the effective UID is the same as the calling ID. nc.setSingleUid(callerUid); } nc.setRequestorUidAndPackageName(callerUid, callerPackageName); @@ -2337,15 +2346,15 @@ public class ConnectivityService extends IConnectivityManager.Stub private final NetworkPolicyCallback mPolicyCallback = new NetworkPolicyCallback() { @Override - public void onUidBlockedReasonChanged(int uid, int blockedReasons) { + public void onUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_BLOCKED_REASON_CHANGED, uid, blockedReasons)); } }; - void handleUidBlockedReasonChanged(int uid, int blockedReasons) { + private void handleUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) { maybeNotifyNetworkBlockedForNewState(uid, blockedReasons); - mUidBlockedReasons.put(uid, blockedReasons); + setUidBlockedReasons(uid, blockedReasons); } private boolean checkAnyPermissionOf(String... permissions) { @@ -3118,6 +3127,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } break; } + case NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED: { + if (msg.arg1 >= 0 && msg.arg1 <= NetworkAgent.MAX_TEARDOWN_DELAY_MS) { + nai.teardownDelayMs = msg.arg1; + } else { + logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1); + } + } } } @@ -3688,6 +3704,23 @@ public class ConnectivityService extends IConnectivityManager.Stub mLegacyTypeTracker.remove(nai, wasDefault); rematchAllNetworksAndRequests(); mLingerMonitor.noteDisconnect(nai); + + // Immediate teardown. + if (nai.teardownDelayMs == 0) { + destroyNetwork(nai); + return; + } + + // Delayed teardown. + try { + mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM); + } catch (RemoteException e) { + Log.d(TAG, "Error marking network restricted during teardown: " + e); + } + mHandler.postDelayed(() -> destroyNetwork(nai), nai.teardownDelayMs); + } + + private void destroyNetwork(NetworkAgentInfo nai) { if (nai.created) { // Tell netd to clean up the configuration for this network // (routing rules, DNS, etc). @@ -3700,6 +3733,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mDnsManager.removeNetwork(nai.network); } mNetIdManager.releaseNetId(nai.network.getNetId()); + nai.onNetworkDestroyed(); } private boolean createNativeNetwork(@NonNull NetworkAgentInfo networkAgent) { @@ -4000,7 +4034,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } - mNetworkRequestCounter.decrementCount(nri.mUid); + decrementRequestCount(nri); mNetworkRequestInfoLogs.log("RELEASE " + nri); if (null != nri.getActiveRequest()) { @@ -4111,6 +4145,20 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private PerUidCounter getRequestCounter(NetworkRequestInfo nri) { + return checkAnyPermissionOf( + nri.mPid, nri.mUid, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + ? mSystemNetworkRequestCounter : mNetworkRequestCounter; + } + + private void incrementRequestCountOrThrow(NetworkRequestInfo nri) { + getRequestCounter(nri).incrementCountOrThrow(nri.mUid); + } + + private void decrementRequestCount(NetworkRequestInfo nri) { + getRequestCounter(nri).decrementCount(nri.mUid); + } + @Override public void setAcceptUnvalidated(Network network, boolean accept, boolean always) { enforceNetworkStackSettingsOrSetup(); @@ -4838,6 +4886,42 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.networkMonitor().forceReevaluation(uid); } + // TODO: call into netd. + private boolean queryUserAccess(int uid, Network network) { + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); + if (nai == null) return false; + + // Any UID can use its default network. + if (nai == getDefaultNetworkForUid(uid)) return true; + + // Privileged apps can use any network. + if (mPermissionMonitor.hasRestrictedNetworksPermission(uid)) { + return true; + } + + // An unprivileged UID can use a VPN iff the VPN applies to it. + if (nai.isVPN()) { + return nai.networkCapabilities.appliesToUid(uid); + } + + // An unprivileged UID can bypass the VPN that applies to it only if it can protect its + // sockets, i.e., if it is the owner. + final NetworkAgentInfo vpn = getVpnForUid(uid); + if (vpn != null && !vpn.networkAgentConfig.allowBypass + && uid != vpn.networkCapabilities.getOwnerUid()) { + return false; + } + + // The UID's permission must be at least sufficient for the network. Since the restricted + // permission was already checked above, that just leaves background networks. + if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_FOREGROUND)) { + return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid); + } + + // Unrestricted network. Anyone gets to use it. + return true; + } + /** * Returns information about the proxy a certain network is using. If given a null network, it * it will return the proxy for the bound network for the caller app or the default proxy if @@ -4858,7 +4942,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return null; } return getLinkPropertiesProxyInfo(activeNetwork); - } else if (mDeps.queryUserAccess(mDeps.getCallingUid(), network.getNetId())) { + } else if (mDeps.queryUserAccess(mDeps.getCallingUid(), network, this)) { // Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which // caller may not have. return getLinkPropertiesProxyInfo(network); @@ -5085,7 +5169,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) { - PermissionUtils.enforceNetworkStackPermission(mContext); + enforceNetworkStackOrSettingsPermission(); mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS, encodeBool(requireVpn), 0 /* arg2 */, ranges)); } @@ -5123,7 +5207,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void setLegacyLockdownVpnEnabled(boolean enabled) { - enforceSettingsPermission(); + enforceNetworkStackOrSettingsPermission(); mHandler.post(() -> mLockdownEnabled = enabled); } @@ -5363,6 +5447,8 @@ public class ConnectivityService extends IConnectivityManager.Stub boolean mPendingIntentSent; @Nullable final Messenger mMessenger; + + // Information about the caller that caused this object to be created. @Nullable private final IBinder mBinder; final int mPid; @@ -5370,6 +5456,13 @@ public class ConnectivityService extends IConnectivityManager.Stub final @NetworkCallback.Flag int mCallbackFlags; @Nullable final String mCallingAttributionTag; + + // Effective UID of this request. This is different from mUid when a privileged process + // files a request on behalf of another UID. This UID is used to determine blocked status, + // UID matching, and so on. mUid above is used for permission checks and to enforce the + // maximum limit of registered callbacks per UID. + final int mAsUid; + // In order to preserve the mapping of NetworkRequest-to-callback when apps register // callbacks using a returned NetworkRequest, the original NetworkRequest needs to be // maintained for keying off of. This is only a concern when the original nri @@ -5397,12 +5490,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return (null == uids) ? new ArraySet<>() : uids; } - NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final PendingIntent pi, - @Nullable String callingAttributionTag) { - this(Collections.singletonList(r), r, pi, callingAttributionTag); + NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r, + @Nullable final PendingIntent pi, @Nullable String callingAttributionTag) { + this(asUid, Collections.singletonList(r), r, pi, callingAttributionTag); } - NetworkRequestInfo(@NonNull final List<NetworkRequest> r, + NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r, @NonNull final NetworkRequest requestForCallback, @Nullable final PendingIntent pi, @Nullable String callingAttributionTag) { ensureAllNetworkRequestsHaveType(r); @@ -5413,7 +5506,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mBinder = null; mPid = getCallingPid(); mUid = mDeps.getCallingUid(); - mNetworkRequestCounter.incrementCountOrThrow(mUid); + mAsUid = asUid; + incrementRequestCountOrThrow(this); /** * Location sensitive data not included in pending intent. Only included in * {@link NetworkCallback}. @@ -5422,14 +5516,15 @@ public class ConnectivityService extends IConnectivityManager.Stub mCallingAttributionTag = callingAttributionTag; } - NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m, + NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r, @Nullable final Messenger m, @Nullable final IBinder binder, @NetworkCallback.Flag int callbackFlags, @Nullable String callingAttributionTag) { - this(Collections.singletonList(r), r, m, binder, callbackFlags, callingAttributionTag); + this(asUid, Collections.singletonList(r), r, m, binder, callbackFlags, + callingAttributionTag); } - NetworkRequestInfo(@NonNull final List<NetworkRequest> r, + NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r, @NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m, @Nullable final IBinder binder, @NetworkCallback.Flag int callbackFlags, @@ -5442,8 +5537,9 @@ public class ConnectivityService extends IConnectivityManager.Stub mBinder = binder; mPid = getCallingPid(); mUid = mDeps.getCallingUid(); + mAsUid = asUid; mPendingIntent = null; - mNetworkRequestCounter.incrementCountOrThrow(mUid); + incrementRequestCountOrThrow(this); mCallbackFlags = callbackFlags; mCallingAttributionTag = callingAttributionTag; @@ -5484,18 +5580,19 @@ public class ConnectivityService extends IConnectivityManager.Stub mBinder = nri.mBinder; mPid = nri.mPid; mUid = nri.mUid; + mAsUid = nri.mAsUid; mPendingIntent = nri.mPendingIntent; - mNetworkRequestCounter.incrementCountOrThrow(mUid); + incrementRequestCountOrThrow(this); mCallbackFlags = nri.mCallbackFlags; mCallingAttributionTag = nri.mCallingAttributionTag; } - NetworkRequestInfo(@NonNull final NetworkRequest r) { - this(Collections.singletonList(r)); + NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r) { + this(asUid, Collections.singletonList(r)); } - NetworkRequestInfo(@NonNull final List<NetworkRequest> r) { - this(r, r.get(0), null /* pi */, null /* callingAttributionTag */); + NetworkRequestInfo(int asUid, @NonNull final List<NetworkRequest> r) { + this(asUid, r, r.get(0), null /* pi */, null /* callingAttributionTag */); } // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer @@ -5531,9 +5628,10 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public String toString() { - return "uid/pid:" + mUid + "/" + mPid + " active request Id: " + final String asUidString = (mAsUid == mUid) ? "" : " asUid: " + mAsUid; + return "uid/pid:" + mUid + "/" + mPid + asUidString + " activeRequest: " + (mActiveRequest == null ? null : mActiveRequest.requestId) - + " callback request Id: " + + " callbackRequest: " + mNetworkRequestForCallback.requestId + " " + mRequests + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent) @@ -5634,7 +5732,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, + public NetworkRequest requestNetwork(int asUid, NetworkCapabilities networkCapabilities, int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder, int legacyType, int callbackFlags, @NonNull String callingPackageName, @Nullable String callingAttributionTag) { @@ -5646,6 +5744,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } final NetworkCapabilities defaultNc = mDefaultRequest.mRequests.get(0).networkCapabilities; final int callingUid = mDeps.getCallingUid(); + // Privileged callers can track the default network of another UID by passing in a UID. + if (asUid != Process.INVALID_UID) { + enforceSettingsPermission(); + } else { + asUid = callingUid; + } final NetworkRequest.Type reqType; try { reqType = NetworkRequest.Type.values()[reqTypeInt]; @@ -5655,10 +5759,10 @@ public class ConnectivityService extends IConnectivityManager.Stub switch (reqType) { case TRACK_DEFAULT: // If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities} - // is unused and will be replaced by ones appropriate for the caller. - // This allows callers to keep track of the default network for their app. + // is unused and will be replaced by ones appropriate for the UID (usually, the + // calling app). This allows callers to keep track of the default network. networkCapabilities = copyDefaultNetworkCapabilitiesForUid( - defaultNc, callingUid, callingPackageName); + defaultNc, asUid, callingUid, callingPackageName); enforceAccessPermission(); break; case TRACK_SYSTEM_DEFAULT: @@ -5710,7 +5814,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId(), reqType); final NetworkRequestInfo nri = getNriToRegister( - networkRequest, messenger, binder, callbackFlags, callingAttributionTag); + asUid, networkRequest, messenger, binder, callbackFlags, + callingAttributionTag); if (DBG) log("requestNetwork for " + nri); // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were @@ -5737,25 +5842,27 @@ public class ConnectivityService extends IConnectivityManager.Stub * requests registered to track the default request. If there is currently a per-app default * tracking the app requestor, then we need to create a version of this nri that mirrors that of * the tracking per-app default so that callbacks are sent to the app requestor appropriately. + * @param asUid the uid on behalf of which to file the request. Different from requestorUid + * when a privileged caller is tracking the default network for another uid. * @param nr the network request for the nri. * @param msgr the messenger for the nri. * @param binder the binder for the nri. * @param callingAttributionTag the calling attribution tag for the nri. * @return the nri to register. */ - private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr, + private NetworkRequestInfo getNriToRegister(final int asUid, @NonNull final NetworkRequest nr, @Nullable final Messenger msgr, @Nullable final IBinder binder, @NetworkCallback.Flag int callbackFlags, @Nullable String callingAttributionTag) { final List<NetworkRequest> requests; if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) { requests = copyDefaultNetworkRequestsForUid( - nr.getRequestorUid(), nr.getRequestorPackageName()); + asUid, nr.getRequestorUid(), nr.getRequestorPackageName()); } else { requests = Collections.singletonList(nr); } return new NetworkRequestInfo( - requests, nr, msgr, binder, callbackFlags, callingAttributionTag); + asUid, requests, nr, msgr, binder, callbackFlags, callingAttributionTag); } private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities, @@ -5836,8 +5943,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.REQUEST); - NetworkRequestInfo nri = - new NetworkRequestInfo(networkRequest, operation, callingAttributionTag); + NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, networkRequest, operation, + callingAttributionTag); if (DBG) log("pendingRequest for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT, nri)); @@ -5904,7 +6011,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); NetworkRequestInfo nri = - new NetworkRequestInfo(networkRequest, messenger, binder, callbackFlags, + new NetworkRequestInfo(callingUid, networkRequest, messenger, binder, callbackFlags, callingAttributionTag); if (VDBG) log("listenForNetwork for " + nri); @@ -5929,8 +6036,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); - NetworkRequestInfo nri = - new NetworkRequestInfo(networkRequest, operation, callingAttributionTag); + NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, networkRequest, operation, + callingAttributionTag); if (VDBG) log("pendingListenForNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); @@ -6080,33 +6187,37 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Get a copy of the network requests of the default request that is currently tracking the * given uid. + * @param asUid the uid on behalf of which to file the request. Different from requestorUid + * when a privileged caller is tracking the default network for another uid. * @param requestorUid the uid to check the default for. * @param requestorPackageName the requestor's package name. * @return a copy of the default's NetworkRequest that is tracking the given uid. */ @NonNull private List<NetworkRequest> copyDefaultNetworkRequestsForUid( - @NonNull final int requestorUid, @NonNull final String requestorPackageName) { + final int asUid, final int requestorUid, @NonNull final String requestorPackageName) { return copyNetworkRequestsForUid( - getDefaultRequestTrackingUid(requestorUid).mRequests, - requestorUid, requestorPackageName); + getDefaultRequestTrackingUid(asUid).mRequests, + asUid, requestorUid, requestorPackageName); } /** * Copy the given nri's NetworkRequest collection. * @param requestsToCopy the NetworkRequest collection to be copied. + * @param asUid the uid on behalf of which to file the request. Different from requestorUid + * when a privileged caller is tracking the default network for another uid. * @param requestorUid the uid to set on the copied collection. * @param requestorPackageName the package name to set on the copied collection. * @return the copied NetworkRequest collection. */ @NonNull private List<NetworkRequest> copyNetworkRequestsForUid( - @NonNull final List<NetworkRequest> requestsToCopy, @NonNull final int requestorUid, - @NonNull final String requestorPackageName) { + @NonNull final List<NetworkRequest> requestsToCopy, final int asUid, + final int requestorUid, @NonNull final String requestorPackageName) { final List<NetworkRequest> requests = new ArrayList<>(); for (final NetworkRequest nr : requestsToCopy) { requests.add(new NetworkRequest(copyDefaultNetworkCapabilitiesForUid( - nr.networkCapabilities, requestorUid, requestorPackageName), + nr.networkCapabilities, asUid, requestorUid, requestorPackageName), nr.legacyType, nextNetworkRequestId(), nr.type)); } return requests; @@ -6114,17 +6225,17 @@ public class ConnectivityService extends IConnectivityManager.Stub @NonNull private NetworkCapabilities copyDefaultNetworkCapabilitiesForUid( - @NonNull final NetworkCapabilities netCapToCopy, @NonNull final int requestorUid, - @NonNull final String requestorPackageName) { + @NonNull final NetworkCapabilities netCapToCopy, final int asUid, + final int requestorUid, @NonNull final String requestorPackageName) { // These capabilities are for a TRACK_DEFAULT callback, so: // 1. Remove NET_CAPABILITY_VPN, because it's (currently!) the only difference between // mDefaultRequest and a per-UID default request. // TODO: stop depending on the fact that these two unrelated things happen to be the same - // 2. Always set the UIDs to mAsUid. restrictRequestUidsForCallerAndSetRequestorInfo will + // 2. Always set the UIDs to asUid. restrictRequestUidsForCallerAndSetRequestorInfo will // not do this in the case of a privileged application. final NetworkCapabilities netCap = new NetworkCapabilities(netCapToCopy); netCap.removeCapability(NET_CAPABILITY_NOT_VPN); - netCap.setSingleUid(requestorUid); + netCap.setSingleUid(asUid); restrictRequestUidsForCallerAndSetRequestorInfo( netCap, requestorUid, requestorPackageName); return netCap; @@ -6185,8 +6296,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // Request used to optionally keep vehicle internal network always active private final NetworkRequest mDefaultVehicleRequest; - // TODO replace with INetd.DUMMY_NET_ID when available. - private static final int NO_SERVICE_NET_ID = 51; + // TODO replace with INetd.UNREACHABLE_NET_ID when available. + private static final int NO_SERVICE_NET_ID = 52; // Sentinel NAI used to direct apps with default networks that should have no connectivity to a // network with no service. This NAI should never be matched against, nor should any public API // ever return the associated network. For this reason, this NAI is not in the list of available @@ -7280,7 +7391,7 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case ConnectivityManager.CALLBACK_BLK_CHANGED: { - maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1 != 0); + maybeLogBlockedStatusChanged(nri, networkAgent.network, arg1); msg.arg1 = arg1; break; } @@ -7936,6 +8047,7 @@ public class ConnectivityService extends IConnectivityManager.Stub updateCapabilitiesForNetwork(networkAgent); } networkAgent.created = true; + networkAgent.onNetworkCreated(); } if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) { @@ -8023,12 +8135,11 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } + final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE); final boolean metered = nai.networkCapabilities.isMetered(); - boolean blocked; - blocked = isUidBlockedByVpn(nri.mUid, mVpnBlockedUidRanges); - blocked |= NetworkPolicyManager.isUidBlocked( - mUidBlockedReasons.get(nri.mUid, BLOCKED_REASON_NONE), metered); - callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0); + final boolean vpnBlocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges); + callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, + getBlockedState(blockedReasons, metered, vpnBlocked)); } // Notify the requests on this NAI that the network is now lingered. @@ -8037,6 +8148,21 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime); } + private static int getBlockedState(int reasons, boolean metered, boolean vpnBlocked) { + if (!metered) reasons &= ~BLOCKED_METERED_REASON_MASK; + return vpnBlocked + ? reasons | BLOCKED_REASON_LOCKDOWN_VPN + : reasons & ~BLOCKED_REASON_LOCKDOWN_VPN; + } + + private void setUidBlockedReasons(int uid, @BlockedReason int blockedReasons) { + if (blockedReasons == BLOCKED_REASON_NONE) { + mUidBlockedReasons.delete(uid); + } else { + mUidBlockedReasons.put(uid, blockedReasons); + } + } + /** * Notify of the blocked state apps with a registered callback matching a given NAI. * @@ -8044,7 +8170,10 @@ public class ConnectivityService extends IConnectivityManager.Stub * any given nai, all requests need to be considered according to the uid who filed it. * * @param nai The target NetworkAgentInfo. - * @param oldMetered True if the previous network capabilities is metered. + * @param oldMetered True if the previous network capabilities were metered. + * @param newMetered True if the current network capabilities are metered. + * @param oldBlockedUidRanges list of UID ranges previously blocked by lockdown VPN. + * @param newBlockedUidRanges list of UID ranges blocked by lockdown VPN. */ private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered, boolean newMetered, List<UidRange> oldBlockedUidRanges, @@ -8053,22 +8182,18 @@ public class ConnectivityService extends IConnectivityManager.Stub for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest nr = nai.requestAt(i); NetworkRequestInfo nri = mNetworkRequests.get(nr); - final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked; - oldVpnBlocked = isUidBlockedByVpn(nri.mUid, oldBlockedUidRanges); - newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges) - ? isUidBlockedByVpn(nri.mUid, newBlockedUidRanges) + final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE); + final boolean oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges); + final boolean newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges) + ? isUidBlockedByVpn(nri.mAsUid, newBlockedUidRanges) : oldVpnBlocked; - final int blockedReasons = mUidBlockedReasons.get(nri.mUid, BLOCKED_REASON_NONE); - oldBlocked = oldVpnBlocked || NetworkPolicyManager.isUidBlocked( - blockedReasons, oldMetered); - newBlocked = newVpnBlocked || NetworkPolicyManager.isUidBlocked( - blockedReasons, newMetered); - - if (oldBlocked != newBlocked) { + final int oldBlockedState = getBlockedState(blockedReasons, oldMetered, oldVpnBlocked); + final int newBlockedState = getBlockedState(blockedReasons, newMetered, newVpnBlocked); + if (oldBlockedState != newBlockedState) { callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, - encodeBool(newBlocked)); + newBlockedState); } } } @@ -8078,25 +8203,23 @@ public class ConnectivityService extends IConnectivityManager.Stub * @param uid The uid for which the rules changed. * @param blockedReasons The reasons for why an uid is blocked. */ - private void maybeNotifyNetworkBlockedForNewState(int uid, int blockedReasons) { + private void maybeNotifyNetworkBlockedForNewState(int uid, @BlockedReason int blockedReasons) { for (final NetworkAgentInfo nai : mNetworkAgentInfos) { final boolean metered = nai.networkCapabilities.isMetered(); final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges); - final boolean oldBlocked, newBlocked; - oldBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked( - mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered); - newBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked( - blockedReasons, metered); - if (oldBlocked == newBlocked) { + final int oldBlockedState = getBlockedState( + mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered, vpnBlocked); + final int newBlockedState = getBlockedState(blockedReasons, metered, vpnBlocked); + if (oldBlockedState == newBlockedState) { continue; } - final int arg = encodeBool(newBlocked); for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest nr = nai.requestAt(i); NetworkRequestInfo nri = mNetworkRequests.get(nr); - if (nri != null && nri.mUid == uid) { - callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg); + if (nri != null && nri.mAsUid == uid) { + callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, + newBlockedState); } } } @@ -8644,7 +8767,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(). - mNetworkRequestCounter.decrementCount(nri.mUid); + decrementRequestCount(nri); return; } @@ -8710,7 +8833,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(). - mNetworkRequestCounter.decrementCount(nri.mUid); + decrementRequestCount(nri); iCb.unlinkToDeath(cbInfo, 0); } @@ -8860,7 +8983,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // nri is not bound to the death of callback. Instead, callback.bindToDeath() is set in // handleRegisterConnectivityDiagnosticsCallback(). nri will be cleaned up as part of the // callback's binder death. - final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId); + final NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, requestWithId); final ConnectivityDiagnosticsCallbackInfo cbInfo = new ConnectivityDiagnosticsCallbackInfo(callback, nri, callingPackageName); @@ -9344,7 +9467,7 @@ public class ConnectivityService extends IConnectivityManager.Stub nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities)); nrs.add(createDefaultRequest()); setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids())); - final NetworkRequestInfo nri = new NetworkRequestInfo(nrs); + final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs); result.add(nri); } return result; @@ -9515,7 +9638,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Include this nri if it will be tracked by the new per-app default requests. final boolean isNriGoingToBeTracked = - getDefaultRequestTrackingUid(nri.mUid) != mDefaultRequest; + getDefaultRequestTrackingUid(nri.mAsUid) != mDefaultRequest; if (isNriGoingToBeTracked) { defaultCallbackRequests.add(nri); } @@ -9537,7 +9660,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final ArraySet<NetworkRequestInfo> callbackRequestsToRegister = new ArraySet<>(); for (final NetworkRequestInfo callbackRequest : perAppCallbackRequestsForUpdate) { final NetworkRequestInfo trackingNri = - getDefaultRequestTrackingUid(callbackRequest.mUid); + getDefaultRequestTrackingUid(callbackRequest.mAsUid); // If this nri is not being tracked, the change it back to an untracked nri. if (trackingNri == mDefaultRequest) { @@ -9547,12 +9670,12 @@ public class ConnectivityService extends IConnectivityManager.Stub continue; } - final String requestorPackageName = - callbackRequest.mRequests.get(0).getRequestorPackageName(); + final NetworkRequest request = callbackRequest.mRequests.get(0); callbackRequestsToRegister.add(new NetworkRequestInfo( callbackRequest, copyNetworkRequestsForUid( - trackingNri.mRequests, callbackRequest.mUid, requestorPackageName))); + trackingNri.mRequests, callbackRequest.mAsUid, + callbackRequest.mUid, request.getRequestorPackageName()))); } return callbackRequestsToRegister; } @@ -9656,7 +9779,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ranges.add(new UidRange(uid, uid)); } setNetworkRequestUids(requests, ranges); - return new NetworkRequestInfo(requests); + return new NetworkRequestInfo(Process.myUid(), requests); } private NetworkRequest createUnmeteredNetworkRequest() { diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 4c3c6ef21fc5..794cb9301d69 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -1651,7 +1651,7 @@ public class IpSecService extends IIpSecService.Stub { c.getMode(), c.getSourceAddress(), c.getDestinationAddress(), - (c.getNetwork() != null) ? c.getNetwork().netId : 0, + (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0, spiRecord.getSpi(), c.getMarkValue(), c.getMarkMask(), diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 3148a6205871..1241b77798ff 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -270,18 +270,9 @@ public final class PinnerService extends SystemService { * Handler for on start pinning message */ private void handlePinOnStart() { - final String bootImage = SystemProperties.get("dalvik.vm.boot-image", ""); - String[] filesToPin = null; - if (bootImage.endsWith("boot-image.prof")) { - // Use the files listed for that specific boot image. - // TODO: find a better way to know we're using the JIT zygote configuration. - filesToPin = mContext.getResources().getStringArray( - com.android.internal.R.array.config_jitzygoteBootImagePinnerServiceFiles); - } else { - // Files to pin come from the overlay and can be specified per-device config - filesToPin = mContext.getResources().getStringArray( - com.android.internal.R.array.config_defaultPinnerServiceFiles); - } + // Files to pin come from the overlay and can be specified per-device config + String[] filesToPin = mContext.getResources().getStringArray( + com.android.internal.R.array.config_defaultPinnerServiceFiles); // Continue trying to pin each file even if we fail to pin some of them for (String fileToPin : filesToPin) { PinnedFile pf = pinFile(fileToPin, @@ -291,10 +282,32 @@ public final class PinnerService extends SystemService { Slog.e(TAG, "Failed to pin file = " + fileToPin); continue; } - synchronized (this) { mPinnedFiles.add(pf); } + if (fileToPin.endsWith(".jar") | fileToPin.endsWith(".apk")) { + // Check whether the runtime has compilation artifacts to pin. + String arch = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]); + String[] files = null; + try { + files = DexFile.getDexFileOutputPaths(fileToPin, arch); + } catch (IOException ioe) { } + if (files == null) { + continue; + } + for (String file : files) { + PinnedFile df = pinFile(file, + Integer.MAX_VALUE, + /*attemptPinIntrospection=*/false); + if (df == null) { + Slog.i(TAG, "Failed to pin ART file = " + file); + continue; + } + synchronized (this) { + mPinnedFiles.add(df); + } + } + } } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 78ffcbdaff4d..0affda4fd561 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -402,10 +402,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { switch (msg.what) { case MSG_USER_SWITCHED: { if (VDBG) log("MSG_USER_SWITCHED userId=" + msg.arg1); - int numPhones = getTelephonyManager().getPhoneCount(); - for (int sub = 0; sub < numPhones; sub++) { - TelephonyRegistry.this.notifyCellLocationForSubscriber(sub, - mCellIdentity[sub]); + int numPhones = getTelephonyManager().getActiveModemCount(); + for (int phoneId = 0; phoneId < numPhones; phoneId++) { + int[] subIds = SubscriptionManager.getSubId(phoneId); + int subId = + (subIds != null) && (subIds.length > 0) + ? subIds[0] + : SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; + TelephonyRegistry.this.notifyCellLocationForSubscriber( + subId, mCellIdentity[phoneId], true /* hasUserSwitched */); } break; } @@ -1170,7 +1175,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)) { try { r.callback.onPhysicalChannelConfigChanged( - mPhysicalChannelConfigs); + shouldSanitizeLocationForPhysicalChannelConfig(r) + ? getLocationSanitizedConfigs(mPhysicalChannelConfigs) + : mPhysicalChannelConfigs); } catch (RemoteException ex) { remove(r.binder); } @@ -1877,20 +1884,20 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void notifyCellLocation(CellIdentity cellLocation) { - notifyCellLocationForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cellLocation); + public void notifyCellLocationForSubscriber(int subId, CellIdentity cellIdentity) { + notifyCellLocationForSubscriber(subId, cellIdentity, false /* hasUserSwitched */); } - @Override - public void notifyCellLocationForSubscriber(int subId, CellIdentity cellIdentity) { - log("notifyCellLocationForSubscriber: subId=" + subId - + " cellIdentity=" + cellIdentity); + private void notifyCellLocationForSubscriber(int subId, CellIdentity cellIdentity, + boolean hasUserSwitched) { + log("notifyCellLocationForSubscriber: subId=" + subId + " cellIdentity=" + cellIdentity); if (!checkNotifyPermission("notifyCellLocation()")) { return; } int phoneId = getPhoneIdFromSubId(subId); synchronized (mRecords) { - if (validatePhoneId(phoneId) && !Objects.equals(cellIdentity, mCellIdentity[phoneId])) { + if (validatePhoneId(phoneId) + && (hasUserSwitched || !Objects.equals(cellIdentity, mCellIdentity[phoneId]))) { mCellIdentity[phoneId] = cellIdentity; for (Record r : mRecords) { if (validateEventAndUserLocked( @@ -2371,8 +2378,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return; } + List<PhysicalChannelConfig> sanitizedConfigs = getLocationSanitizedConfigs(configs); if (VDBG) { - log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs); + log("notifyPhysicalChannelConfig: subId=" + subId + " configs=" + configs + + " sanitizedConfigs=" + sanitizedConfigs); } synchronized (mRecords) { @@ -2385,11 +2394,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { && idMatch(r.subId, subId, phoneId)) { try { if (DBG_LOC) { - log("notifyPhysicalChannelConfig: " - + "mPhysicalChannelConfigs=" - + configs + " r=" + r); + log("notifyPhysicalChannelConfig: mPhysicalChannelConfigs=" + + (shouldSanitizeLocationForPhysicalChannelConfig(r) + ? sanitizedConfigs : configs) + + " r=" + r); } - r.callback.onPhysicalChannelConfigChanged(configs); + r.callback.onPhysicalChannelConfigChanged( + shouldSanitizeLocationForPhysicalChannelConfig(r) + ? sanitizedConfigs : configs); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2400,6 +2412,25 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + private static boolean shouldSanitizeLocationForPhysicalChannelConfig(Record record) { + // Always redact location info from PhysicalChannelConfig if the registrant is from neither + // PHONE nor SYSTEM process. There is no user case that the registrant needs the location + // info (e.g. physicalCellId). This also remove the need for the location permissions check. + return record.callerUid != Process.PHONE_UID && record.callerUid != Process.SYSTEM_UID; + } + + /** + * Return a copy of the PhysicalChannelConfig list but with location info removed. + */ + private static List<PhysicalChannelConfig> getLocationSanitizedConfigs( + List<PhysicalChannelConfig> configs) { + List<PhysicalChannelConfig> sanitizedConfigs = new ArrayList<>(configs.size()); + for (PhysicalChannelConfig config : configs) { + sanitizedConfigs.add(config.createLocationInfoSanitizedCopy()); + } + return sanitizedConfigs; + } + /** * Notify that the data enabled has changed. * @@ -2527,7 +2558,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { final int recordCount = mRecords.size(); pw.println("last known state:"); pw.increaseIndent(); - for (int i = 0; i < getTelephonyManager().getPhoneCount(); i++) { + for (int i = 0; i < getTelephonyManager().getActiveModemCount(); i++) { pw.println("Phone Id=" + i); pw.increaseIndent(); pw.println("mCallState=" + mCallState[i]); diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 6c18cdea51fa..4622e98bbbb2 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -29,7 +29,10 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; @@ -158,6 +161,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb; @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker; @NonNull private final VcnContext mVcnContext; + @NonNull private final BroadcastReceiver mPkgChangeReceiver; /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */ @Nullable private LocationPermissionChecker mLocationPermissionChecker; @@ -203,6 +207,29 @@ public class VcnManagementService extends IVcnManagementService.Stub { mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE); mVcnContext = mDeps.newVcnContext(mContext, mLooper, mNetworkProvider); + mPkgChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + + if (Intent.ACTION_PACKAGE_ADDED.equals(action) + || Intent.ACTION_PACKAGE_REPLACED.equals(action) + || Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + mTelephonySubscriptionTracker.handleSubscriptionsChanged(); + } else { + Log.wtf(TAG, "received unexpected intent: " + action); + } + } + }; + + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); + intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + intentFilter.addDataScheme("package"); + mContext.registerReceiver( + mPkgChangeReceiver, intentFilter, null /* broadcastPermission */, mHandler); + // Run on handler to ensure I/O does not block system server startup mHandler.post(() -> { PersistableBundle configBundle = null; @@ -349,7 +376,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } - private void enforceCallingUserAndCarrierPrivilege(ParcelUuid subscriptionGroup) { + private void enforceCallingUserAndCarrierPrivilege( + ParcelUuid subscriptionGroup, String pkgName) { // Only apps running in the primary (system) user are allowed to configure the VCN. This is // in line with Telephony's behavior with regards to binding to a Carrier App provided // CarrierConfigService. @@ -363,12 +391,15 @@ public class VcnManagementService extends IVcnManagementService.Stub { subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup)); }); - final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class); for (SubscriptionInfo info : subscriptionInfos) { + final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class) + .createForSubscriptionId(info.getSubscriptionId()); + // Check subscription is active first; much cheaper/faster check, and an app (currently) // cannot be carrier privileged for inactive subscriptions. if (subMgr.isValidSlotIndex(info.getSimSlotIndex()) - && telMgr.hasCarrierPrivileges(info.getSubscriptionId())) { + && telMgr.checkCarrierPrivilegesForPackage(pkgName) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { // TODO (b/173717728): Allow configuration for inactive, but manageable // subscriptions. // TODO (b/173718661): Check for whole subscription groups at a time. @@ -536,7 +567,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName()); - enforceCallingUserAndCarrierPrivilege(subscriptionGroup); + enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName); Binder.withCleanCallingIdentity(() -> { synchronized (mLock) { @@ -554,11 +585,14 @@ public class VcnManagementService extends IVcnManagementService.Stub { * <p>Implements the IVcnManagementService Binder interface. */ @Override - public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) { + public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); + requireNonNull(opPkgName, "opPkgName was null"); Slog.v(TAG, "VCN config cleared for subGrp: " + subscriptionGroup); - enforceCallingUserAndCarrierPrivilege(subscriptionGroup); + mContext.getSystemService(AppOpsManager.class) + .checkPackage(mDeps.getBinderCallingUid(), opPkgName); + enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName); Binder.withCleanCallingIdentity(() -> { synchronized (mLock) { @@ -821,8 +855,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { final IBinder cbBinder = callback.asBinder(); final VcnStatusCallbackInfo cbInfo = - new VcnStatusCallbackInfo( - subGroup, callback, opPkgName, mDeps.getBinderCallingUid()); + new VcnStatusCallbackInfo(subGroup, callback, opPkgName, callingUid); try { cbBinder.linkToDeath(cbInfo, 0 /* flags */); diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index b3373d0bb536..351231f34c4b 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -69,6 +69,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -101,6 +102,7 @@ public final class AppHibernationService extends SystemService { private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>(); private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore; private final Injector mInjector; + private final Executor mBackgroundExecutor; @VisibleForTesting boolean mIsServiceEnabled; @@ -126,6 +128,7 @@ public final class AppHibernationService extends SystemService { mIActivityManager = injector.getActivityManager(); mUserManager = injector.getUserManager(); mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore(); + mBackgroundExecutor = injector.getBackgroundExecutor(); mInjector = injector; final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); @@ -147,11 +150,13 @@ public final class AppHibernationService extends SystemService { @Override public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { - List<GlobalLevelState> states = - mGlobalLevelHibernationDiskStore.readHibernationStates(); - synchronized (mLock) { - initializeGlobalHibernationStates(states); - } + mBackgroundExecutor.execute(() -> { + List<GlobalLevelState> states = + mGlobalLevelHibernationDiskStore.readHibernationStates(); + synchronized (mLock) { + initializeGlobalHibernationStates(states); + } + }); } if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mIsServiceEnabled = isAppHibernationEnabled(); @@ -170,16 +175,15 @@ public final class AppHibernationService extends SystemService { * @return true if package is hibernating for the user */ boolean isHibernatingForUser(String packageName, int userId) { - if (!checkHibernationEnabled("isHibernatingForUser")) { + String methodName = "isHibernatingForUser"; + if (!checkHibernationEnabled(methodName)) { return false; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); - userId = handleIncomingUser(userId, "isHibernating"); - if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { - Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user " - + userId); + userId = handleIncomingUser(userId, methodName); + if (!checkUserStatesExist(userId, methodName)) { return false; } synchronized (mLock) { @@ -225,16 +229,15 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { - if (!checkHibernationEnabled("setHibernatingForUser")) { + String methodName = "setHibernatingForUser"; + if (!checkHibernationEnabled(methodName)) { return; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); - userId = handleIncomingUser(userId, "setHibernating"); - if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { - Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user " - + userId); + userId = handleIncomingUser(userId, methodName); + if (!checkUserStatesExist(userId, methodName)) { return; } synchronized (mLock) { @@ -298,16 +301,15 @@ public final class AppHibernationService extends SystemService { */ @NonNull List<String> getHibernatingPackagesForUser(int userId) { ArrayList<String> hibernatingPackages = new ArrayList<>(); - if (!checkHibernationEnabled("getHibernatingPackagesForUser")) { + String methodName = "getHibernatingPackagesForUser"; + if (!checkHibernationEnabled(methodName)) { return hibernatingPackages; } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); - userId = handleIncomingUser(userId, "getHibernatingPackagesForUser"); - if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { - Slog.w(TAG, "Attempt to get hibernating packages for a stopped or nonexistent user " - + userId); + userId = handleIncomingUser(userId, methodName); + if (!checkUserStatesExist(userId, methodName)) { return hibernatingPackages; } synchronized (mLock) { @@ -477,10 +479,15 @@ public final class AppHibernationService extends SystemService { HibernationStateDiskStore<UserLevelState> diskStore = mInjector.getUserLevelDiskStore(userId); mUserDiskStores.put(userId, diskStore); - List<UserLevelState> storedStates = diskStore.readHibernationStates(); - synchronized (mLock) { - initializeUserHibernationStates(userId, storedStates); - } + mBackgroundExecutor.execute(() -> { + List<UserLevelState> storedStates = diskStore.readHibernationStates(); + synchronized (mLock) { + // Ensure user hasn't stopped in the time to execute. + if (mUserManager.isUserUnlockingOrUnlocked(userId)) { + initializeUserHibernationStates(userId, storedStates); + } + } + }); } @Override @@ -550,6 +557,20 @@ public final class AppHibernationService extends SystemService { } } + private boolean checkUserStatesExist(int userId, String methodName) { + if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { + Slog.e(TAG, String.format( + "Attempt to call %s on stopped or nonexistent user %d", methodName, userId)); + return false; + } + if (!mUserStates.contains(userId)) { + Slog.w(TAG, String.format( + "Attempt to call %s before states have been read from disk", methodName)); + return false; + } + return true; + } + private boolean checkHibernationEnabled(String methodName) { if (!mIsServiceEnabled) { Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName)); @@ -720,6 +741,8 @@ public final class AppHibernationService extends SystemService { UserManager getUserManager(); + Executor getBackgroundExecutor(); + HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore(); HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId); @@ -758,6 +781,11 @@ public final class AppHibernationService extends SystemService { } @Override + public Executor getBackgroundExecutor() { + return mScheduledExecutorService; + } + + @Override public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() { File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME); return new HibernationStateDiskStore<>( diff --git a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java index c83659d2ff56..24cf43339847 100644 --- a/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java +++ b/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java @@ -109,6 +109,7 @@ class HibernationStateDiskStore<T> { * @return the parsed list of hibernation states, null if file does not exist */ @Nullable + @WorkerThread List<T> readHibernationStates() { synchronized (this) { if (!mHibernationFile.exists()) { diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 6776f49b7d8f..6aec9fcf9a24 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -106,6 +106,29 @@ class HostClipboardMonitor implements Runnable { return false; } + private void closePipe() { + try { + final RandomAccessFile pipe = mPipe; + mPipe = null; + if (pipe != null) { + pipe.close(); + } + } catch (IOException ignore) { + } + } + + private byte[] receiveMessage() throws IOException { + final int size = Integer.reverseBytes(mPipe.readInt()); + final byte[] receivedData = new byte[size]; + mPipe.readFully(receivedData); + return receivedData; + } + + private void sendMessage(byte[] message) throws IOException { + mPipe.writeInt(Integer.reverseBytes(message.length)); + mPipe.write(message); + } + public HostClipboardMonitor(HostClipboardCallback cb) { mHostClipboardCallback = cb; } @@ -120,17 +143,12 @@ class HostClipboardMonitor implements Runnable { while ((mPipe == null) && !openPipe()) { Thread.sleep(100); } - int size = mPipe.readInt(); - size = Integer.reverseBytes(size); - byte[] receivedData = new byte[size]; - mPipe.readFully(receivedData); + + final byte[] receivedData = receiveMessage(); mHostClipboardCallback.onHostClipboardUpdated( new String(receivedData)); } catch (IOException e) { - try { - mPipe.close(); - } catch (IOException ee) {} - mPipe = null; + closePipe(); } catch (InterruptedException e) {} } } @@ -138,8 +156,7 @@ class HostClipboardMonitor implements Runnable { public void setHostClipboard(String content) { try { if (mPipe != null) { - mPipe.writeInt(Integer.reverseBytes(content.getBytes().length)); - mPipe.write(content.getBytes()); + sendMessage(content.getBytes()); } } catch(IOException e) { Slog.e("HostClipboardMonitor", diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index ae9b0015de43..d29a0c715e95 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -23,7 +23,9 @@ import static android.app.compat.PackageOverride.VALUE_UNDEFINED; import android.annotation.Nullable; import android.app.compat.PackageOverride; import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; import android.compat.annotation.EnabledSince; +import android.compat.annotation.Overridable; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -60,6 +62,15 @@ public final class CompatChange extends CompatibilityChangeInfo { static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id. /** + * An overridable change ID to be used only in the CTS test for this SystemApi + */ + @ChangeId + @Disabled + @Overridable + static final long CTS_SYSTEM_API_OVERRIDABLE_CHANGEID = 174043039; // This is a bug id. + + + /** * Callback listener for when compat changes are updated for a package. * See {@link #registerListener(ChangeListener)} for more details. */ @@ -211,6 +222,7 @@ public final class CompatChange extends CompatibilityChangeInfo { boolean hasPackageOverride(String pname) { return mRawOverrides.containsKey(pname); } + /** * Remove any package override for the given package name, restoring the default behaviour. * @@ -355,7 +367,7 @@ public final class CompatChange extends CompatibilityChangeInfo { override.setPackageName(entry.getKey()); override.setMinVersionCode(entry.getValue().getMinVersionCode()); override.setMaxVersionCode(entry.getValue().getMaxVersionCode()); - override.setEnabled(entry.getValue().getEnabled()); + override.setEnabled(entry.getValue().isEnabled()); rawList.add(override); } changeOverrides.setRaw(rawOverrides); diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index ef86f42d6c3c..55e269630093 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -304,6 +304,16 @@ final class CompatConfig { } /** + * Returns whether the change is overridable. + */ + boolean isOverridable(long changeId) { + synchronized (mChanges) { + CompatChange c = mChanges.get(changeId); + return c != null && c.getOverridable(); + } + } + + /** * 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 @@ -343,7 +353,7 @@ final class CompatConfig { /** * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or - * {@link #addOverrides(CompatibilityChangeConfig, String)} for a certain package. + * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package. * * <p>This restores the default behaviour for the given app. * @@ -632,8 +642,11 @@ final class CompatConfig { } boolean shouldInvalidateCache = false; for (CompatChange c: changes) { + if (!c.hasPackageOverride(packageName)) { + continue; + } OverrideAllowedState allowedState = - mOverrideValidator.getOverrideAllowedState(c.getId(), packageName); + mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName); shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext); } if (shouldInvalidateCache) { diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java index aa66a1a8b01f..b5006913af35 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -16,6 +16,9 @@ package com.android.server.compat; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import static com.android.internal.compat.OverrideAllowedState.ALLOWED; import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; @@ -24,6 +27,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_S import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE; import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD; +import android.annotation.NonNull; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -69,8 +73,25 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { mForceNonDebuggableFinalBuild = false; } + /** + * Check the allowed state for the given changeId and packageName on a recheck. + * + * <p>Recheck happens when the given app is getting updated. In this case we cannot do a + * permission check on the caller, so we're using the fact that the override was present as + * proof that the original caller was allowed to set this override. + */ + OverrideAllowedState getOverrideAllowedStateForRecheck(long changeId, + @NonNull String packageName) { + return getOverrideAllowedStateInternal(changeId, packageName, true); + } + @Override public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) { + return getOverrideAllowedStateInternal(changeId, packageName, false); + } + + private OverrideAllowedState getOverrideAllowedStateInternal(long changeId, String packageName, + boolean isRecheck) { if (mCompatConfig.isLoggingOnly(changeId)) { return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1); } @@ -99,6 +120,16 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { } catch (NameNotFoundException e) { return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1); } + // If the change is annotated as @Overridable, apps with the specific permission can + // set the override even on production builds. When rechecking the override, e.g. during an + // app update we can bypass this check, as it wouldn't have been here in the first place. + if (mCompatConfig.isOverridable(changeId) + && (isRecheck + || mContext.checkCallingOrSelfPermission( + OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) + == PERMISSION_GRANTED)) { + return new OverrideAllowedState(ALLOWED, -1, -1); + } int appTargetSdk = applicationInfo.targetSdkVersion; // Only allow overriding debuggable apps. if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { @@ -130,5 +161,4 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { void forceNonDebuggableFinalForTest(boolean value) { mForceNonDebuggableFinalBuild = value; } - } diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 40e386359a40..20469a21db0c 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -18,6 +18,7 @@ package com.android.server.compat; import static android.Manifest.permission.LOG_COMPAT_CHANGE; import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD; import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; @@ -182,11 +183,12 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override - public void setOverridesFromInstaller(CompatibilityOverrideConfig overrides, + public void setOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides, String packageName) { - checkCompatChangeOverridePermission(); + // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods. + checkCompatChangeOverrideOverridablePermission(); + checkAllCompatOverridesAreOverridable(overrides); mCompatConfig.addOverrides(overrides, packageName); - killPackage(packageName); } @Override @@ -383,6 +385,26 @@ public class PlatformCompat extends IPlatformCompat.Stub { } } + private void checkCompatChangeOverrideOverridablePermission() { + // Don't check for permissions within the system process + if (Binder.getCallingUid() == SYSTEM_UID) { + return; + } + if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot override compat change"); + } + } + + private void checkAllCompatOverridesAreOverridable(CompatibilityOverrideConfig overrides) { + for (Long changeId : overrides.overrides.keySet()) { + if (!mCompatConfig.isOverridable(changeId)) { + throw new SecurityException("Only change ids marked as Overridable can be " + + "overridden."); + } + } + } + private void checkCompatChangeReadAndLogPermission() { checkCompatChangeReadPermission(); checkCompatChangeLogPermission(); diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java index 7b20ded19205..058dac882225 100644 --- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java +++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java @@ -26,6 +26,7 @@ import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL; import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; +import static android.net.SocketKeepalive.ERROR_NO_SUCH_SLOT; import static android.net.SocketKeepalive.ERROR_STOP_REASON_UNINITIALIZED; import static android.net.SocketKeepalive.ERROR_UNSUPPORTED; import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; @@ -518,6 +519,8 @@ public class KeepaliveTracker { } } else if (reason == ERROR_STOP_REASON_UNINITIALIZED) { throw new IllegalStateException("Unexpected stop reason: " + reason); + } else if (reason == ERROR_NO_SUCH_SLOT) { + throw new IllegalStateException("No such slot: " + reason); } else { notifyErrorCallback(ki.mCallback, reason); } diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index f3d201289f0e..6ea84ce35002 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -18,12 +18,14 @@ package com.android.server.connectivity; import static android.util.TimeUtils.NANOS_PER_MS; +import android.annotation.Nullable; import android.content.Context; import android.net.ConnectivityManager; import android.net.INetdEventCallback; import android.net.MacAddress; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.NetworkRequest; import android.net.metrics.ConnectStats; import android.net.metrics.DnsEvent; import android.net.metrics.INetdEventListener; @@ -98,6 +100,7 @@ public class NetdEventListenerService extends INetdEventListener.Stub { private final TokenBucket mConnectTb = new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT); + final TransportForNetIdNetworkCallback mCallback = new TransportForNetIdNetworkCallback(); /** * There are only 3 possible callbacks. @@ -158,6 +161,9 @@ public class NetdEventListenerService extends INetdEventListener.Stub { public NetdEventListenerService(ConnectivityManager cm) { // We are started when boot is complete, so ConnectivityService should already be running. mCm = cm; + // Clear all capabilities to listen all networks. + mCm.registerNetworkCallback(new NetworkRequest.Builder().clearCapabilities().build(), + mCallback); } private static long projectSnapshotTime(long timeMs) { @@ -389,18 +395,13 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } private long getTransports(int netId) { - // TODO: directly query ConnectivityService instead of going through Binder interface. - NetworkCapabilities nc = mCm.getNetworkCapabilities(new Network(netId)); + final NetworkCapabilities nc = mCallback.getNetworkCapabilities(netId); if (nc == null) { return 0; } return BitUtils.packBits(nc.getTransportTypes()); } - private static void maybeLog(String s, Object... args) { - if (DBG) Log.d(TAG, String.format(s, args)); - } - /** Helper class for buffering summaries of NetworkMetrics at regular time intervals */ static class NetworkMetricsSnapshot { @@ -428,4 +429,29 @@ public class NetdEventListenerService extends INetdEventListener.Stub { return String.format("%tT.%tL: %s", timeMs, timeMs, j.toString()); } } + + private class TransportForNetIdNetworkCallback extends ConnectivityManager.NetworkCallback { + private final SparseArray<NetworkCapabilities> mCapabilities = new SparseArray<>(); + + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { + synchronized (mCapabilities) { + mCapabilities.put(network.getNetId(), nc); + } + } + + @Override + public void onLost(Network network) { + synchronized (mCapabilities) { + mCapabilities.remove(network.getNetId()); + } + } + + @Nullable + public NetworkCapabilities getNetworkCapabilities(int netId) { + synchronized (mCapabilities) { + return mCapabilities.get(netId); + } + } + } } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 103ab957f312..ee32fbf00dfe 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -49,6 +49,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -200,6 +201,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Set to true when partial connectivity was detected. public boolean partialConnectivity; + // Delay between when the network is disconnected and when the native network is destroyed. + public int teardownDelayMs; + // Captive portal info of the network from RFC8908, if any. // Obtained by ConnectivityService and merged into NetworkAgent-provided information. public CaptivePortalData capportApiData; @@ -576,6 +580,28 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } } + /** + * Notify the NetworkAgent that the network is successfully connected. + */ + public void onNetworkCreated() { + try { + networkAgent.onNetworkCreated(); + } catch (RemoteException e) { + Log.e(TAG, "Error sending network created event", e); + } + } + + /** + * Notify the NetworkAgent that the native network has been destroyed. + */ + public void onNetworkDestroyed() { + try { + networkAgent.onNetworkDestroyed(); + } catch (RemoteException e) { + Log.e(TAG, "Error sending network destroyed event", e); + } + } + // TODO: consider moving out of NetworkAgentInfo into its own class private class NetworkAgentMessageHandler extends INetworkAgentRegistry.Stub { private final Handler mHandler; @@ -633,7 +659,13 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { @Override public void sendEpsQosSessionAvailable(final int qosCallbackId, final QosSession session, final EpsBearerQosSessionAttributes attributes) { - mQosCallbackTracker.sendEventQosSessionAvailable(qosCallbackId, session, attributes); + mQosCallbackTracker.sendEventEpsQosSessionAvailable(qosCallbackId, session, attributes); + } + + @Override + public void sendNrQosSessionAvailable(final int qosCallbackId, final QosSession session, + final NrQosSessionAttributes attributes) { + mQosCallbackTracker.sendEventNrQosSessionAvailable(qosCallbackId, session, attributes); } @Override @@ -646,6 +678,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { @QosCallbackException.ExceptionType final int exceptionType) { mQosCallbackTracker.sendEventQosCallbackError(qosCallbackId, exceptionType); } + + @Override + public void sendTeardownDelayMs(int teardownDelayMs) { + mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED, + teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget(); + } } /** diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 488677ac1b59..37116797d8d7 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -271,6 +271,13 @@ public class PermissionMonitor { return mApps.containsKey(uid); } + /** + * Returns whether the given uid has permission to use restricted networks. + */ + public synchronized boolean hasRestrictedNetworksPermission(int uid) { + return Boolean.TRUE.equals(mApps.get(uid)); + } + private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) { List<Integer> network = new ArrayList<>(); List<Integer> system = new ArrayList<>(); diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java index 0f5400d0f8e6..534dbe7699a7 100644 --- a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java +++ b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java @@ -27,6 +27,7 @@ import android.net.QosSession; import android.os.IBinder; import android.os.RemoteException; import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; import android.util.Log; import java.util.Objects; @@ -146,13 +147,23 @@ class QosCallbackAgentConnection implements IBinder.DeathRecipient { mNetworkAgentInfo.onQosCallbackUnregistered(mAgentCallbackId); } - void sendEventQosSessionAvailable(final QosSession session, + void sendEventEpsQosSessionAvailable(final QosSession session, final EpsBearerQosSessionAttributes attributes) { try { - if (DBG) log("sendEventQosSessionAvailable: sending..."); + if (DBG) log("sendEventEpsQosSessionAvailable: sending..."); mCallback.onQosEpsBearerSessionAvailable(session, attributes); } catch (final RemoteException e) { - loge("sendEventQosSessionAvailable: remote exception", e); + loge("sendEventEpsQosSessionAvailable: remote exception", e); + } + } + + void sendEventNrQosSessionAvailable(final QosSession session, + final NrQosSessionAttributes attributes) { + try { + if (DBG) log("sendEventNrQosSessionAvailable: sending..."); + mCallback.onNrQosSessionAvailable(session, attributes); + } catch (final RemoteException e) { + loge("sendEventNrQosSessionAvailable: remote exception", e); } } diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java index 8bda5323e4f8..b6ab47b276e3 100644 --- a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java +++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java @@ -27,6 +27,7 @@ import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; import android.util.Log; import com.android.net.module.util.CollectionUtils; @@ -179,17 +180,31 @@ public class QosCallbackTracker { } /** - * Called when the NetworkAgent sends the qos session available event + * Called when the NetworkAgent sends the qos session available event for EPS * * @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, + public void sendEventEpsQosSessionAvailable(final int qosCallbackId, final QosSession session, final EpsBearerQosSessionAttributes attributes) { - runOnAgentConnection(qosCallbackId, "sendEventQosSessionAvailable: ", - ac -> ac.sendEventQosSessionAvailable(session, attributes)); + runOnAgentConnection(qosCallbackId, "sendEventEpsQosSessionAvailable: ", + ac -> ac.sendEventEpsQosSessionAvailable(session, attributes)); + } + + /** + * Called when the NetworkAgent sends the qos session available event for NR + * + * @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 sendEventNrQosSessionAvailable(final int qosCallbackId, + final QosSession session, + final NrQosSessionAttributes attributes) { + runOnAgentConnection(qosCallbackId, "sendEventNrQosSessionAvailable: ", + ac -> ac.sendEventNrQosSessionAvailable(session, attributes)); } /** diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 30e0c7e9b9aa..e5ce4f062973 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -17,12 +17,11 @@ package com.android.server.connectivity; import static android.Manifest.permission.BIND_VPN_SERVICE; -import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.os.UserHandle.PER_USER_RANGE; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN; +import static android.os.UserHandle.PER_USER_RANGE; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; @@ -223,7 +222,7 @@ public class Vpn { protected NetworkAgent mNetworkAgent; private final Looper mLooper; @VisibleForTesting - protected final NetworkCapabilities mNetworkCapabilities; + protected NetworkCapabilities mNetworkCapabilities; private final SystemServices mSystemServices; private final Ikev2SessionCreator mIkev2SessionCreator; private final UserManager mUserManager; @@ -460,11 +459,12 @@ public class Vpn { mLegacyState = LegacyVpnInfo.STATE_DISCONNECTED; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE, "" /* subtypeName */); - mNetworkCapabilities = new NetworkCapabilities(); - mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); - mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); - mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); - mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE)); + mNetworkCapabilities = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_VPN) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) + .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE)) + .build(); loadAlwaysOnPackage(); } @@ -525,8 +525,10 @@ public class Vpn { } private void resetNetworkCapabilities() { - mNetworkCapabilities.setUids(null); - mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE)); + mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities) + .setUids(null) + .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE)) + .build(); } /** @@ -1124,17 +1126,19 @@ public class Vpn { } /** - * Return netId of current running VPN network. + * Return Network of current running VPN network. * - * @return a netId if there is a running VPN network or NETID_UNSET if there is no running VPN + * @return a Network if there is a running VPN network or null if there is no running VPN * network or network is null. */ - public synchronized int getNetId() { + @VisibleForTesting + @Nullable + public synchronized Network getNetwork() { final NetworkAgent agent = mNetworkAgent; - if (null == agent) return NETID_UNSET; + if (null == agent) return null; final Network network = agent.getNetwork(); - if (null == network) return NETID_UNSET; - return network.getNetId(); + if (null == network) return null; + return network; } private LinkProperties makeLinkProperties() { @@ -1237,29 +1241,33 @@ public class Vpn { // registered with registerDefaultNetworkCallback. This in turn protects the invariant // that an app calling ConnectivityManager#bindProcessToNetwork(getDefaultNetwork()) // behaves the same as when it uses the default network. - mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + final NetworkCapabilities.Builder capsBuilder = + new NetworkCapabilities.Builder(mNetworkCapabilities); + capsBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); mLegacyState = LegacyVpnInfo.STATE_CONNECTING; updateState(DetailedState.CONNECTING, "agentConnect"); - NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder().build(); - networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown; + final NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder() + .setBypassableVpn(mConfig.allowBypass && !mLockdown) + .build(); - mNetworkCapabilities.setOwnerUid(mOwnerUID); - mNetworkCapabilities.setAdministratorUids(new int[] {mOwnerUID}); - mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserId, + capsBuilder.setOwnerUid(mOwnerUID); + capsBuilder.setAdministratorUids(new int[] {mOwnerUID}); + capsBuilder.setUids(createUserAndRestrictedProfilesRanges(mUserId, mConfig.allowedApplications, mConfig.disallowedApplications)); - mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType())); + capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType())); // Only apps targeting Q and above can explicitly declare themselves as metered. // These VPNs are assumed metered unless they state otherwise. if (mIsPackageTargetingAtLeastQ && mConfig.isMetered) { - mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_METERED); + capsBuilder.removeCapability(NET_CAPABILITY_NOT_METERED); } else { - mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); + capsBuilder.addCapability(NET_CAPABILITY_NOT_METERED); } + mNetworkCapabilities = capsBuilder.build(); mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */, mNetworkCapabilities, lp, new NetworkScore.Builder().setLegacyInt(VPN_DEFAULT_SCORE).build(), @@ -1280,7 +1288,6 @@ public class Vpn { }); mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null) ? Arrays.asList(mConfig.underlyingNetworks) : null); - mNetworkInfo.setIsAvailable(true); updateState(DetailedState.CONNECTED, "agentConnect"); } @@ -1426,7 +1433,8 @@ public class Vpn { // restore old state mConfig = oldConfig; mConnection = oldConnection; - mNetworkCapabilities.setUids(oldUsers); + mNetworkCapabilities = + new NetworkCapabilities.Builder(mNetworkCapabilities).setUids(oldUsers).build(); mNetworkAgent = oldNetworkAgent; mInterface = oldInterface; throw e; @@ -1576,7 +1584,8 @@ public class Vpn { try { addUserToRanges(existingRanges, userId, mConfig.allowedApplications, mConfig.disallowedApplications); - mNetworkCapabilities.setUids(existingRanges); + mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities) + .setUids(existingRanges).build(); } catch (Exception e) { Log.wtf(TAG, "Failed to add restricted user to owner", e); } @@ -1605,7 +1614,8 @@ public class Vpn { final List<Range<Integer>> removedRanges = uidRangesForUser(userId, existingRanges); existingRanges.removeAll(removedRanges); - mNetworkCapabilities.setUids(existingRanges); + mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities) + .setUids(existingRanges).build(); } catch (Exception e) { Log.wtf(TAG, "Failed to remove restricted user to owner", e); } @@ -1849,22 +1859,13 @@ public class Vpn { /** * Updates underlying network set. */ - public synchronized boolean setUnderlyingNetworks(Network[] networks) { + public synchronized boolean setUnderlyingNetworks(@Nullable Network[] networks) { if (!isCallerEstablishedOwnerLocked()) { return false; } - if (networks == null) { - mConfig.underlyingNetworks = null; - } else { - mConfig.underlyingNetworks = new Network[networks.length]; - for (int i = 0; i < networks.length; ++i) { - if (networks[i] == null) { - mConfig.underlyingNetworks[i] = null; - } else { - mConfig.underlyingNetworks[i] = new Network(networks[i].getNetId()); - } - } - } + // Make defensive copy since the content of array might be altered by the caller. + mConfig.underlyingNetworks = + (networks != null) ? Arrays.copyOf(networks, networks.length) : null; mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null) ? Arrays.asList(mConfig.underlyingNetworks) : null); return true; @@ -1886,7 +1887,12 @@ public class Vpn { if (!isRunningLocked()) { return false; } - return mNetworkCapabilities.appliesToUid(uid); + final Set<Range<Integer>> uids = mNetworkCapabilities.getUids(); + if (uids == null) return true; + for (final Range<Integer> range : uids) { + if (range.contains(uid)) return true; + } + return false; } /** diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 6f12155c5ec6..6a407e99f1e4 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -440,9 +440,8 @@ public abstract class BrightnessMappingStrategy { } private float permissibleRatio(float currLux, float prevLux) { - return MathUtils.exp(MAX_GRAD - * (MathUtils.log(currLux + LUX_GRAD_SMOOTHING) - - MathUtils.log(prevLux + LUX_GRAD_SMOOTHING))); + return MathUtils.pow((currLux + LUX_GRAD_SMOOTHING) + / (prevLux + LUX_GRAD_SMOOTHING), MAX_GRAD); } protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index f658e33e0530..58a702527e5f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -619,8 +619,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } } - addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress, - activeSource.physicalAddress, deviceType)); + if (!mService.isPowerStandbyOrTransient()) { + addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress, + activeSource.physicalAddress, deviceType)); + } } private boolean handleNewDeviceAtTheTailOfActivePath(int path) { @@ -796,10 +798,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly void onNewAvrAdded(HdmiDeviceInfo avr) { assertRunOnServiceThread(); - addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); - if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId()) - && !hasAction(SetArcTransmissionStateAction.class)) { - startArcAction(true); + if (!mService.isPowerStandbyOrTransient()) { + addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress())); + if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId()) + && !hasAction(SetArcTransmissionStateAction.class)) { + startArcAction(true); + } } } @@ -1656,6 +1660,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // Remove recording actions. removeAction(OneTouchRecordAction.class); removeAction(TimerRecordingAction.class); + removeAction(NewDeviceAction.class); disableSystemAudioIfExist(); disableArcIfExist(); @@ -1696,12 +1701,20 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { setArcStatus(false); // Seq #44. - removeAction(RequestArcInitiationAction.class); + removeAllRunningArcAction(); if (!hasAction(RequestArcTerminationAction.class) && isArcEstablished()) { addAndStartAction(new RequestArcTerminationAction(this, avr.getLogicalAddress())); } } + @ServiceThreadOnly + private void removeAllRunningArcAction() { + // Running or pending actions make TV fail to broadcast <Standby> to connected devices + removeAction(RequestArcTerminationAction.class); + removeAction(RequestArcInitiationAction.class); + removeAction(SetArcTransmissionStateAction.class); + } + @Override @ServiceThreadOnly protected void onStandby(boolean initiatedByCec, int standbyAction) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java index ee3427f0a383..740407c42178 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java @@ -35,9 +35,23 @@ final class HdmiControlShellCommand extends ShellCommand { private final IHdmiControlService.Stub mBinderService; + final CountDownLatch mLatch; + AtomicInteger mCecResult; + IHdmiControlCallback.Stub mHdmiControlCallback; HdmiControlShellCommand(IHdmiControlService.Stub binderService) { mBinderService = binderService; + mLatch = new CountDownLatch(1); + mCecResult = new AtomicInteger(); + mHdmiControlCallback = + new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) { + getOutPrintWriter().println(" done (" + getResultString(result) + ")"); + mCecResult.set(result); + mLatch.countDown(); + } + }; } @Override @@ -70,6 +84,8 @@ final class HdmiControlShellCommand extends ShellCommand { pw.println(" --args <vendor specific arguments>"); pw.println(" [--id <true if vendor command should be sent with vendor id>]"); pw.println(" Send a Vendor Command to the given target device"); + pw.println(" setsystemaudiomode, setsam [on|off]"); + pw.println(" Sets the System Audio Mode feature on or off on TV devices"); } private int handleShellCommand(String cmd) throws RemoteException { @@ -81,6 +97,9 @@ final class HdmiControlShellCommand extends ShellCommand { return oneTouchPlay(pw); case "vendorcommand": return vendorCommand(pw); + case "setsystemaudiomode": + case "setsam": + return setSystemAudioMode(pw); } getErrPrintWriter().println("Unhandled command: " + cmd); @@ -88,28 +107,14 @@ final class HdmiControlShellCommand extends ShellCommand { } private int oneTouchPlay(PrintWriter pw) throws RemoteException { - final CountDownLatch latch = new CountDownLatch(1); - AtomicInteger cecResult = new AtomicInteger(); pw.print("Sending One Touch Play..."); - mBinderService.oneTouchPlay(new IHdmiControlCallback.Stub() { - @Override - public void onComplete(int result) { - pw.println(" done (" + result + ")"); - latch.countDown(); - cecResult.set(result); - } - }); + mBinderService.oneTouchPlay(mHdmiControlCallback); - try { - if (!latch.await(HdmiConfig.TIMEOUT_MS, TimeUnit.MILLISECONDS)) { - getErrPrintWriter().println("One Touch Play timed out."); - return 1; - } - } catch (InterruptedException e) { - getErrPrintWriter().println("Caught InterruptedException"); - Thread.currentThread().interrupt(); + if (!receiveCallback("One Touch Play")) { + return 1; } - return cecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1; + + return mCecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1; } private int vendorCommand(PrintWriter pw) throws RemoteException { @@ -157,4 +162,62 @@ final class HdmiControlShellCommand extends ShellCommand { mBinderService.sendVendorCommand(deviceType, destination, params, hasVendorId); return 0; } + + private int setSystemAudioMode(PrintWriter pw) throws RemoteException { + if (1 > getRemainingArgsCount()) { + throw new IllegalArgumentException( + "Please indicate if System Audio Mode should be turned \"on\" or \"off\"."); + } + + String arg = getNextArg(); + if (arg.equals("on")) { + pw.println("Setting System Audio Mode on"); + mBinderService.setSystemAudioMode(true, mHdmiControlCallback); + } else if (arg.equals("off")) { + pw.println("Setting System Audio Mode off"); + mBinderService.setSystemAudioMode(false, mHdmiControlCallback); + } else { + throw new IllegalArgumentException( + "Please indicate if System Audio Mode should be turned \"on\" or \"off\"."); + } + + if (!receiveCallback("Set System Audio Mode")) { + return 1; + } + + return mCecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1; + } + + private boolean receiveCallback(String command) { + try { + if (!mLatch.await(HdmiConfig.TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + getErrPrintWriter().println(command + " timed out."); + return false; + } + } catch (InterruptedException e) { + getErrPrintWriter().println("Caught InterruptedException"); + Thread.currentThread().interrupt(); + } + return true; + } + + private String getResultString(int result) { + switch (result) { + case HdmiControlManager.RESULT_SUCCESS: + return "Success"; + case HdmiControlManager.RESULT_TIMEOUT: + return "Timeout"; + case HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE: + return "Source not available"; + case HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE: + return "Target not available"; + case HdmiControlManager.RESULT_EXCEPTION: + return "Exception"; + case HdmiControlManager.RESULT_INCORRECT_MODE: + return "Incorrect mode"; + case HdmiControlManager.RESULT_COMMUNICATION_FAILED: + return "Communication Failed"; + } + return Integer.toString(result); + } } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 6e99cba6ea91..76ecc1acc7ac 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -15,14 +15,17 @@ */ package com.android.server.locksettings; + import static android.os.UserHandle.USER_SYSTEM; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.UserInfo; import android.os.Handler; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserManager; import android.provider.DeviceConfig; import android.provider.Settings; @@ -35,6 +38,8 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.widget.RebootEscrowListener; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -65,6 +70,22 @@ class RebootEscrowManager { public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count"; static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp"; + static final String REBOOT_ESCROW_KEY_PROVIDER = "reboot_escrow_key_provider"; + + /** + * The verified boot 2.0 vbmeta digest of the current slot, the property value is always + * available after boot. + */ + static final String VBMETA_DIGEST_PROP_NAME = "ro.boot.vbmeta.digest"; + /** + * The system prop contains vbmeta digest of the inactive slot. The build property is set after + * an OTA update. RebootEscrowManager will store it in disk before the OTA reboot, so the value + * is available for vbmeta digest verification after the device reboots. + */ + static final String OTHER_VBMETA_DIGEST_PROP_NAME = "ota.other.vbmeta_digest"; + static final String REBOOT_ESCROW_KEY_VBMETA_DIGEST = "reboot_escrow_key_vbmeta_digest"; + static final String REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST = + "reboot_escrow_key_other_vbmeta_digest"; /** * Number of boots until we consider the escrow data to be stale for the purposes of metrics. @@ -86,6 +107,31 @@ class RebootEscrowManager { private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3; private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30; + @IntDef(prefix = {"ERROR_"}, value = { + ERROR_NONE, + ERROR_UNKNOWN, + ERROR_NO_PROVIDER, + ERROR_LOAD_ESCROW_KEY, + ERROR_RETRY_COUNT_EXHAUSTED, + ERROR_UNLOCK_ALL_USERS, + ERROR_PROVIDER_MISMATCH, + ERROR_KEYSTORE_FAILURE, + }) + @Retention(RetentionPolicy.SOURCE) + @interface RebootEscrowErrorCode { + } + + static final int ERROR_NONE = 0; + static final int ERROR_UNKNOWN = 1; + static final int ERROR_NO_PROVIDER = 2; + static final int ERROR_LOAD_ESCROW_KEY = 3; + static final int ERROR_RETRY_COUNT_EXHAUSTED = 4; + static final int ERROR_UNLOCK_ALL_USERS = 5; + static final int ERROR_PROVIDER_MISMATCH = 6; + static final int ERROR_KEYSTORE_FAILURE = 7; + + private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; + /** * Logs events for later debugging in bugreports. */ @@ -199,6 +245,10 @@ class RebootEscrowManager { 0); } + public long getCurrentTimeMillis() { + return System.currentTimeMillis(); + } + public int getLoadEscrowDataRetryLimit() { return DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA, "load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT); @@ -221,6 +271,11 @@ class RebootEscrowManager { public RebootEscrowEventLog getEventLog() { return new RebootEscrowEventLog(); } + + public String getVbmetaDigest(boolean other) { + return other ? SystemProperties.get(OTHER_VBMETA_DIGEST_PROP_NAME) + : SystemProperties.get(VBMETA_DIGEST_PROP_NAME); + } } RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) { @@ -261,6 +316,7 @@ class RebootEscrowManager { if (rebootEscrowUsers.isEmpty()) { Slog.i(TAG, "No reboot escrow data found for users," + " skipping loading escrow data"); + clearMetricsStorage(); return; } @@ -284,6 +340,7 @@ class RebootEscrowManager { } Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts"); + mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED; onGetRebootEscrowKeyFailed(users, attemptNumber); } @@ -307,6 +364,17 @@ class RebootEscrowManager { } if (escrowKey == null) { + if (mLoadEscrowDataErrorCode == ERROR_NONE) { + // Specifically check if the RoR provider has changed after reboot. + int providerType = mInjector.serverBasedResumeOnReboot() + ? RebootEscrowProviderInterface.TYPE_SERVER_BASED + : RebootEscrowProviderInterface.TYPE_HAL; + if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) { + mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH; + } else { + mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY; + } + } onGetRebootEscrowKeyFailed(users, attemptNumber + 1); return; } @@ -321,9 +389,49 @@ class RebootEscrowManager { // Clear the old key in keystore. A new key will be generated by new RoR requests. mKeyStoreManager.clearKeyStoreEncryptionKey(); + if (!allUsersUnlocked && mLoadEscrowDataErrorCode == ERROR_NONE) { + mLoadEscrowDataErrorCode = ERROR_UNLOCK_ALL_USERS; + } onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1); } + private void clearMetricsStorage() { + mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_VBMETA_DIGEST, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_PROVIDER, USER_SYSTEM); + } + + private int getVbmetaDigestStatusOnRestoreComplete() { + String currentVbmetaDigest = mInjector.getVbmetaDigest(false); + String vbmetaDigestStored = mStorage.getString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, + "", USER_SYSTEM); + String vbmetaDigestOtherStored = mStorage.getString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, + "", USER_SYSTEM); + + // The other vbmeta digest is never set, assume no slot switch is attempted. + if (vbmetaDigestOtherStored.isEmpty()) { + if (currentVbmetaDigest.equals(vbmetaDigestStored)) { + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT; + } + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH; + } + + // The other vbmeta digest is set, we expect to boot into the new slot. + if (currentVbmetaDigest.equals(vbmetaDigestOtherStored)) { + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT; + } else if (currentVbmetaDigest.equals(vbmetaDigestStored)) { + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_FALLBACK_SLOT; + } + return FrameworkStatsLog + .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH; + } + private void reportMetricOnRestoreComplete(boolean success, int attemptCount) { int serviceType = mInjector.serverBasedResumeOnReboot() ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED @@ -331,26 +439,32 @@ class RebootEscrowManager { long armedTimestamp = mStorage.getLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, -1, USER_SYSTEM); - mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM); - int escrowDurationInSeconds = armedTimestamp != -1 - ? (int) (System.currentTimeMillis() - armedTimestamp) / 1000 : -1; + int escrowDurationInSeconds = -1; + long currentTimeStamp = mInjector.getCurrentTimeMillis(); + if (armedTimestamp != -1 && currentTimeStamp > armedTimestamp) { + escrowDurationInSeconds = (int) (currentTimeStamp - armedTimestamp) / 1000; + } - // TODO(b/179105110) design error code; and report the true value for other fields. - int vbmetaDigestStatus = FrameworkStatsLog - .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MATCH_EXPECTED_SLOT; + int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete(); + if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) { + mLoadEscrowDataErrorCode = ERROR_UNKNOWN; + } - mInjector.reportMetric(success, 0 /* error code */, serviceType, attemptCount, + // TODO(179105110) report the duration since boot complete. + mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount, escrowDurationInSeconds, vbmetaDigestStatus, -1); + + mLoadEscrowDataErrorCode = ERROR_NONE; } private void onEscrowRestoreComplete(boolean success, int attemptCount) { int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM); - mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); int bootCountDelta = mInjector.getBootCount() - previousBootCount; if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) { reportMetricOnRestoreComplete(success, attemptCount); } + clearMetricsStorage(); } private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException { @@ -358,6 +472,14 @@ class RebootEscrowManager { if (rebootEscrowProvider == null) { Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrowProvider is unavailable"); + mLoadEscrowDataErrorCode = ERROR_NO_PROVIDER; + return null; + } + + // Server based RoR always need the decryption key from keystore. + if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED + && kk == null) { + mLoadEscrowDataErrorCode = ERROR_KEYSTORE_FAILURE; return null; } @@ -463,7 +585,7 @@ class RebootEscrowManager { return; } - mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM); + clearMetricsStorage(); rebootEscrowProvider.clearRebootEscrowKey(); List<UserInfo> users = mUserManager.getUsers(); @@ -486,6 +608,9 @@ class RebootEscrowManager { return false; } + int actualProviderType = rebootEscrowProvider.getType(); + // TODO(b/183140900) Fail the reboot if provider type mismatches. + RebootEscrowKey escrowKey; synchronized (mKeyGenerationLock) { escrowKey = mPendingRebootEscrowKey; @@ -505,8 +630,14 @@ class RebootEscrowManager { boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk); if (armedRebootEscrow) { mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM); - mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, System.currentTimeMillis(), + mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, mInjector.getCurrentTimeMillis(), + USER_SYSTEM); + // Store the vbmeta digest of both slots. + mStorage.setString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, mInjector.getVbmetaDigest(false), USER_SYSTEM); + mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, + mInjector.getVbmetaDigest(true), USER_SYSTEM); + mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM); mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS); } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java index 4b00772088f2..e8f6f4abd030 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java @@ -60,6 +60,11 @@ class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface { } @Override + public int getType() { + return TYPE_HAL; + } + + @Override public boolean hasRebootEscrowSupport() { return mInjector.getRebootEscrow() != null; } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java index af6faad3c76e..e106d817c533 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java @@ -16,7 +16,11 @@ package com.android.server.locksettings; +import android.annotation.IntDef; + import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import javax.crypto.SecretKey; @@ -28,6 +32,21 @@ import javax.crypto.SecretKey; * @hide */ public interface RebootEscrowProviderInterface { + @IntDef(prefix = {"TYPE_"}, value = { + TYPE_HAL, + TYPE_SERVER_BASED, + }) + @Retention(RetentionPolicy.SOURCE) + @interface RebootEscrowProviderType { + } + int TYPE_HAL = 0; + int TYPE_SERVER_BASED = 1; + + /** + * Returns the reboot escrow provider type. + */ + @RebootEscrowProviderType int getType(); + /** * Returns true if the secure store/discard of reboot escrow key is supported. */ diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java index 697bf08a232e..28669875f1cd 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java @@ -95,6 +95,11 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa } @Override + public int getType() { + return TYPE_SERVER_BASED; + } + + @Override public boolean hasRebootEscrowSupport() { return mInjector.getServiceConnection() != null; } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 39ed7e8b1e1a..2e4d41c7d364 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -96,9 +96,10 @@ public abstract class NetworkPolicyManagerInternal { /** * Notifies that the specified {@link NetworkStatsProvider} has reached its quota - * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)}. + * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or + * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}. * * @param tag the human readable identifier of the custom network stats provider. */ - public abstract void onStatsProviderLimitReached(@NonNull String tag); + public abstract void onStatsProviderWarningOrLimitReached(@NonNull String tag); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 46c80e7c44e3..c12785805a2e 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -38,6 +38,15 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_ADMIN_DISABLED; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; +import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; +import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; +import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; +import static android.net.ConnectivityManager.BLOCKED_REASON_RESTRICTED_MODE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; @@ -66,15 +75,6 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLI import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; -import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_ADMIN_DISABLED; -import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_DATA_SAVER; -import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_MASK; -import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_USER_RESTRICTED; -import static android.net.NetworkPolicyManager.BLOCKED_REASON_APP_STANDBY; -import static android.net.NetworkPolicyManager.BLOCKED_REASON_BATTERY_SAVER; -import static android.net.NetworkPolicyManager.BLOCKED_REASON_DOZE; -import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE; -import static android.net.NetworkPolicyManager.BLOCKED_REASON_RESTRICTED_MODE; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; @@ -174,12 +174,10 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; -import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; -import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkIdentity; @@ -425,15 +423,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_LIMIT_REACHED = 5; private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6; private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7; - private static final int MSG_UPDATE_INTERFACE_QUOTA = 10; - private static final int MSG_REMOVE_INTERFACE_QUOTA = 11; + private static final int MSG_UPDATE_INTERFACE_QUOTAS = 10; + private static final int MSG_REMOVE_INTERFACE_QUOTAS = 11; private static final int MSG_POLICIES_CHANGED = 13; private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15; private static final int MSG_SUBSCRIPTION_OVERRIDE = 16; private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17; private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18; private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19; - private static final int MSG_STATS_PROVIDER_LIMIT_REACHED = 20; + private static final int MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED = 20; // TODO: Add similar docs for other messages. /** * Message to indicate that reasons for why an uid is blocked changed. @@ -1220,10 +1218,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static boolean updateCapabilityChange(SparseBooleanArray lastValues, boolean newValue, Network network) { - final boolean lastValue = lastValues.get(network.netId, false); - final boolean changed = (lastValue != newValue) || lastValues.indexOfKey(network.netId) < 0; + final boolean lastValue = lastValues.get(network.getNetId(), false); + final boolean changed = (lastValue != newValue) + || lastValues.indexOfKey(network.getNetId()) < 0; if (changed) { - lastValues.put(network.netId, newValue); + lastValues.put(network.getNetId(), newValue); } return changed; } @@ -1246,7 +1245,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNetworkRoaming, newRoaming, network); if (meteredChanged || roamingChanged) { - mLogger.meterednessChanged(network.netId, newMetered); + mLogger.meterednessChanged(network.getNetId(), newMetered); updateNetworkRulesNL(); } } @@ -1922,16 +1921,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Collect all ifaces from a {@link NetworkStateSnapshot} into the given set. */ private static void collectIfaces(ArraySet<String> ifaces, NetworkStateSnapshot snapshot) { - final String baseIface = snapshot.linkProperties.getInterfaceName(); - if (baseIface != null) { - ifaces.add(baseIface); - } - for (LinkProperties stackedLink : snapshot.linkProperties.getStackedLinks()) { - final String stackedIface = stackedLink.getInterfaceName(); - if (stackedIface != null) { - ifaces.add(stackedIface); - } - } + ifaces.addAll(snapshot.linkProperties.getAllInterfaceNames()); } /** @@ -2012,7 +2002,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNetIdToSubId.clear(); final ArrayMap<NetworkStateSnapshot, NetworkIdentity> identified = new ArrayMap<>(); for (final NetworkStateSnapshot snapshot : snapshots) { - mNetIdToSubId.put(snapshot.network.netId, parseSubId(snapshot)); + mNetIdToSubId.put(snapshot.network.getNetId(), parseSubId(snapshot)); // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype // in the object created here is never used and its value doesn't matter, so use @@ -2045,39 +2035,45 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED; final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED; - if (hasLimit || policy.metered) { - final long quotaBytes; - if (hasLimit && policy.hasCycle()) { - final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager - .cycleIterator(policy).next(); - final long start = cycle.first.toInstant().toEpochMilli(); - final long end = cycle.second.toInstant().toEpochMilli(); - final long totalBytes = getTotalBytes(policy.template, start, end); - - if (policy.lastLimitSnooze >= start) { - // snoozing past quota, but we still need to restrict apps, - // so push really high quota. - quotaBytes = Long.MAX_VALUE; - } else { - // remaining "quota" bytes are based on total usage in - // current cycle. kernel doesn't like 0-byte rules, so we - // set 1-byte quota and disable the radio later. - quotaBytes = Math.max(1, policy.limitBytes - totalBytes); - } - } else { - // metered network, but no policy limit; we still need to - // restrict apps, so push really high quota. - quotaBytes = Long.MAX_VALUE; + long limitBytes = Long.MAX_VALUE; + long warningBytes = Long.MAX_VALUE; + if ((hasLimit || hasWarning) && policy.hasCycle()) { + final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager + .cycleIterator(policy).next(); + final long start = cycle.first.toInstant().toEpochMilli(); + final long end = cycle.second.toInstant().toEpochMilli(); + final long totalBytes = getTotalBytes(policy.template, start, end); + + // If the limit notification is not snoozed, the limit quota needs to be calculated. + if (hasLimit && policy.lastLimitSnooze < start) { + // remaining "quota" bytes are based on total usage in + // current cycle. kernel doesn't like 0-byte rules, so we + // set 1-byte quota and disable the radio later. + limitBytes = Math.max(1, policy.limitBytes - totalBytes); + } + + // If the warning notification was snoozed by user, or the service already knows + // it is over warning bytes, doesn't need to calculate warning bytes. + if (hasWarning && policy.lastWarningSnooze < start + && !policy.isOverWarning(totalBytes)) { + warningBytes = Math.max(1, policy.warningBytes - totalBytes); } + } + if (hasWarning || hasLimit || policy.metered) { if (matchingIfaces.size() > 1) { // TODO: switch to shared quota once NMS supports Slog.w(TAG, "shared quota unsupported; generating rule for each iface"); } + // Set the interface warning and limit. For interfaces which has no cycle, + // or metered with no policy quotas, or snoozed notification; we still need to put + // iptables rule hooks to restrict apps for data saver, so push really high quota. + // TODO: Push NetworkStatsProvider.QUOTA_UNLIMITED instead of Long.MAX_VALUE to + // providers. for (int j = matchingIfaces.size() - 1; j >= 0; j--) { final String iface = matchingIfaces.valueAt(j); - setInterfaceQuotaAsync(iface, quotaBytes); + setInterfaceQuotasAsync(iface, warningBytes, limitBytes); newMeteredIfaces.add(iface); } } @@ -2100,7 +2096,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (int j = matchingIfaces.size() - 1; j >= 0; j--) { final String iface = matchingIfaces.valueAt(j); if (!newMeteredIfaces.contains(iface)) { - setInterfaceQuotaAsync(iface, Long.MAX_VALUE); + setInterfaceQuotasAsync(iface, Long.MAX_VALUE, Long.MAX_VALUE); newMeteredIfaces.add(iface); } } @@ -2112,7 +2108,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) { final String iface = mMeteredIfaces.valueAt(i); if (!newMeteredIfaces.contains(iface)) { - removeInterfaceQuotaAsync(iface); + removeInterfaceQuotasAsync(iface); } } mMeteredIfaces = newMeteredIfaces; @@ -4982,7 +4978,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mListeners.finishBroadcast(); return true; } - case MSG_STATS_PROVIDER_LIMIT_REACHED: { + case MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED: { mNetworkStats.forceUpdate(); synchronized (mNetworkPoliciesSecondLock) { @@ -5053,19 +5049,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNetworkStats.advisePersistThreshold(persistThreshold); return true; } - case MSG_UPDATE_INTERFACE_QUOTA: { - final String iface = (String) msg.obj; - // int params need to be stitched back into a long - final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL); - removeInterfaceQuota(iface); - setInterfaceQuota(iface, quota); - mNetworkStats.setStatsProviderLimitAsync(iface, quota); + case MSG_UPDATE_INTERFACE_QUOTAS: { + final IfaceQuotas val = (IfaceQuotas) msg.obj; + // TODO: Consider set a new limit before removing the original one. + removeInterfaceLimit(val.iface); + setInterfaceLimit(val.iface, val.limit); + mNetworkStats.setStatsProviderWarningAndLimitAsync(val.iface, val.warning, + val.limit); return true; } - case MSG_REMOVE_INTERFACE_QUOTA: { + case MSG_REMOVE_INTERFACE_QUOTAS: { final String iface = (String) msg.obj; - removeInterfaceQuota(iface); - mNetworkStats.setStatsProviderLimitAsync(iface, QUOTA_UNLIMITED); + removeInterfaceLimit(iface); + mNetworkStats.setStatsProviderWarningAndLimitAsync(iface, QUOTA_UNLIMITED, + QUOTA_UNLIMITED); return true; } case MSG_RESET_FIREWALL_RULES_BY_UID: { @@ -5213,15 +5210,32 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private void setInterfaceQuotaAsync(String iface, long quotaBytes) { - // long quotaBytes split up into two ints to fit in message - mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA, (int) (quotaBytes >> 32), - (int) (quotaBytes & 0xFFFFFFFF), iface).sendToTarget(); + private static final class IfaceQuotas { + @NonNull public final String iface; + // Warning and limit bytes of interface qutoas, could be QUOTA_UNLIMITED or Long.MAX_VALUE + // if not set. 0 is not acceptable since kernel doesn't like 0-byte rules. + public final long warning; + public final long limit; + + private IfaceQuotas(@NonNull String iface, long warning, long limit) { + this.iface = iface; + this.warning = warning; + this.limit = limit; + } + } + + private void setInterfaceQuotasAsync(@NonNull String iface, + long warningBytes, long limitBytes) { + mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTAS, + new IfaceQuotas(iface, warningBytes, limitBytes)).sendToTarget(); } - private void setInterfaceQuota(String iface, long quotaBytes) { + private void setInterfaceLimit(String iface, long limitBytes) { try { - mNetworkManager.setInterfaceQuota(iface, quotaBytes); + // For legacy design the data warning is covered by global alert, where the + // kernel will notify upper layer for a small amount of change of traffic + // statistics. Thus, passing warning is not needed. + mNetworkManager.setInterfaceQuota(iface, limitBytes); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting interface quota", e); } catch (RemoteException e) { @@ -5229,11 +5243,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private void removeInterfaceQuotaAsync(String iface) { - mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface).sendToTarget(); + private void removeInterfaceQuotasAsync(String iface) { + mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTAS, iface).sendToTarget(); } - private void removeInterfaceQuota(String iface) { + private void removeInterfaceLimit(String iface) { try { mNetworkManager.removeInterfaceQuota(iface); } catch (IllegalStateException e) { @@ -5738,9 +5752,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void onStatsProviderLimitReached(@NonNull String tag) { - Log.v(TAG, "onStatsProviderLimitReached: " + tag); - mHandler.obtainMessage(MSG_STATS_PROVIDER_LIMIT_REACHED).sendToTarget(); + public void onStatsProviderWarningOrLimitReached(@NonNull String tag) { + Log.v(TAG, "onStatsProviderWarningOrLimitReached: " + tag); + mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget(); } } @@ -5782,7 +5796,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mNetworkPoliciesSecondLock") private int getSubIdLocked(Network network) { - return mNetIdToSubId.get(network.netId, INVALID_SUBSCRIPTION_ID); + return mNetIdToSubId.get(network.getNetId(), INVALID_SUBSCRIPTION_ID); } @GuardedBy("mNetworkPoliciesSecondLock") diff --git a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java index 0cb0bc2c0896..0e9a9da6804b 100644 --- a/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkStatsManagerInternal.java @@ -37,8 +37,9 @@ public abstract class NetworkStatsManagerInternal { public abstract void forceUpdate(); /** - * Set the quota limit to all registered custom network stats providers. + * Set the warning and limit to all registered custom network stats providers. * Note that invocation of any interface will be sent to all providers. */ - public abstract void setStatsProviderLimitAsync(@NonNull String iface, long quota); + public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning, + long limit); } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 7b376847fd44..19f5e3cd5dfa 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -24,6 +24,7 @@ import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkIdentity.SUBTYPE_COMBINED; import static android.net.NetworkStack.checkNetworkStackPermission; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; @@ -65,6 +66,7 @@ import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE; import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES; import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -92,7 +94,6 @@ import android.net.DataUsageRequest; import android.net.INetworkManagementEventObserver; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; -import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkIdentity; @@ -101,7 +102,9 @@ import android.net.NetworkStateSnapshot; import android.net.NetworkStats; import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; +import android.net.NetworkSpecifier; import android.net.NetworkTemplate; +import android.net.TelephonyNetworkSpecifier; import android.net.TrafficStats; import android.net.UnderlyingNetworkInfo; import android.net.Uri; @@ -131,6 +134,7 @@ import android.service.NetworkInterfaceProto; import android.service.NetworkStatsServiceDumpProto; import android.telephony.PhoneStateListener; import android.telephony.SubscriptionPlan; +import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -1320,8 +1324,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(), ident.getRoaming(), true /* metered */, true /* onDefaultNetwork */, ident.getOemManaged()); - findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent); - findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent); + final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot); + findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent); + findOrCreateNetworkIdentitySet(mActiveUidIfaces, ifaceVt).add(vtIdent); } if (isMobile) { @@ -1358,17 +1363,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // (or non eBPF offloaded) TX they would appear on both, however egress interface // accounting is explicitly bypassed for traffic from the clat uid. // - final List<LinkProperties> stackedLinks = snapshot.linkProperties.getStackedLinks(); - for (LinkProperties stackedLink : stackedLinks) { - final String stackedIface = stackedLink.getInterfaceName(); - if (stackedIface != null) { - findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident); - findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident); + // TODO: This code might be combined to above code. + for (String iface : snapshot.linkProperties.getAllInterfaceNames()) { + // baseIface has been handled, so ignore it. + if (TextUtils.equals(baseIface, iface)) continue; + if (iface != null) { + findOrCreateNetworkIdentitySet(mActiveIfaces, iface).add(ident); + findOrCreateNetworkIdentitySet(mActiveUidIfaces, iface).add(ident); if (isMobile) { - mobileIfaces.add(stackedIface); + mobileIfaces.add(iface); } - mStatsFactory.noteStackedIface(stackedIface, baseIface); + mStatsFactory.noteStackedIface(iface, baseIface); } } } @@ -1376,6 +1382,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]); } + private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) { + if (!state.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + throw new IllegalArgumentException("Mobile state need capability TRANSPORT_CELLULAR"); + } + + final NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier(); + if (spec instanceof TelephonyNetworkSpecifier) { + return ((TelephonyNetworkSpecifier) spec).getSubscriptionId(); + } else { + Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier"); + return INVALID_SUBSCRIPTION_ID; + } + } + /** * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different @@ -1673,9 +1693,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public void setStatsProviderLimitAsync(@NonNull String iface, long quota) { - if (LOGV) Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")"); - invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetLimit(iface, quota)); + public void setStatsProviderWarningAndLimitAsync( + @NonNull String iface, long warning, long limit) { + if (LOGV) { + Slog.v(TAG, "setStatsProviderWarningAndLimitAsync(" + + iface + "," + warning + "," + limit + ")"); + } + invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface, + warning, limit)); } } @@ -2070,10 +2095,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @Override - public void notifyLimitReached() { - Log.d(TAG, mTag + ": onLimitReached"); + public void notifyWarningOrLimitReached() { + Log.d(TAG, mTag + ": notifyWarningOrLimitReached"); LocalServices.getService(NetworkPolicyManagerInternal.class) - .onStatsProviderLimitReached(mTag); + .onStatsProviderWarningOrLimitReached(mTag); } @Override diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 7d1827d1b6e2..0c1ba962323f 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -32,6 +32,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationChannel; @@ -177,6 +178,8 @@ public class PreferencesHelper implements RankingConfig { private Map<String, List<String>> mOemLockedApps = new HashMap(); + private int mCurrentUserId = UserHandle.USER_NULL; + public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, ZenModeHelper zenHelper, NotificationChannelLogger notificationChannelLogger, AppOpsManager appOpsManager, @@ -191,7 +194,8 @@ public class PreferencesHelper implements RankingConfig { updateBadgingEnabled(); updateBubblesEnabled(); - syncChannelsBypassingDnd(mContext.getUserId()); + mCurrentUserId = ActivityManager.getCurrentUser(); + syncChannelsBypassingDnd(); } public void readXml(XmlPullParser parser, boolean forRestore, int userId) @@ -790,7 +794,7 @@ public class PreferencesHelper implements RankingConfig { // but the system can if (group.isBlocked() != oldGroup.isBlocked()) { group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE); - updateChannelsBypassingDnd(mContext.getUserId()); + updateChannelsBypassingDnd(); } } } @@ -871,13 +875,13 @@ public class PreferencesHelper implements RankingConfig { // fields on the channel yet if (existing.getUserLockedFields() == 0 && hasDndAccess) { boolean bypassDnd = channel.canBypassDnd(); - if (bypassDnd != existing.canBypassDnd()) { + if (bypassDnd != existing.canBypassDnd() || wasUndeleted) { existing.setBypassDnd(bypassDnd); needsPolicyFileChange = true; if (bypassDnd != mAreChannelsBypassingDnd || previousExistingImportance != existing.getImportance()) { - updateChannelsBypassingDnd(mContext.getUserId()); + updateChannelsBypassingDnd(); } } } @@ -941,7 +945,7 @@ public class PreferencesHelper implements RankingConfig { r.channels.put(channel.getId(), channel); if (channel.canBypassDnd() != mAreChannelsBypassingDnd) { - updateChannelsBypassingDnd(mContext.getUserId()); + updateChannelsBypassingDnd(); } MetricsLogger.action(getChannelLog(channel, pkg).setType( com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN)); @@ -1013,7 +1017,7 @@ public class PreferencesHelper implements RankingConfig { if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd || channel.getImportance() != updatedChannel.getImportance()) { - updateChannelsBypassingDnd(mContext.getUserId()); + updateChannelsBypassingDnd(); } } updateConfig(); @@ -1110,7 +1114,7 @@ public class PreferencesHelper implements RankingConfig { mNotificationChannelLogger.logNotificationChannelDeleted(channel, uid, pkg); if (mAreChannelsBypassingDnd && channel.canBypassDnd()) { - updateChannelsBypassingDnd(mContext.getUserId()); + updateChannelsBypassingDnd(); } } } @@ -1454,7 +1458,7 @@ public class PreferencesHelper implements RankingConfig { } } if (!deletedChannelIds.isEmpty() && mAreChannelsBypassingDnd) { - updateChannelsBypassingDnd(mContext.getUserId()); + updateChannelsBypassingDnd(); } return deletedChannelIds; } @@ -1600,29 +1604,29 @@ public class PreferencesHelper implements RankingConfig { } /** - * Syncs {@link #mAreChannelsBypassingDnd} with the user's notification policy before + * Syncs {@link #mAreChannelsBypassingDnd} with the current user's notification policy before * updating - * @param userId */ - private void syncChannelsBypassingDnd(int userId) { + private void syncChannelsBypassingDnd() { mAreChannelsBypassingDnd = (mZenModeHelper.getNotificationPolicy().state & NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND) == 1; - updateChannelsBypassingDnd(userId); + updateChannelsBypassingDnd(); } /** - * Updates the user's NotificationPolicy based on whether the given userId + * Updates the user's NotificationPolicy based on whether the current userId * has channels bypassing DND * @param userId */ - private void updateChannelsBypassingDnd(int userId) { + private void updateChannelsBypassingDnd() { synchronized (mPackagePreferences) { final int numPackagePreferences = mPackagePreferences.size(); for (int i = 0; i < numPackagePreferences; i++) { final PackagePreferences r = mPackagePreferences.valueAt(i); - // Package isn't associated with this userId or notifications from this package are - // blocked - if (userId != UserHandle.getUserId(r.uid) || r.importance == IMPORTANCE_NONE) { + // Package isn't associated with the current userId or notifications from this + // package are blocked + if (mCurrentUserId != UserHandle.getUserId(r.uid) + || r.importance == IMPORTANCE_NONE) { continue; } @@ -2168,14 +2172,16 @@ public class PreferencesHelper implements RankingConfig { * Called when user switches */ public void onUserSwitched(int userId) { - syncChannelsBypassingDnd(userId); + mCurrentUserId = userId; + syncChannelsBypassingDnd(); } /** * Called when user is unlocked */ public void onUserUnlocked(int userId) { - syncChannelsBypassingDnd(userId); + mCurrentUserId = userId; + syncChannelsBypassingDnd(); } public void onUserRemoved(int userId) { diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java index cc6a8243799d..ed1f5f567d95 100644 --- a/services/core/java/com/android/server/os/NativeTombstoneManager.java +++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java @@ -392,7 +392,7 @@ public final class NativeTombstoneManager { int pid = 0; int uid = 0; - String processName = ""; + String processName = null; String crashReason = ""; String selinuxLabel = ""; @@ -407,8 +407,10 @@ public final class NativeTombstoneManager { uid = stream.readInt(Tombstone.UID); break; - case (int) Tombstone.PROCESS_NAME: - processName = stream.readString(Tombstone.PROCESS_NAME); + case (int) Tombstone.COMMAND_LINE: + if (processName == null) { + processName = stream.readString(Tombstone.COMMAND_LINE); + } break; case (int) Tombstone.CAUSES: @@ -472,7 +474,7 @@ public final class NativeTombstoneManager { result.mAppId = appId; result.mPid = pid; result.mUid = uid; - result.mProcessName = processName; + result.mProcessName = processName == null ? "" : processName; result.mTimestampMs = timestampMs; result.mCrashReason = crashReason; diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 5d8b75d2af4e..37b9d5649d7d 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -51,8 +51,8 @@ import android.os.FileUtils; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.os.UserHandle; import android.os.Trace; +import android.os.UserHandle; import android.os.WorkSource; import android.os.storage.StorageManager; import android.util.Log; @@ -258,22 +258,22 @@ public class PackageDexOptimizer { packageStats, options.isDowngrade(), profileName, dexMetadataPath, options.getCompilationReason()); - // Only report metrics for base apk for now. - // TODO: add ISA and APK type to metrics. - if (pkg.getBaseCodePath().equals(path)) { + // OTAPreopt doesn't have stats so don't report in that case. + if (packageStats != null) { Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics"); try { long sessionId = Math.randomLongInternal(); ArtStatsLogUtils.writeStatsLog( mArtStatsLogger, sessionId, - path, compilerFilter, sharedGid, packageStats.getCompileTime(path), dexMetadataPath, options.getCompilationReason(), - newResult); + newResult, + ArtStatsLogUtils.getApkType(path), + dexCodeIsa); } finally { Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER); } diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java index c8dc1b1ff562..f99a3c330c32 100644 --- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java +++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java @@ -39,6 +39,7 @@ public class ArtStatsLogUtils { private static final String TAG = ArtStatsLogUtils.class.getSimpleName(); private static final String PROFILE_DEX_METADATA = "primary.prof"; private static final String VDEX_DEX_METADATA = "primary.vdex"; + private static final String BASE_APK= "base.apk"; private static final int ART_COMPILATION_REASON_INSTALL_BULK_SECONDARY = @@ -122,16 +123,34 @@ public class ArtStatsLogUtils { ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK); } + private static final Map<String, Integer> ISA_MAP = new HashMap(); + + static { + COMPILE_FILTER_MAP.put("arm", ArtStatsLog. + ART_DATUM_REPORTED__ISA__ART_ISA_ARM); + COMPILE_FILTER_MAP.put("arm64", ArtStatsLog. + ART_DATUM_REPORTED__ISA__ART_ISA_ARM64); + COMPILE_FILTER_MAP.put("x86", ArtStatsLog. + ART_DATUM_REPORTED__ISA__ART_ISA_X86); + COMPILE_FILTER_MAP.put("x86_64", ArtStatsLog. + ART_DATUM_REPORTED__ISA__ART_ISA_X86_64); + COMPILE_FILTER_MAP.put("mips", ArtStatsLog. + ART_DATUM_REPORTED__ISA__ART_ISA_MIPS); + COMPILE_FILTER_MAP.put("mips64", ArtStatsLog. + ART_DATUM_REPORTED__ISA__ART_ISA_MIPS64); + } + public static void writeStatsLog( ArtStatsLogger logger, long sessionId, - String path, String compilerFilter, int uid, long compileTime, String dexMetadataPath, int compilationReason, - int result) { + int result, + int apkType, + String isa) { int dexMetadataType = getDexMetadataType(dexMetadataPath); logger.write( sessionId, @@ -140,7 +159,9 @@ public class ArtStatsLogUtils { compilerFilter, ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE, result, - dexMetadataType); + dexMetadataType, + apkType, + isa); logger.write( sessionId, uid, @@ -148,7 +169,16 @@ public class ArtStatsLogUtils { compilerFilter, ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME, compileTime, - dexMetadataType); + dexMetadataType, + apkType, + isa); + } + + public static int getApkType(String path) { + if (path.equals(BASE_APK)) { + return ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE; + } + return ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_SPLIT; } private static int getDexMetadataType(String dexMetadataPath) { @@ -207,7 +237,9 @@ public class ArtStatsLogUtils { String compilerFilter, int kind, long value, - int dexMetadataType) { + int dexMetadataType, + int apkType, + String isa) { ArtStatsLog.write( ArtStatsLog.ART_DATUM_REPORTED, sessionId, @@ -220,7 +252,10 @@ public class ArtStatsLogUtils { ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN, kind, value, - dexMetadataType); + dexMetadataType, + apkType, + ISA_MAP.getOrDefault(isa, + ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN)); } } } diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index 0a6772bd8f6a..fe21201f5cb7 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -734,7 +734,15 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return REBOOT_ERROR_SLOT_MISMATCH; } - if (!mInjector.getLockSettingsService().armRebootEscrow()) { + final long origId = Binder.clearCallingIdentity(); + boolean result; + try { + result = mInjector.getLockSettingsService().armRebootEscrow(); + } finally { + Binder.restoreCallingIdentity(origId); + } + + if (!result) { Slog.w(TAG, "Failure to escrow key for reboot"); return REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE; } @@ -742,11 +750,20 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return REBOOT_ERROR_NONE; } + private boolean useServerBasedRoR() { + final long origId = Binder.clearCallingIdentity(); + try { + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA, + "server_based_ror_enabled", false); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + private void reportMetricsOnRebootWithLskf(String packageName, boolean slotSwitch, @ResumeOnRebootRebootErrorCode int errorCode) { int uid = mInjector.getUidFromPackageName(packageName); - boolean serverBased = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA, - "server_based_ror_enabled", false); + boolean serverBased = useServerBasedRoR(); int preparedClientCount; synchronized (this) { preparedClientCount = mCallerPreparedForReboot.size(); diff --git a/services/core/java/com/android/server/stats/StatsHelper.java b/services/core/java/com/android/server/stats/StatsHelper.java new file mode 100644 index 000000000000..9b9f6b502f2c --- /dev/null +++ b/services/core/java/com/android/server/stats/StatsHelper.java @@ -0,0 +1,45 @@ +/* + * 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.stats; + +import static android.app.StatsManager.ACTION_STATSD_STARTED; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.Context; +import android.content.Intent; +import android.os.UserHandle; + +/** + * Provides helper methods for the Statsd APEX + * + * @hide + **/ +@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) +public final class StatsHelper { + private StatsHelper() {} + + /** + * Send statsd ready broadcast + * + **/ + public static void sendStatsdReadyBroadcast(@NonNull final Context context) { + context.sendBroadcastAsUser( + new Intent(ACTION_STATSD_STARTED).addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND), + UserHandle.SYSTEM, android.Manifest.permission.DUMP); + } +} diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java index 35194658a48f..85d2d39d7a1d 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java @@ -20,7 +20,7 @@ import android.annotation.Nullable; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; -import android.net.ConnectivityManager; +import android.content.pm.PackageManager; import android.os.SystemProperties; import android.provider.Settings; @@ -52,9 +52,7 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat } private boolean deviceHasTelephonyNetwork() { - // TODO b/150583524 Avoid the use of a deprecated API. - return mContext.getSystemService(ConnectivityManager.class) - .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); } @Override diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java index 8dcc547508ec..6ff8d6f8ba94 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -290,25 +290,6 @@ public class UnderlyingNetworkTracker { maybeNotifyCallback(); } - private void handleNetworkSuspended(@NonNull Network network, boolean isSuspended) { - mVcnContext.ensureRunningOnLooperThread(); - - if (!isSameNetwork(mRecordInProgress, network)) { - Slog.wtf(TAG, "Invalid update to isSuspended"); - return; - } - - final NetworkCapabilities newCaps = - new NetworkCapabilities(mRecordInProgress.getNetworkCapabilities()); - if (isSuspended) { - newCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - } else { - newCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); - } - - handleCapabilitiesChanged(network, newCaps); - } - private void handlePropertiesChanged( @NonNull Network network, @NonNull LinkProperties linkProperties) { mVcnContext.ensureRunningOnLooperThread(); @@ -366,20 +347,11 @@ public class UnderlyingNetworkTracker { @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { + if (networkCapabilities.equals(mRecordInProgress.getNetworkCapabilities())) return; handleCapabilitiesChanged(network, networkCapabilities); } @Override - public void onNetworkSuspended(@NonNull Network network) { - handleNetworkSuspended(network, true /* isSuspended */); - } - - @Override - public void onNetworkResumed(@NonNull Network network) { - handleNetworkSuspended(network, false /* isSuspended */); - } - - @Override public void onLinkPropertiesChanged( @NonNull Network network, @NonNull LinkProperties linkProperties) { handlePropertiesChanged(network, linkProperties); diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 3f74938005a7..89ed956b3aef 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -41,6 +41,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -110,6 +111,24 @@ public class Vcn extends Handler { @NonNull private final VcnNetworkRequestListener mRequestListener; @NonNull private final VcnCallback mVcnCallback; + /** + * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs. + * + * <p>Due to potential for race conditions, VcnGatewayConnections MUST only be created and added + * to this map in {@link #handleNetworkRequested(NetworkRequest, int, int)}, when a VCN receives + * a NetworkRequest that matches a VcnGatewayConnectionConfig for this VCN's VcnConfig. + * + * <p>A VcnGatewayConnection instance MUST NEVER overwrite an existing instance - otherwise + * there is potential for a orphaned VcnGatewayConnection instance that does not get properly + * shut down. + * + * <p>Due to potential for race conditions, VcnGatewayConnections MUST only be removed from this + * map once they have finished tearing down, which is reported to this VCN via {@link + * VcnGatewayStatusCallback#onQuit()}. Once this is done, all NetworkRequests are retrieved from + * the NetworkProvider so that another VcnGatewayConnectionConfig can match the + * previously-matched request. + */ + // TODO(b/182533200): remove the invariant on VcnGatewayConnection lifecycles @NonNull private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections = new HashMap<>(); @@ -191,6 +210,19 @@ public class Vcn extends Handler { return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values())); } + /** Get current Configs and Gateways for testing purposes */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public Map<VcnGatewayConnectionConfig, VcnGatewayConnection> + getVcnGatewayConnectionConfigMap() { + return Collections.unmodifiableMap(new HashMap<>(mVcnGatewayConnections)); + } + + /** Set whether this Vcn is active for testing purposes */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public void setIsActive(boolean isActive) { + mIsActive.set(isActive); + } + private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener { @Override public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { @@ -202,11 +234,6 @@ public class Vcn extends Handler { @Override public void handleMessage(@NonNull Message msg) { - // Ignore if this Vcn is not active and we're not receiving new configs - if (!isActive() && msg.what != MSG_EVENT_CONFIG_UPDATED) { - return; - } - switch (msg.what) { case MSG_EVENT_CONFIG_UPDATED: handleConfigUpdated((VcnConfig) msg.obj); @@ -237,9 +264,31 @@ public class Vcn extends Handler { mConfig = config; - // TODO(b/181815405): Reevaluate active VcnGatewayConnection(s) + if (mIsActive.getAndSet(true)) { + // VCN is already active - teardown any GatewayConnections whose configs have been + // removed and get all current requests + for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : + mVcnGatewayConnections.entrySet()) { + final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey(); + final VcnGatewayConnection gatewayConnection = entry.getValue(); + + // GatewayConnectionConfigs must match exactly (otherwise authentication or + // connection details may have changed). + if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) { + if (gatewayConnection == null) { + Slog.wtf( + getLogTag(), + "Found gatewayConnectionConfig without GatewayConnection"); + } else { + gatewayConnection.teardownAsynchronously(); + } + } + } - if (!mIsActive.getAndSet(true)) { + // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be + // satisfied start a new GatewayConnection) + mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); + } else { // If this VCN was not previously active, it is exiting Safe Mode. Re-register the // request listener to get NetworkRequests again (and all cached requests). mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener); @@ -259,13 +308,16 @@ public class Vcn extends Handler { private void handleEnterSafeMode() { handleTeardown(); - mVcnGatewayConnections.clear(); - mVcnCallback.onEnteredSafeMode(); } private void handleNetworkRequested( @NonNull NetworkRequest request, int score, int providerId) { + if (!isActive()) { + Slog.v(getLogTag(), "Received NetworkRequest while inactive. Ignore for now"); + return; + } + if (score > getNetworkScore()) { if (VDBG) { Slog.v( @@ -318,8 +370,10 @@ public class Vcn extends Handler { mVcnGatewayConnections.remove(config); // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied - // start a new GatewayConnection) - mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); + // start a new GatewayConnection), but only if the Vcn is still active + if (isActive()) { + mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); + } } private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) { diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index a262939c0ef9..1208eccc69eb 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -30,6 +30,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UpdateEngine; import android.os.UpdateEngineCallback; +import android.os.UserHandle; +import android.os.UserManager; import android.provider.DeviceConfig; import android.util.Log; @@ -295,10 +297,56 @@ public final class ProfcollectForwardingService extends SystemService { return; } - try { - mIProfcollect.report(); - } catch (RemoteException e) { - Log.e(LOG_TAG, e.getMessage()); + final boolean uploadReport = + DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, + "upload_report", false); + + new Thread(() -> { + try { + String reportUuid = mIProfcollect.report(); + + if (!uploadReport) { + return; + } + + final int profileId = getBBProfileId(); + mIProfcollect.copy_report_to_bb(profileId, reportUuid); + String reportPath = + "/data/user/" + profileId + + "/com.google.android.apps.internal.betterbug/cache/" + + reportUuid + ".zip"; + Intent uploadIntent = + new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE") + .setPackage("com.google.android.apps.internal.betterbug") + .putExtra("EXTRA_DESTINATION", "PROFCOLLECT") + .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName()) + .putExtra("EXTRA_PROFILE_PATH", reportPath) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + Context context = getContext(); + if (context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0) != null) { + context.sendBroadcast(uploadIntent); + } + mIProfcollect.delete_report(reportUuid); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage()); + } + }).start(); + } + + /** + * Get BetterBug's profile ID. It is the work profile ID, if it exists. Otherwise the system + * user ID. + * + * @return BetterBug's profile ID. + */ + private int getBBProfileId() { + UserManager userManager = UserManager.get(getContext()); + int[] profiles = userManager.getProfileIds(UserHandle.USER_SYSTEM, false); + for (int p : profiles) { + if (userManager.getUserInfo(p).isManagedProfile()) { + return p; + } } + return UserHandle.USER_SYSTEM; } } diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index 1b8ab2175458..2f0d71a2a579 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -58,6 +58,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; /** * Tests for {@link com.android.server.apphibernation.AppHibernationService} @@ -116,8 +117,8 @@ public final class AppHibernationServiceTest { mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); UserInfo userInfo = addUser(USER_ID_1); - mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1); + mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); mAppHibernationService.mIsServiceEnabled = true; } @@ -150,8 +151,8 @@ public final class AppHibernationServiceTest { throws RemoteException { // WHEN a new user is added and a package from the user is hibernated UserInfo user2 = addUser(USER_ID_2); - mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); + mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(user2)); mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true); // THEN the new user's package is hibernated @@ -188,8 +189,8 @@ public final class AppHibernationServiceTest { // GIVEN an unlocked user with all packages installed UserInfo userInfo = addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3}); - mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2); + mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); // WHEN packages are hibernated for the user mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true); @@ -259,6 +260,12 @@ public final class AppHibernationServiceTest { } @Override + public Executor getBackgroundExecutor() { + // Just execute immediately in tests. + return r -> r.run(); + } + + @Override public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() { return Mockito.mock(HibernationStateDiskStore.class); } 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 fcd6b842426a..7bdc87ef6bd7 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -116,7 +116,7 @@ class CompatConfigBuilder { } CompatConfigBuilder addOverridableChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true)); + mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", 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 bd774056aef8..a866363f46e6 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -260,6 +260,36 @@ public class CompatConfigTest { } @Test + public void testInstallerCanSetOverrides() throws Exception { + final long changeId = 1234L; + final int installerUid = 23; + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addOverridableChangeWithId(1234L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + PackageManager packageManager = mock(PackageManager.class); + when(mContext.getPackageManager()).thenReturn(packageManager); + when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt())) + .thenReturn(applicationInfo); + + // Force the validator to prevent overriding the change by using a user build. + when(mBuildClassifier.isDebuggableBuild()).thenReturn(false); + when(mBuildClassifier.isFinalBuild()).thenReturn(true); + + CompatibilityOverrideConfig config = new CompatibilityOverrideConfig( + Collections.singletonMap(1234L, + new PackageOverride.Builder() + .setMaxVersionCode(99L) + .setEnabled(true) + .build())); + + compatConfig.addOverrides(config, "com.some.package"); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); + } + + @Test public void testApplyDeferredOverridesAfterInstallingApp() throws Exception { ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() .withPackageName("com.notinstalled.foo") @@ -639,9 +669,18 @@ public class CompatConfigTest { .build()); when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt())) .thenThrow(new NameNotFoundException()); - - compatConfig.addOverride(1L, "foo.bar", true); - compatConfig.addOverride(2L, "bar.baz", false); + compatConfig.addOverrides( + new CompatibilityOverrideConfig( + Collections.singletonMap( + 1L, + new PackageOverride.Builder().setEnabled(true).build())), + "foo.bar"); + compatConfig.addOverrides( + new CompatibilityOverrideConfig( + Collections.singletonMap( + 2L, + new PackageOverride.Builder().setEnabled(false).build())), + "bar.baz"); assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<overrides>\n" 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 3fc6e9918382..a2664e5f3b0a 100644 --- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java @@ -113,7 +113,7 @@ public class PlatformCompatTest { 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)); + new CompatibilityChangeInfo(8L, "", -1, -1, true, false, "", true)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/content/OWNERS b/services/tests/servicestests/src/com/android/server/content/OWNERS new file mode 100644 index 000000000000..6264a1427c7f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/content/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/content/OWNERS 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 91342ce925f6..8c08226201a8 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -21,6 +21,7 @@ import static android.content.pm.UserInfo.FLAG_PRIMARY; import static android.content.pm.UserInfo.FLAG_PROFILE; import static android.os.UserHandle.USER_SYSTEM; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -110,6 +111,10 @@ public class RebootEscrowManagerTests { public interface MockableRebootEscrowInjected { int getBootCount(); + long getCurrentTimeMillis(); + + boolean forceServerBased(); + void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete); } @@ -174,6 +179,9 @@ public class RebootEscrowManagerTests { @Override public boolean serverBasedResumeOnReboot() { + if (mInjected.forceServerBased()) { + return true; + } return mServerBased; } @@ -205,9 +213,20 @@ public class RebootEscrowManagerTests { } @Override + public String getVbmetaDigest(boolean other) { + return other ? "" : "fake digest"; + } + + @Override + public long getCurrentTimeMillis() { + return mInjected.getCurrentTimeMillis(); + } + + @Override public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete) { + mInjected.reportMetric(success, errorCode, serviceType, attemptCount, escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete); } @@ -430,16 +449,21 @@ public class RebootEscrowManagerTests { // pretend reboot happens here when(mInjected.getBootCount()).thenReturn(1); + when(mInjected.getCurrentTimeMillis()).thenReturn(30000L); + mStorage.setLong(RebootEscrowManager.REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, 10000L, + USER_SYSTEM); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), eq(0) /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */, - anyInt(), anyInt(), anyInt()); + eq(20), eq(0) /* vbmeta status */, anyInt()); when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue()); mService.loadRebootEscrowDataIfAvailable(null); verify(mRebootEscrow).retrieveKey(); assertTrue(metricsSuccessCaptor.getValue()); verify(mKeyStoreManager).clearKeyStoreEncryptionKey(); + assertEquals(mStorage.getLong(RebootEscrowManager.REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, + -1, USER_SYSTEM), -1); } @Test @@ -468,7 +492,7 @@ public class RebootEscrowManagerTests { ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */, - anyInt(), anyInt(), anyInt()); + anyInt(), eq(0) /* vbmeta status */, anyInt()); when(mServiceConnection.unwrap(any(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); @@ -479,6 +503,84 @@ public class RebootEscrowManagerTests { } @Test + public void loadRebootEscrowDataIfAvailable_ServerBasedRemoteException_Failure() + 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); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, + eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class); + mService.loadRebootEscrowDataIfAvailable(null); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + metricsErrorCodeCaptor.getValue()); + } + + @Test + public void loadRebootEscrowDataIfAvailable_ServerBasedIoError_RetryFailure() 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); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, + eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class); + + HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); + thread.start(); + mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper())); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED), + metricsErrorCodeCaptor.getValue()); + } + + @Test public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception { setServerBasedRebootEscrowProvider(); @@ -607,9 +709,14 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(10); when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue()); + // Trigger a vbmeta digest mismatch + mStorage.setString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST, + "non sense value", USER_SYSTEM); mService.loadRebootEscrowDataIfAvailable(null); verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */, - eq(1) /* attempt count */, anyInt(), anyInt(), anyInt()); + eq(1) /* attempt count */, anyInt(), eq(2) /* vbmeta status */, anyInt()); + assertEquals(mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST, + "", USER_SYSTEM), ""); } @Test @@ -636,12 +743,17 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + // Return a null escrow key doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - anyInt() /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */, - anyInt(), anyInt(), anyInt()); - when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]); + metricsErrorCodeCaptor.capture(), eq(1) /* HAL based */, + eq(1) /* attempt count */, anyInt(), anyInt(), anyInt()); + + when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null); mService.loadRebootEscrowDataIfAvailable(null); verify(mRebootEscrow).retrieveKey(); assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + metricsErrorCodeCaptor.getValue()); } } 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 fb01ff6e16c6..100d3ea87a89 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -1773,57 +1773,75 @@ public class NetworkPolicyManagerServiceTest { true); } + private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) { + stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE, + rxBytes, 1, txBytes, 1, 0); + when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong())) + .thenReturn(stats.getTotalBytes()); + when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong())) + .thenReturn(stats); + } + + private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException { + final NetworkPolicyManagerInternal npmi = LocalServices + .getService(NetworkPolicyManagerInternal.class); + npmi.onStatsProviderWarningOrLimitReached("TEST"); + // Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED. + postMsgAndWaitForCompletion(); + verify(mStatsService).forceUpdate(); + // Wait for processing of MSG_*_INTERFACE_QUOTAS. + postMsgAndWaitForCompletion(); + } + /** - * Test that when StatsProvider triggers limit reached, new limit will be calculated and - * re-armed. + * Test that when StatsProvider triggers warning and limit reached, new quotas will be + * calculated and re-armed. */ @Test - public void testStatsProviderLimitReached() throws Exception { + public void testStatsProviderWarningAndLimitReached() throws Exception { final int CYCLE_DAY = 15; final NetworkStats stats = new NetworkStats(0L, 1); - stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE, - 2999, 1, 2000, 1, 0); - when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong())) - .thenReturn(stats.getTotalBytes()); - when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong())) - .thenReturn(stats); + increaseMockedTotalBytes(stats, 2999, 2000); // Get active mobile network in place expectMobileDefaults(); mService.updateNetworks(); - verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, Long.MAX_VALUE); + verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, + Long.MAX_VALUE); - // Set limit to 10KB. + // Set warning to 7KB and limit to 10KB. setNetworkPolicies(new NetworkPolicy( - sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, 10000L, - true)); + sTemplateMobileAll, CYCLE_DAY, TIMEZONE_UTC, 7000L, 10000L, true)); postMsgAndWaitForCompletion(); - // Verifies that remaining quota is set to providers. - verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L); - + // Verifies that remaining quotas are set to providers. + verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L); reset(mStatsService); - // Increase the usage. - stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE, - 1000, 1, 999, 1, 0); - when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong())) - .thenReturn(stats.getTotalBytes()); - when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong())) - .thenReturn(stats); + // Increase the usage and simulates that limit reached fires earlier by provider, + // but actually the quota is not yet reached. Verifies that the limit reached leads to + // a force update and new quotas should be set. + increaseMockedTotalBytes(stats, 1000, 999); + triggerOnStatsProviderWarningOrLimitReached(); + verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L); + reset(mStatsService); - // Simulates that limit reached fires earlier by provider, but actually the quota is not - // yet reached. - final NetworkPolicyManagerInternal npmi = LocalServices - .getService(NetworkPolicyManagerInternal.class); - npmi.onStatsProviderLimitReached("TEST"); + // Increase the usage and simulate warning reached, the new warning should be unlimited + // since service will disable warning quota to stop lower layer from keep triggering + // warning reached event. + increaseMockedTotalBytes(stats, 1000L, 1000); + triggerOnStatsProviderWarningOrLimitReached(); + verify(mStatsService).setStatsProviderWarningAndLimitAsync( + TEST_IFACE, Long.MAX_VALUE, 1002L); + reset(mStatsService); - // Verifies that the limit reached leads to a force update and new limit should be set. - postMsgAndWaitForCompletion(); - verify(mStatsService).forceUpdate(); - postMsgAndWaitForCompletion(); - verify(mStatsService).setStatsProviderLimitAsync(TEST_IFACE, 10000L - 4999L - 1999L); + // Increase the usage that over the warning and limit, the new limit should set to 1 to + // block the network traffic. + increaseMockedTotalBytes(stats, 1000L, 1000); + triggerOnStatsProviderWarningOrLimitReached(); + verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L); + reset(mStatsService); } /** diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java index 13d75a77507f..f014119319e9 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java @@ -22,6 +22,7 @@ import com.android.internal.art.ArtStatsLog; import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -49,6 +50,9 @@ public final class ArtStatsLogUtilsTest { private static final String COMPILER_FILTER = "space-profile"; private static final String PROFILE_DEX_METADATA = "primary.prof"; private static final String VDEX_DEX_METADATA = "primary.vdex"; + private static final String INSTRUCTION_SET = "arm64"; + private static final String BASE_APK = "base.apk"; + private static final String SPLIT_APK = "split.apk"; private static final byte[] DEX_CONTENT = "dexData".getBytes(); private static final int COMPILATION_REASON = 1; private static final int RESULT_CODE = 222; @@ -97,17 +101,18 @@ public final class ArtStatsLogUtilsTest { ArtStatsLogUtils.writeStatsLog( mockLogger, SESSION_ID, - apk.toString(), COMPILER_FILTER, UID, COMPILE_TIME, dexMetadataPath.toString(), COMPILATION_REASON, - RESULT_CODE); + RESULT_CODE, + ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, + INSTRUCTION_SET); // Assert verifyWrites(ArtStatsLog. - ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX); + ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX); } finally { deleteSliently(dexMetadataPath); deleteSliently(apk); @@ -127,17 +132,18 @@ public final class ArtStatsLogUtilsTest { ArtStatsLogUtils.writeStatsLog( mockLogger, SESSION_ID, - apk.toString(), COMPILER_FILTER, UID, COMPILE_TIME, dexMetadataPath.toString(), COMPILATION_REASON, - RESULT_CODE); + RESULT_CODE, + ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, + INSTRUCTION_SET); // Assert verifyWrites(ArtStatsLog. - ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE); + ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE); } finally { deleteSliently(dexMetadataPath); deleteSliently(apk); @@ -157,17 +163,18 @@ public final class ArtStatsLogUtilsTest { ArtStatsLogUtils.writeStatsLog( mockLogger, SESSION_ID, - apk.toString(), COMPILER_FILTER, UID, COMPILE_TIME, dexMetadataPath.toString(), COMPILATION_REASON, - RESULT_CODE); + RESULT_CODE, + ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, + INSTRUCTION_SET); // Assert verifyWrites(ArtStatsLog. - ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX); + ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX); } finally { deleteSliently(dexMetadataPath); deleteSliently(apk); @@ -185,17 +192,18 @@ public final class ArtStatsLogUtilsTest { ArtStatsLogUtils.writeStatsLog( mockLogger, SESSION_ID, - apk.toString(), COMPILER_FILTER, UID, COMPILE_TIME, /*dexMetadataPath=*/ null, COMPILATION_REASON, - RESULT_CODE); + RESULT_CODE, + ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, + INSTRUCTION_SET); // Assert verifyWrites(ArtStatsLog. - ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE); + ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE); } finally { deleteSliently(apk); } @@ -214,23 +222,35 @@ public final class ArtStatsLogUtilsTest { ArtStatsLogUtils.writeStatsLog( mockLogger, SESSION_ID, - apk.toString(), COMPILER_FILTER, UID, COMPILE_TIME, dexMetadataPath.toString(), COMPILATION_REASON, - RESULT_CODE); + RESULT_CODE, + ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, + INSTRUCTION_SET); // Assert verifyWrites(ArtStatsLog. - ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN); + ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN); } finally { deleteSliently(dexMetadataPath); deleteSliently(apk); } } + @Test + public void testGetApkType() { + // Act + int result1 = ArtStatsLogUtils.getApkType(BASE_APK); + int result2 = ArtStatsLogUtils.getApkType(SPLIT_APK); + + // Assert + Assert.assertEquals(result1, ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE); + Assert.assertEquals(result2, ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_SPLIT); + } + private void verifyWrites(int dexMetadataType) { InOrder inorder = inOrder(mockLogger); inorder.verify(mockLogger).write( @@ -239,7 +259,9 @@ public final class ArtStatsLogUtilsTest { COMPILER_FILTER, ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE, RESULT_CODE, - dexMetadataType); + dexMetadataType, + ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, + INSTRUCTION_SET); inorder.verify(mockLogger).write( SESSION_ID, UID, @@ -247,7 +269,9 @@ public final class ArtStatsLogUtilsTest { COMPILER_FILTER, ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME, COMPILE_TIME, - dexMetadataType); + dexMetadataType, + ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, + INSTRUCTION_SET); } private Path zipFiles(String suffix, Path... files) throws IOException { diff --git a/services/tests/servicestests/utils-mockito/com/android/server/testutils/OWNERS b/services/tests/servicestests/utils-mockito/com/android/server/testutils/OWNERS new file mode 100644 index 000000000000..d825dfd7cf00 --- /dev/null +++ b/services/tests/servicestests/utils-mockito/com/android/server/testutils/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/pm/OWNERS diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 4259831c15f8..ee89e1c840d6 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -833,10 +833,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testDefaultAssistant_overrideDefault() { - final int userId = 0; + final int userId = mContext.getUserId(); final String testComponent = "package/class"; final List<UserInfo> userInfos = new ArrayList<>(); - userInfos.add(new UserInfo(0, "", 0)); + userInfos.add(new UserInfo(userId, "", 0)); final ArraySet<ComponentName> validAssistants = new ArraySet<>(); validAssistants.add(ComponentName.unflattenFromString(testComponent)); when(mActivityManager.isLowRamDevice()).thenReturn(false); @@ -2393,7 +2393,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(mTestNotificationChannel); reset(mListeners); - mBinderService.updateNotificationChannelForPackage(PKG, 0, mTestNotificationChannel); + mBinderService.updateNotificationChannelForPackage(PKG, mUid, mTestNotificationChannel); verify(mListeners, times(1)).notifyNotificationChannelChanged(eq(PKG), eq(Process.myUserHandle()), eq(mTestNotificationChannel), eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED)); @@ -2929,7 +2929,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetListenerAccessForUser() throws Exception { - UserHandle user = UserHandle.of(10); + UserHandle user = UserHandle.of(mContext.getUserId() + 10); ComponentName c = ComponentName.unflattenFromString("package/Component"); mBinderService.setNotificationListenerAccessGrantedForUser(c, user.getIdentifier(), true); @@ -2945,20 +2945,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testSetAssistantAccessForUser() throws Exception { - UserHandle user = UserHandle.of(10); - List<UserInfo> uis = new ArrayList<>(); UserInfo ui = new UserInfo(); - ui.id = 10; + ui.id = mContext.getUserId() + 10; + UserHandle user = UserHandle.of(ui.id); + List<UserInfo> uis = new ArrayList<>(); uis.add(ui); ComponentName c = ComponentName.unflattenFromString("package/Component"); - when(mUm.getEnabledProfiles(10)).thenReturn(uis); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); mBinderService.setNotificationAssistantAccessGrantedForUser(c, user.getIdentifier(), true); verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), true, true); - verify(mAssistants).setUserSet(10, true); + verify(mAssistants).setUserSet(ui.id, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, true); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -2967,7 +2967,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testGetAssistantAllowedForUser() throws Exception { - UserHandle user = UserHandle.of(10); + UserHandle user = UserHandle.of(mContext.getUserId() + 10); try { mBinderService.getAllowedNotificationAssistantForUser(user.getIdentifier()); } catch (IllegalStateException e) { @@ -2987,12 +2987,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throw e; } } - verify(mAssistants, times(1)).getAllowedComponents(0); + verify(mAssistants, times(1)).getAllowedComponents(mContext.getUserId()); } @Test public void testSetDndAccessForUser() throws Exception { - UserHandle user = UserHandle.of(10); + UserHandle user = UserHandle.of(mContext.getUserId() + 10); ComponentName c = ComponentName.unflattenFromString("package/Component"); mBinderService.setNotificationPolicyAccessGrantedForUser( c.getPackageName(), user.getIdentifier(), true); @@ -3012,9 +3012,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationListenerAccessGranted(c, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), mContext.getUserId(), true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), mContext.getUserId(), false, true); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3023,7 +3023,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetAssistantAccess() throws Exception { List<UserInfo> uis = new ArrayList<>(); UserInfo ui = new UserInfo(); - ui.id = 0; + ui.id = mContext.getUserId(); uis.add(ui); when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); ComponentName c = ComponentName.unflattenFromString("package/Component"); @@ -3031,9 +3031,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), ui.id, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), ui.id, false, true); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3042,10 +3042,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetAssistantAccess_multiProfile() throws Exception { List<UserInfo> uis = new ArrayList<>(); UserInfo ui = new UserInfo(); - ui.id = 0; + ui.id = mContext.getUserId(); uis.add(ui); UserInfo ui10 = new UserInfo(); - ui10.id = 10; + ui10.id = mContext.getUserId() + 10; uis.add(ui10); when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); ComponentName c = ComponentName.unflattenFromString("package/Component"); @@ -3053,13 +3053,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), ui.id, true, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 10, true, true); + c.flattenToString(), ui10.id, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), ui.id, false, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 10, false, true); + c.flattenToString(), ui10.id, false, true); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3072,16 +3072,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList); List<UserInfo> uis = new ArrayList<>(); UserInfo ui = new UserInfo(); - ui.id = 0; + ui.id = mContext.getUserId(); uis.add(ui); when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); mBinderService.setNotificationAssistantAccessGranted(null, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, false); + c.flattenToString(), ui.id, true, false); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, false); + c.flattenToString(), ui.id, false, false); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3090,21 +3090,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testSetAssistantAccessForUser_nullWithAllowedAssistant() throws Exception { List<UserInfo> uis = new ArrayList<>(); UserInfo ui = new UserInfo(); - ui.id = 10; + ui.id = mContext.getUserId() + 10; uis.add(ui); UserHandle user = ui.getUserHandle(); ArrayList<ComponentName> componentList = new ArrayList<>(); ComponentName c = ComponentName.unflattenFromString("package/Component"); componentList.add(c); when(mAssistants.getAllowedComponents(anyInt())).thenReturn(componentList); - when(mUm.getEnabledProfiles(10)).thenReturn(uis); + when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); mBinderService.setNotificationAssistantAccessGrantedForUser( null, user.getIdentifier(), true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), true, false); - verify(mAssistants).setUserSet(10, true); + verify(mAssistants).setUserSet(ui.id, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, false); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -3116,10 +3116,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { throws Exception { List<UserInfo> uis = new ArrayList<>(); UserInfo ui = new UserInfo(); - ui.id = 0; + ui.id = mContext.getUserId(); uis.add(ui); UserInfo ui10 = new UserInfo(); - ui10.id = 10; + ui10.id = mContext.getUserId() + 10; uis.add(ui10); UserHandle user = ui.getUserHandle(); ArrayList<ComponentName> componentList = new ArrayList<>(); @@ -3135,8 +3135,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { c.flattenToString(), user.getIdentifier(), true, false); verify(mAssistants, times(1)).setPackageOrComponentEnabled( c.flattenToString(), ui10.id, true, false); - verify(mAssistants).setUserSet(0, true); - verify(mAssistants).setUserSet(10, true); + verify(mAssistants).setUserSet(ui.id, true); + verify(mAssistants).setUserSet(ui10.id, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, false); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( @@ -3152,7 +3152,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationPolicyAccessGranted(c.getPackageName(), true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.getPackageName(), 0, true, true); + c.getPackageName(), mContext.getUserId(), true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -3179,7 +3179,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ComponentName c = ComponentName.unflattenFromString("package/Component"); List<UserInfo> uis = new ArrayList<>(); UserInfo ui = new UserInfo(); - ui.id = 0; + ui.id = mContext.getUserId(); uis.add(ui); when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); @@ -3214,9 +3214,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationListenerAccessGranted(c, true); verify(mListeners, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), mContext.getUserId(), true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), mContext.getUserId(), false, true); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3228,7 +3228,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { ComponentName c = ComponentName.unflattenFromString("package/Component"); List<UserInfo> uis = new ArrayList<>(); UserInfo ui = new UserInfo(); - ui.id = 0; + ui.id = mContext.getUserId(); uis.add(ui); when(mUm.getEnabledProfiles(ui.id)).thenReturn(uis); @@ -3237,9 +3237,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mListeners, never()).setPackageOrComponentEnabled( anyString(), anyInt(), anyBoolean(), anyBoolean()); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, false, true); + c.flattenToString(), ui.id, false, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), ui.id, true, true); } @Test @@ -3253,7 +3253,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mListeners, never()).setPackageOrComponentEnabled( anyString(), anyInt(), anyBoolean(), anyBoolean()); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( - c.getPackageName(), 0, true, true); + c.getPackageName(), mContext.getUserId(), true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 477592ba4129..26cea2ca7a6c 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -837,20 +837,31 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser boolean prevHostConnected = mHostConnected; UsbPort port = (UsbPort) args.arg1; UsbPortStatus status = (UsbPortStatus) args.arg2; - mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST; - mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE; - mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK; - mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY); + + if (status != null) { + mHostConnected = status.getCurrentDataRole() == DATA_ROLE_HOST; + mSourcePower = status.getCurrentPowerRole() == POWER_ROLE_SOURCE; + mSinkPower = status.getCurrentPowerRole() == POWER_ROLE_SINK; + mAudioAccessoryConnected = (status.getCurrentMode() == MODE_AUDIO_ACCESSORY); + + // Ideally we want to see if PR_SWAP and DR_SWAP is supported. + // But, this should be suffice, since, all four combinations are only supported + // when PR_SWAP and DR_SWAP are supported. + mSupportsAllCombinations = status.isRoleCombinationSupported( + POWER_ROLE_SOURCE, DATA_ROLE_HOST) + && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST) + && status.isRoleCombinationSupported(POWER_ROLE_SOURCE, + DATA_ROLE_DEVICE) + && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE); + } else { + mHostConnected = false; + mSourcePower = false; + mSinkPower = false; + mAudioAccessoryConnected = false; + mSupportsAllCombinations = false; + } + mAudioAccessorySupported = port.isModeSupported(MODE_AUDIO_ACCESSORY); - // Ideally we want to see if PR_SWAP and DR_SWAP is supported. - // But, this should be suffice, since, all four combinations are only supported - // when PR_SWAP and DR_SWAP are supported. - mSupportsAllCombinations = status.isRoleCombinationSupported( - POWER_ROLE_SOURCE, DATA_ROLE_HOST) - && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_HOST) - && status.isRoleCombinationSupported(POWER_ROLE_SOURCE, - DATA_ROLE_DEVICE) - && status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE); args.recycle(); updateUsbNotification(false); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ae1984e67c9c..7c234fcc949e 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -17,7 +17,6 @@ package android.telephony; import android.Manifest; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -32,10 +31,11 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; +import android.telephony.gba.TlsParams; +import android.telephony.gba.UaSecurityProtocolIdentifier; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsRegistrationAttributes; import android.telephony.ims.ImsSsData; -import android.telephony.ims.SipDelegateManager; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.feature.RcsFeature; @@ -3616,6 +3616,71 @@ public class CarrierConfigManager { public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool"; /** + * Indicates that GBA_ME should be used for GBA authentication, as defined in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_ME = 1; + + /** + * Indicates that GBA_U should be used for GBA authentication, as defined in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_U = 2; + + /** + * Indicates that GBA_Digest should be used for GBA authentication, as defined + * in 3GPP TS 33.220. + * @hide + */ + @SystemApi + public static final int GBA_DIGEST = 3; + + /** + * An integer representing the GBA mode to use for requesting credentials + * via {@link TelephonyManager#bootstrapAuthenticationRequest}. + * + * One of {@link #GBA_ME}, {@link #GBA_U}, or {@link #GBA_DIGEST}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_MODE_INT = "gba_mode_int"; + + /** + * An integer representing the organization code to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code ORG_} constants in {@link UaSecurityProtocolIdentifier}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_SECURITY_ORGANIZATION_INT = + "gba_ua_security_organization_int"; + + /** + * An integer representing the security protocol to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code UA_SECURITY_PROTOCOL_} constants in {@link UaSecurityProtocolIdentifier}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_SECURITY_PROTOCOL_INT = + "gba_ua_security_protocol_int"; + + /** + * An integer representing the cipher suite to be used when building the + * {@link UaSecurityProtocolIdentifier} used when requesting GBA authentication. + * + * See the {@code TLS_} constants in {@link android.telephony.gba.TlsParams}. + * @hide + */ + @SystemApi + public static final String KEY_GBA_UA_TLS_CIPHER_SUITE_INT = + "gba_ua_tls_cipher_suite_int"; + + /** * GPS configs. See the GNSS HAL documentation for more details. */ public static final class Gps { @@ -4026,6 +4091,14 @@ public class CarrierConfigManager { public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int"; + /** + * Specifies the RCS feature tag allowed for the carrier. + * + * <p>The values refer to RCC.07 2.4.4. + */ + public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY = + KEY_PREFIX + "rcs_feature_tag_allowed_string_array"; + private Ims() {} private static PersistableBundle getDefaults() { @@ -4039,6 +4112,27 @@ public class CarrierConfigManager { defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false); defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true); defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60); + defaults.putStringArray(KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY, new String[]{ + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.deferred\"", + "+g.gsma.rcs.cpm.pager-large", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftsms\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callcomposer\"", + "+g.gsma.callcomposer", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callunanswered\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.sharedmap\"", + "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.sharedsketch\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopush\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geosms\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot\"", + "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot.sa\"", + "+g.gsma.rcs.botversion=\"#=1,#=2\"", + "+g.gsma.rcs.cpimext"}); + return defaults; } } @@ -4794,6 +4888,13 @@ public class CarrierConfigManager { // Default wifi configurations. sDefaults.putAll(Wifi.getDefaults()); sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); + sDefaults.putInt(KEY_GBA_MODE_INT, GBA_ME); + sDefaults.putInt(KEY_GBA_UA_SECURITY_ORGANIZATION_INT, + UaSecurityProtocolIdentifier.ORG_3GPP); + sDefaults.putInt(KEY_GBA_UA_SECURITY_PROTOCOL_INT, + UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT); + sDefaults.putInt(KEY_GBA_UA_TLS_CIPHER_SUITE_INT, TlsParams.TLS_NULL_WITH_NULL_NULL); + sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1)); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY, diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index dfe269cbb0d9..1c9cd94b245d 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -292,6 +292,14 @@ public final class PhysicalChannelConfig implements Parcelable { } /** + * Return a copy of this PhysicalChannelConfig object but redact all the location info. + * @hide + */ + public PhysicalChannelConfig createLocationInfoSanitizedCopy() { + return new Builder(this).setPhysicalCellId(PHYSICAL_CELL_ID_UNKNOWN).build(); + } + + /** * @return String representation of the connection status * @hide */ @@ -540,6 +548,23 @@ public final class PhysicalChannelConfig implements Parcelable { mBand = BAND_UNKNOWN; } + /** + * Builder object constructed from existing PhysicalChannelConfig object. + * @hide + */ + public Builder(PhysicalChannelConfig config) { + mNetworkType = config.getNetworkType(); + mFrequencyRange = config.getFrequencyRange(); + mDownlinkChannelNumber = config.getDownlinkChannelNumber(); + mUplinkChannelNumber = config.getUplinkChannelNumber(); + mCellBandwidthDownlinkKhz = config.getCellBandwidthDownlinkKhz(); + mCellBandwidthUplinkKhz = config.getCellBandwidthUplinkKhz(); + mCellConnectionStatus = config.getConnectionStatus(); + mContextIds = Arrays.copyOf(config.getContextIds(), config.getContextIds().length); + mPhysicalCellId = config.getPhysicalCellId(); + mBand = config.getBand(); + } + public PhysicalChannelConfig build() { return new PhysicalChannelConfig(this); } diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index f110daecd952..2d06062cfa44 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -564,6 +564,7 @@ public class ServiceState implements Parcelable { * @hide */ @UnsupportedAppUsage + @TestApi public int getDataRegState() { return mDataRegState; } diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index cfb29f124b43..5a12865fb2a0 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -300,9 +300,12 @@ public class SmsMessage { * @param data Message data. * @param isCdma Indicates weather the type of the SMS is CDMA. * @return An SmsMessage representing the message. + * + * @hide */ + @SystemApi @Nullable - public static SmsMessage createSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) { + public static SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) { SmsMessageBase wrappedMessage; if (isCdma) { @@ -318,23 +321,6 @@ public class SmsMessage { } /** - * Create an SmsMessage from a native SMS-Submit PDU, specified by Bluetooth Message Access - * Profile Specification v1.4.2 5.8. - * This is used by Bluetooth MAP profile to decode message when sending non UTF-8 SMS messages. - * - * @param data Message data. - * @param isCdma Indicates weather the type of the SMS is CDMA. - * @return An SmsMessage representing the message. - * - * @hide - */ - @SystemApi - @Nullable - public static SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) { - return null; - } - - /** * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the * length in bytes (not hex chars) less the SMSC header * diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index bae032c7da6c..a2467f2067ac 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -99,6 +99,7 @@ import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.IBooleanConsumer; import com.android.internal.telephony.ICallForwardingInfoCallback; @@ -128,6 +129,7 @@ import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -417,6 +419,27 @@ public class TelephonyManager { } /** + * Post a runnable to the BackgroundThread. + * + * Used to invoke user callbacks without calling into the caller's executor from the caller's + * calling thread context, for example to provide asynchronous error information that is + * generated locally (not over a binder thread). + * + * <p>This is not necessary unless you are invoking caller's code asynchronously from within + * the caller's thread context. + * + * @param r a runnable. + */ + private static void runOnBackgroundThread(@NonNull Runnable r) { + try { + BackgroundThread.getExecutor().execute(r); + } catch (RejectedExecutionException e) { + throw new IllegalStateException( + "Failed to post a callback from the caller's thread context.", e); + } + } + + /** * Returns the multi SIM variant * Returns DSDS for Dual SIM Dual Standby * Returns DSDA for Dual SIM Dual Active @@ -5875,7 +5898,7 @@ public class TelephonyManager { /** * Error response to - * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}. + * {@link TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()}. * * Invoked when an error condition prevents updated {@link CellInfo} from being fetched * and returned from the modem. Callers of requestCellInfoUpdate() should override this @@ -5893,6 +5916,20 @@ public class TelephonyManager { }; /** + * Used for checking if the target SDK version for the current process is S or above. + * + * <p> Applies to the following methods: + * {@link #requestCellInfoUpdate}, + * {@link #setPreferredOpportunisticDataSubscription}, + * {@link #updateAvailableNetworks}, + * requestNumberVerification(), + * setSimPowerStateForSlot(), + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + private static final long NULL_TELEPHONY_THROW_NO_CB = 182185642L; + + /** * Requests all available cell information from the current subscription for observed * camped/registered, serving, and neighboring cells. * @@ -5912,7 +5949,14 @@ public class TelephonyManager { @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { try { ITelephony telephony = getITelephony(); - if (telephony == null) return; + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + telephony.requestCellInfoUpdate( getSubId(), new ICellInfoCallback.Stub() { @@ -5939,6 +5983,8 @@ public class TelephonyManager { } }, getOpPackageName(), getAttributionTag()); } catch (RemoteException ex) { + runOnBackgroundThread(() -> executor.execute( + () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex))); } } @@ -5966,7 +6012,14 @@ public class TelephonyManager { @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) { try { ITelephony telephony = getITelephony(); - if (telephony == null) return; + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } + } + telephony.requestCellInfoUpdateWithWorkSource( getSubId(), new ICellInfoCallback.Stub() { @@ -5994,6 +6047,8 @@ public class TelephonyManager { } }, getOpPackageName(), getAttributionTag(), workSource); } catch (RemoteException ex) { + runOnBackgroundThread(() -> executor.execute( + () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex))); } } @@ -6960,14 +7015,21 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); - if (telephony != null) { - telephony.requestNumberVerification(range, timeoutMillis, internalCallback, - getOpPackageName()); + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } } + + telephony.requestNumberVerification(range, timeoutMillis, internalCallback, + getOpPackageName()); } catch (RemoteException ex) { Rlog.e(TAG, "requestNumberVerification RemoteException", ex); - executor.execute(() -> - callback.onVerificationFailed(NumberVerificationCallback.REASON_UNSPECIFIED)); + runOnBackgroundThread(() -> executor.execute( + () -> callback.onVerificationFailed( + NumberVerificationCallback.REASON_UNSPECIFIED))); } } @@ -10333,6 +10395,8 @@ public class TelephonyManager { } try { ITelephony telephony = getITelephony(); + if (telephony == null) throw new IllegalStateException("Telephony is null."); + IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() { @Override public void accept(int result) { @@ -10340,11 +10404,18 @@ public class TelephonyManager { Binder.withCleanCallingIdentity(() -> callback.accept(result))); } }; - if (telephony != null) { - telephony.setSimPowerStateForSlotWithCallback(slotIndex, state, internalCallback); + if (telephony == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Telephony is null"); + } else { + return; + } } + telephony.setSimPowerStateForSlotWithCallback(slotIndex, state, internalCallback); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e); + runOnBackgroundThread(() -> executor.execute( + () -> callback.accept(SET_SIM_POWER_STATE_MODEM_ERROR))); } catch (SecurityException e) { Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", e); @@ -12774,22 +12845,12 @@ public class TelephonyManager { try { IOns iOpportunisticNetworkService = getIOns(); if (iOpportunisticNetworkService == null) { - if (executor == null || callback == null) { - return; - } - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { - callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); - } else { - callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); - } - }); - } finally { - Binder.restoreCallingIdentity(identity); + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Opportunistic Network Service is null"); + } else { + // Let the general remote exception handling catch this. + throw new RemoteException("Null Opportunistic Network Service!"); } - return; } ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { @Override @@ -12812,9 +12873,18 @@ public class TelephonyManager { .setPreferredDataSubscriptionId(subId, needValidation, callbackStub, pkgForDebug); } catch (RemoteException ex) { - Rlog.e(TAG, "setPreferredDataSubscriptionId RemoteException", ex); + Rlog.e(TAG, "setPreferredOpportunisticDataSubscription RemoteException", ex); + if (executor == null || callback == null) { + return; + } + runOnBackgroundThread(() -> executor.execute(() -> { + if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { + callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION); + } else { + callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION); + } + })); } - return; } /** @@ -12871,37 +12941,18 @@ public class TelephonyManager { @Nullable @CallbackExecutor Executor executor, @UpdateAvailableNetworksResult @Nullable Consumer<Integer> callback) { String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; + Objects.requireNonNull(availableNetworks, "availableNetworks must not be null."); try { IOns iOpportunisticNetworkService = getIOns(); - if (iOpportunisticNetworkService == null || availableNetworks == null) { - if (executor == null || callback == null) { - return; - } - if (iOpportunisticNetworkService == null) { - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { - callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION); - } else { - callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE); - } - }); - } finally { - Binder.restoreCallingIdentity(identity); - } + if (iOpportunisticNetworkService == null) { + if (Compatibility.isChangeEnabled(NULL_TELEPHONY_THROW_NO_CB)) { + throw new IllegalStateException("Opportunistic Network Service is null"); } else { - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - callback.accept(UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS); - }); - } finally { - Binder.restoreCallingIdentity(identity); - } + // Let the general remote exception handling catch this. + throw new RemoteException("Null Opportunistic Network Service!"); } - return; } + IUpdateAvailableNetworksCallback callbackStub = new IUpdateAvailableNetworksCallback.Stub() { @Override @@ -12909,20 +12960,25 @@ public class TelephonyManager { if (executor == null || callback == null) { return; } - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - callback.accept(result); - }); - } finally { - Binder.restoreCallingIdentity(identity); - } + Binder.withCleanCallingIdentity(() -> { + executor.execute(() -> callback.accept(result)); + }); } }; - iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks, callbackStub, - pkgForDebug); + iOpportunisticNetworkService + .updateAvailableNetworks(availableNetworks, callbackStub, pkgForDebug); } catch (RemoteException ex) { Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex); + if (executor == null || callback == null) { + return; + } + runOnBackgroundThread(() -> executor.execute(() -> { + if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) { + callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION); + } else { + callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE); + } + })); } } @@ -14570,7 +14626,12 @@ public class TelephonyManager { * DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING} can still be requested in * order to undo the mitigations above it (i.e {@link * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} and/or {@link - * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}). + * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}). </p> + * + * <p> In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of + * this API must also be listed in the device configuration as an authorized app in + * {@code packages/services/Telephony/res/values/config.xml} under the + * {@code thermal_mitigation_allowlisted_packages} key. </p> * * @param thermalMitigationRequest Thermal mitigation request. See {@link * ThermalMitigationRequest} for details. @@ -14589,7 +14650,8 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest); + return telephony.sendThermalMitigationRequest(getSubId(), thermalMitigationRequest, + getOpPackageName()); } throw new IllegalStateException("telephony service is null."); } catch (RemoteException ex) { diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java index 406c38bf60ef..9bc7a5c6cf96 100644 --- a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java +++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java @@ -53,7 +53,7 @@ public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessi * * @return the qci of the session */ - public int getQci() { + public int getQosIdentifier() { return mQci; } @@ -66,7 +66,7 @@ public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessi * * @return the guaranteed bit rate in kbps */ - public long getGuaranteedUplinkBitRate() { + public long getGuaranteedUplinkBitRateKbps() { return mGuaranteedUplinkBitRate; } @@ -79,7 +79,7 @@ public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessi * * @return the guaranteed bit rate in kbps */ - public long getGuaranteedDownlinkBitRate() { + public long getGuaranteedDownlinkBitRateKbps() { return mGuaranteedDownlinkBitRate; } @@ -92,7 +92,7 @@ public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessi * * @return the max uplink bit rate in kbps */ - public long getMaxUplinkBitRate() { + public long getMaxUplinkBitRateKbps() { return mMaxUplinkBitRate; } @@ -105,7 +105,7 @@ public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessi * * @return the max downlink bit rate in kbps */ - public long getMaxDownlinkBitRate() { + public long getMaxDownlinkBitRateKbps() { return mMaxDownlinkBitRate; } diff --git a/telephony/java/android/telephony/data/NrQos.java b/telephony/java/android/telephony/data/NrQos.java index 2011eed26977..fe124ac15393 100644 --- a/telephony/java/android/telephony/data/NrQos.java +++ b/telephony/java/android/telephony/data/NrQos.java @@ -50,6 +50,18 @@ public final class NrQos extends Qos implements Parcelable { return new NrQos(in); } + public int get5Qi() { + return fiveQi; + } + + public int getQfi() { + return qosFlowId; + } + + public int getAveragingWindow() { + return averagingWindowMs; + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(Qos.QOS_TYPE_NR, dest, flags); diff --git a/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl b/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl new file mode 100644 index 000000000000..fd3bbb0865cb --- /dev/null +++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.aidl @@ -0,0 +1,19 @@ +/* + * 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.telephony.data; + + parcelable NrQosSessionAttributes; diff --git a/telephony/java/android/telephony/data/NrQosSessionAttributes.java b/telephony/java/android/telephony/data/NrQosSessionAttributes.java new file mode 100644 index 000000000000..4c37687910a1 --- /dev/null +++ b/telephony/java/android/telephony/data/NrQosSessionAttributes.java @@ -0,0 +1,259 @@ +/* + * 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.telephony.data; + +import android.annotation.IntRange; +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.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Provides Qos attributes of an NR bearer. + * + * {@hide} + */ +@SystemApi +public final class NrQosSessionAttributes implements Parcelable, QosSessionAttributes { + private static final String TAG = NrQosSessionAttributes.class.getSimpleName(); + private final int m5Qi; + private final @IntRange(from=1, to=63) int mQfi; + private final long mMaxUplinkBitRate; + private final long mMaxDownlinkBitRate; + private final long mGuaranteedUplinkBitRate; + private final long mGuaranteedDownlinkBitRate; + private final long mAveragingWindow; + @NonNull private final List<InetSocketAddress> mRemoteAddresses; + + /** + * 5G QOS Identifier (5QI), see 3GPP TS 24.501 and 23.501. + * 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 5QI of the QOS flow + */ + public int getQosIdentifier() { + return m5Qi; + } + + /** + * QOS flow identifier of the QOS flow description in the + * range of 1 to 63. see 3GPP TS 24.501 and 23.501. + * + * @return the QOS flow identifier of the session + */ + public @IntRange(from=1, to=63) int getQosFlowIdentifier() { + return mQfi; + } + + /** + * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink. + * + * see 3GPP TS 24.501 section 6.2.5 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the guaranteed bit rate in kbps + */ + public long getGuaranteedUplinkBitRateKbps() { + return mGuaranteedUplinkBitRate; + } + + /** + * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink. + * + * see 3GPP TS 24.501 section 6.2.5 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the guaranteed bit rate in kbps + */ + public long getGuaranteedDownlinkBitRateKbps() { + return mGuaranteedDownlinkBitRate; + } + + /** + * The maximum uplink kbps that the network will accept. + * + * see 3GPP TS 24.501 section 6.2.5 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the max uplink bit rate in kbps + */ + public long getMaxUplinkBitRateKbps() { + return mMaxUplinkBitRate; + } + + /** + * The maximum downlink kbps that the network can provide. + * + * see 3GPP TS 24.501 section 6.2.5 + * + * Note: The Qos Session may be shared with OTHER applications besides yours. + * + * @return the max downlink bit rate in kbps + */ + public long getMaxDownlinkBitRateKbps() { + return mMaxDownlinkBitRate; + } + + /** + * The duration in milliseconds over which the maximum bit rates and guaranteed bit rates + * are calculated + * + * see 3GPP TS 24.501 section 6.2.5 + * + * @return the averaging window duration in milliseconds + */ + @NonNull + public Duration getBitRateWindowDuration() { + return Duration.ofMillis(mAveragingWindow); + } + + /** + * 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 fiveQi 5G quality class indicator + * @param qfi QOS flow identifier + * @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 averagingWindow the averaging window duration in milliseconds + * @param remoteAddresses the remote addresses that the uplink bit rates apply to + * + * @hide + */ + public NrQosSessionAttributes(final int fiveQi, final int qfi, + final long maxDownlinkBitRate, final long maxUplinkBitRate, + final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate, + final long averagingWindow, @NonNull final List<InetSocketAddress> remoteAddresses) { + Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null"); + m5Qi = fiveQi; + mQfi = qfi; + mMaxDownlinkBitRate = maxDownlinkBitRate; + mMaxUplinkBitRate = maxUplinkBitRate; + mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate; + mGuaranteedUplinkBitRate = guaranteedUplinkBitRate; + mAveragingWindow = averagingWindow; + + 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 NrQosSessionAttributes(@NonNull final Parcel in) { + m5Qi = in.readInt(); + mQfi = in.readInt(); + mMaxDownlinkBitRate = in.readLong(); + mMaxUplinkBitRate = in.readLong(); + mGuaranteedDownlinkBitRate = in.readLong(); + mGuaranteedUplinkBitRate = in.readLong(); + mAveragingWindow = 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 its filtered out the null values in the ..ctor + Log.e(TAG, "unable to unparcel remote address at index: " + i, e); + } + } + mRemoteAddresses = Collections.unmodifiableList(remoteAddresses); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeInt(m5Qi); + dest.writeInt(mQfi); + dest.writeLong(mMaxDownlinkBitRate); + dest.writeLong(mMaxUplinkBitRate); + dest.writeLong(mGuaranteedDownlinkBitRate); + dest.writeLong(mGuaranteedUplinkBitRate); + dest.writeLong(mAveragingWindow); + + 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<NrQosSessionAttributes> CREATOR = + new Creator<NrQosSessionAttributes>() { + @NonNull + @Override + public NrQosSessionAttributes createFromParcel(@NonNull final Parcel in) { + return new NrQosSessionAttributes(in); + } + + @NonNull + @Override + public NrQosSessionAttributes[] newArray(final int size) { + return new NrQosSessionAttributes[size]; + } + }; +} diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index 52d0f036788c..a133eadb3517 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -29,7 +29,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Contains the User Capability Exchange capabilities corresponding to a contact's URI. @@ -110,7 +112,6 @@ public final class RcsContactUceCapability implements Parcelable { /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through SIP OPTIONS. - * @hide */ public static final class OptionsBuilder { @@ -151,7 +152,7 @@ public final class RcsContactUceCapability implements Parcelable { * @param tags the list of the supported feature tags * @return this OptionBuilder */ - public @NonNull OptionsBuilder addFeatureTags(@NonNull List<String> tags) { + public @NonNull OptionsBuilder addFeatureTags(@NonNull Set<String> tags) { mCapabilities.mFeatureTags.addAll(tags); return this; } @@ -220,7 +221,7 @@ public final class RcsContactUceCapability implements Parcelable { private @CapabilityMechanism int mCapabilityMechanism; private @RequestResult int mRequestResult; - private final List<String> mFeatureTags = new ArrayList<>(); + private final Set<String> mFeatureTags = new HashSet<>(); private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>(); private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism, @@ -235,7 +236,9 @@ public final class RcsContactUceCapability implements Parcelable { mCapabilityMechanism = in.readInt(); mSourceType = in.readInt(); mRequestResult = in.readInt(); - in.readStringList(mFeatureTags); + List<String> featureTagList = new ArrayList<>(); + in.readStringList(featureTagList); + mFeatureTags.addAll(featureTagList); in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader()); } @@ -245,7 +248,7 @@ public final class RcsContactUceCapability implements Parcelable { out.writeInt(mCapabilityMechanism); out.writeInt(mSourceType); out.writeInt(mRequestResult); - out.writeStringList(mFeatureTags); + out.writeStringList(new ArrayList<>(mFeatureTags)); out.writeParcelableList(mPresenceTuples, flags); } @@ -285,7 +288,20 @@ public final class RcsContactUceCapability implements Parcelable { if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { return Collections.emptyList(); } - return Collections.unmodifiableList(mFeatureTags); + return Collections.unmodifiableList(new ArrayList<>(mFeatureTags)); + } + + /** + * @return The feature tags present in the OPTIONS response from the network. + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS} + */ + public @NonNull Set<String> getFeatureTags() { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { + return Collections.emptySet(); + } + return Collections.unmodifiableSet(mFeatureTags); } /** diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java index a217d1321342..c3d7325f2e0a 100644 --- a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java @@ -26,7 +26,8 @@ import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.stub.CapabilityExchangeEventListener; import android.util.Log; -import java.util.List; +import java.util.ArrayList; +import java.util.Set; /** * The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by @@ -84,7 +85,7 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis * request to the framework. */ public void onRemoteCapabilityRequest(@NonNull Uri contactUri, - @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) + @NonNull Set<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) throws ImsException { ICapabilityExchangeEventListener listener = mListenerBinder; if (listener == null) { @@ -114,7 +115,8 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis }; try { - listener.onRemoteCapabilityRequest(contactUri, remoteCapabilities, internalCallback); + listener.onRemoteCapabilityRequest(contactUri, new ArrayList<>(remoteCapabilities), + internalCallback); } catch (RemoteException e) { Log.w(LOG_TAG, "Remote capability request exception: " + e); throw new ImsException("Remote is not available", diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 85703f8de5e5..b384e50d9f03 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -47,6 +47,7 @@ import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.HashSet; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -145,8 +146,8 @@ public class RcsFeature extends ImsFeature { throws RemoteException { OptionsResponseCallback callbackWrapper = new RcsOptionsResponseAidlWrapper(callback); executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal() - .sendOptionsCapabilityRequest(contactUri, myCapabilities, callbackWrapper), - "sendOptionsCapabilityRequest"); + .sendOptionsCapabilityRequest(contactUri, new HashSet<>(myCapabilities), + callbackWrapper), "sendOptionsCapabilityRequest"); } // Call the methods with a clean calling identity on the executor and wait indefinitely for @@ -390,6 +391,7 @@ public class RcsFeature extends ImsFeature { * 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( @NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) { @@ -398,14 +400,45 @@ public class RcsFeature extends ImsFeature { } /** + * Retrieve the implementation of UCE for this {@link RcsFeature}, which can use either + * presence or OPTIONS for capability exchange. + * + * Will only be requested by the framework if capability exchange is configured + * as capable during a + * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} + * operation and the RcsFeature sets the status of the capability to true using + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. + * + * @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. + */ + public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl( + @NonNull CapabilityExchangeEventListener listener) { + // Base Implementation, override to implement functionality + return new RcsCapabilityExchangeImplBase(); + } + + /** * Remove the given CapabilityExchangeImplBase instance. * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be removed. + * @hide */ public void removeCapabilityExchangeImpl( @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) { // Override to implement the process of removing RcsCapabilityExchangeImplBase instance. } + /** + * Remove the given CapabilityExchangeImplBase instance. + * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be destroyed. + */ + public void destroyCapabilityExchangeImpl( + @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) { + // Override to implement the process of destroying RcsCapabilityExchangeImplBase instance. + } + /**{@inheritDoc}*/ @Override public void onFeatureRemoved() { diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java index 62955487897f..a3be8dab2891 100644 --- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java +++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java @@ -26,7 +26,7 @@ import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.RcsFeature; -import java.util.List; +import java.util.Set; /** * The interface that is used by the framework to listen to events from the vendor RCS stack @@ -98,7 +98,8 @@ public interface CapabilityExchangeEventListener { * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError}. * @param contactUri The URI associated with the remote contact that is * requesting capabilities. - * @param remoteCapabilities The remote contact's capability information. + * @param remoteCapabilities The remote contact's capability information. The capability + * information is in the format defined in RCC.07 section 2.6.1.3. * @param callback The callback of this request which is sent from the remote user. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not * currently connected to the framework. This can happen if the {@link RcsFeature} is not @@ -107,6 +108,6 @@ public interface CapabilityExchangeEventListener { * cases when the Telephony stack has crashed. */ void onRemoteCapabilityRequest(@NonNull Uri contactUri, - @NonNull List<String> remoteCapabilities, + @NonNull Set<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) throws ImsException; } diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index 03e17fbc2c0d..a117adcfb99a 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -33,6 +33,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collection; import java.util.List; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -355,12 +356,13 @@ public class RcsCapabilityExchangeImplBase { void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException; } - private final Executor mBinderExecutor; + private Executor mBinderExecutor; /** * Create a new RcsCapabilityExchangeImplBase instance. * * @param executor The executor that remote calls from the framework will be called on. + * @hide */ public RcsCapabilityExchangeImplBase(@NonNull Executor executor) { if (executor == null) { @@ -370,6 +372,12 @@ public class RcsCapabilityExchangeImplBase { } /** + * Create a new RcsCapabilityExchangeImplBase instance. + */ + public RcsCapabilityExchangeImplBase() { + } + + /** * The user capabilities of one or multiple contacts have been requested by the framework. * <p> * The implementer must follow up this call with an @@ -433,6 +441,7 @@ 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") @@ -446,4 +455,27 @@ public class RcsCapabilityExchangeImplBase { // Do not do anything, this is a stub implementation. } } + + /** + * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism + * in order to receive the capabilities of the remote user in response. + * <p> + * The implementer must use {@link OptionsResponseCallback} to send the response of + * this query from the network back to the framework. + * @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. + */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") + public void sendOptionsCapabilityRequest(@NonNull Uri contactUri, + @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation."); + try { + callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED); + } catch (ImsException e) { + // Do not do anything, this is a stub implementation. + } + } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 45702c311ff3..97078c3dd76e 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2231,10 +2231,12 @@ interface ITelephony { * * @param subId the id of the subscription * @param thermalMitigationRequest holds the parameters necessary for the request. + * @param callingPackage the package name of the calling package. * @throws InvalidThermalMitigationRequestException if the parametes are invalid. */ int sendThermalMitigationRequest(int subId, - in ThermalMitigationRequest thermalMitigationRequest); + in ThermalMitigationRequest thermalMitigationRequest, + String callingPackage); /** * Get the Generic Bootstrapping Architecture authentication keys @@ -2325,6 +2327,16 @@ interface ITelephony { boolean getCarrierSingleRegistrationEnabled(int subId); /** + * Overrides the ims feature validation result + */ + boolean setImsFeatureValidationOverride(int subId, String enabled); + + /** + * Gets the ims feature validation override value + */ + boolean getImsFeatureValidationOverride(int subId); + + /** * Return the mobile provisioning url that is used to launch a browser to allow users to manage * their mobile plan. */ diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt index a4d8353d1253..1e54093bf111 100644 --- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt +++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt @@ -19,6 +19,7 @@ package android.net import android.os.Build import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 +import com.android.modules.utils.build.SdkLevel.isAtLeastS import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.assertParcelSane @@ -44,7 +45,13 @@ class NetworkAgentConfigTest { setPartialConnectivityAcceptable(false) setUnvalidatedConnectivityAcceptable(true) }.build() - assertParcelSane(config, 10) + if (isAtLeastS()) { + // From S, the config will have 12 items + assertParcelSane(config, 12) + } else { + // For R or below, the config will have 10 items + assertParcelSane(config, 10) + } } @Test @IgnoreUpTo(Build.VERSION_CODES.Q) diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index d40b88ca599f..1f50e31cf243 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -329,7 +329,8 @@ public class NetworkCapabilitiesTest { if (isAtLeastS()) { netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)); netCap.setUids(uids); - } else if (isAtLeastR()) { + } + if (isAtLeastR()) { netCap.setOwnerUid(123); netCap.setAdministratorUids(new int[] {5, 11}); } @@ -531,11 +532,22 @@ public class NetworkCapabilitiesTest { assertFalse(nc1.equalsNetCapabilities(nc2)); nc2.addUnwantedCapability(NET_CAPABILITY_INTERNET); assertTrue(nc1.equalsNetCapabilities(nc2)); - - nc1.removeCapability(NET_CAPABILITY_INTERNET); - assertFalse(nc1.equalsNetCapabilities(nc2)); - nc2.removeCapability(NET_CAPABILITY_INTERNET); - assertTrue(nc1.equalsNetCapabilities(nc2)); + if (isAtLeastS()) { + // Remove a required capability doesn't affect unwanted capabilities. + // This is a behaviour change from S. + nc1.removeCapability(NET_CAPABILITY_INTERNET); + assertTrue(nc1.equalsNetCapabilities(nc2)); + + nc1.removeUnwantedCapability(NET_CAPABILITY_INTERNET); + assertFalse(nc1.equalsNetCapabilities(nc2)); + nc2.removeUnwantedCapability(NET_CAPABILITY_INTERNET); + assertTrue(nc1.equalsNetCapabilities(nc2)); + } else { + nc1.removeCapability(NET_CAPABILITY_INTERNET); + assertFalse(nc1.equalsNetCapabilities(nc2)); + nc2.removeCapability(NET_CAPABILITY_INTERNET); + assertTrue(nc1.equalsNetCapabilities(nc2)); + } } @Test @@ -596,11 +608,21 @@ public class NetworkCapabilitiesTest { // This will effectively move NOT_ROAMING capability from required to unwanted for nc1. nc1.addUnwantedCapability(NET_CAPABILITY_NOT_ROAMING); - nc2.combineCapabilities(nc1); - // We will get this capability in both requested and unwanted lists thus this request - // will never be satisfied. - assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING)); + if (isAtLeastS()) { + // From S, it is not allowed to have the same capability in both wanted and + // unwanted list. + assertThrows(IllegalArgumentException.class, () -> nc2.combineCapabilities(nc1)); + // Remove unwanted capability to continue other tests. + nc1.removeUnwantedCapability(NET_CAPABILITY_NOT_ROAMING); + } else { + nc2.combineCapabilities(nc1); + // We will get this capability in both requested and unwanted lists thus this request + // will never be satisfied. + assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING)); + // For R or below, remove unwanted capability via removeCapability. + nc1.removeCapability(NET_CAPABILITY_NOT_ROAMING); + } nc1.setSSID(TEST_SSID); nc2.combineCapabilities(nc1); 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 db49e0b0047e..b6e42743e2a3 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 @@ -73,7 +73,7 @@ import kotlin.test.assertTrue import kotlin.test.fail const val SERVICE_BIND_TIMEOUT_MS = 5_000L -const val TEST_TIMEOUT_MS = 1_000L +const val TEST_TIMEOUT_MS = 10_000L /** * Test that exercises an instrumented version of ConnectivityService against an instrumented diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index 6fc605e269fe..6cbdd258c00a 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -41,10 +41,10 @@ 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.anyBoolean; 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; @@ -64,6 +64,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.os.Process; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -219,8 +220,8 @@ public class ConnectivityManagerTest { ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - anyInt(), any(), nullable(String.class))).thenReturn(request); + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(request); manager.requestNetwork(request, callback, handler); // callback triggers @@ -247,8 +248,8 @@ public class ConnectivityManagerTest { ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - anyInt(), any(), nullable(String.class))).thenReturn(req1); + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req1); manager.requestNetwork(req1, callback, handler); // callback triggers @@ -265,8 +266,8 @@ public class ConnectivityManagerTest { verify(callback, timeout(100).times(0)).onLosing(any(), anyInt()); // callback can be registered again - when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - anyInt(), any(), nullable(String.class))).thenReturn(req2); + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req2); manager.requestNetwork(req2, callback, handler); // callback triggers @@ -289,8 +290,8 @@ public class ConnectivityManagerTest { info.targetSdkVersion = VERSION_CODES.N_MR1 + 1; when(mCtx.getApplicationInfo()).thenReturn(info); - when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(), - any(), nullable(String.class))).thenReturn(request); + when(mService.requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(), + anyInt(), any(), nullable(String.class))).thenReturn(request); Handler handler = new Handler(Looper.getMainLooper()); manager.requestNetwork(request, callback, handler); @@ -357,34 +358,40 @@ public class ConnectivityManagerTest { final NetworkCallback callback = new ConnectivityManager.NetworkCallback(); manager.requestNetwork(request, callback); - verify(mService).requestNetwork(eq(request.networkCapabilities), + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities), eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); 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(), - anyInt(), any(), any()); + verify(mService, never()).requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), + anyInt(), anyInt(), any(), any()); verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); + Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + manager.registerDefaultNetworkCallback(callback); - verify(mService).requestNetwork(eq(null), + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null), eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); - Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + manager.registerDefaultNetworkCallbackAsUid(42, callback, handler); + verify(mService).requestNetwork(eq(42), eq(null), + eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), + eq(testPkgName), eq(testAttributionTag)); + manager.requestBackgroundNetwork(request, handler, callback); - verify(mService).requestNetwork(eq(request.networkCapabilities), + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities), eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); manager.registerSystemDefaultNetworkCallback(callback, handler); - verify(mService).requestNetwork(eq(null), + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null), eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index ed9a44b614d5..14c72f1445a9 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -18,6 +18,7 @@ package com.android.server; import static android.Manifest.permission.CHANGE_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; +import static android.Manifest.permission.NETWORK_SETTINGS; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; @@ -30,10 +31,14 @@ import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; +import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE; -import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; @@ -91,10 +96,6 @@ import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; 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.NetworkPolicyManager.BLOCKED_METERED_REASON_DATA_SAVER; -import static android.net.NetworkPolicyManager.BLOCKED_METERED_REASON_USER_RESTRICTED; -import static android.net.NetworkPolicyManager.BLOCKED_REASON_BATTERY_SAVER; -import static android.net.NetworkPolicyManager.BLOCKED_REASON_NONE; import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; @@ -266,6 +267,7 @@ import android.security.Credentials; import android.system.Os; import android.telephony.TelephonyManager; import android.telephony.data.EpsBearerQosSessionAttributes; +import android.telephony.data.NrQosSessionAttributes; import android.test.mock.MockContentResolver; import android.text.TextUtils; import android.util.ArraySet; @@ -715,6 +717,9 @@ public class ConnectivityServiceTest { private int mProbesSucceeded; private String mNmValidationRedirectUrl = null; private boolean mNmProvNotificationRequested = false; + private Runnable mCreatedCallback; + private Runnable mUnwantedCallback; + private Runnable mDisconnectedCallback; private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); // Contains the redirectUrl from networkStatus(). Before reading, wait for @@ -769,6 +774,24 @@ public class ConnectivityServiceTest { mRedirectUrl = redirectUrl; mNetworkStatusReceived.open(); } + + @Override + public void onNetworkCreated() { + super.onNetworkCreated(); + if (mCreatedCallback != null) mCreatedCallback.run(); + } + + @Override + public void onNetworkUnwanted() { + super.onNetworkUnwanted(); + if (mUnwantedCallback != null) mUnwantedCallback.run(); + } + + @Override + public void onNetworkDestroyed() { + super.onNetworkDestroyed(); + if (mDisconnectedCallback != null) mDisconnectedCallback.run(); + } }; assertEquals(na.getNetwork().netId, nmNetworkCaptor.getValue().netId); @@ -970,6 +993,18 @@ public class ConnectivityServiceTest { p.timestampMillis = DATA_STALL_TIMESTAMP; mNmCallbacks.notifyDataStallSuspected(p); } + + public void setCreatedCallback(Runnable r) { + mCreatedCallback = r; + } + + public void setUnwantedCallback(Runnable r) { + mUnwantedCallback = r; + } + + public void setDisconnectedCallback(Runnable r) { + mDisconnectedCallback = r; + } } /** @@ -1178,11 +1213,6 @@ public class ConnectivityServiceTest { } @Override - public int getNetId() { - return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId; - } - - @Override public int getActiveVpnType() { return mVpnType; } @@ -1206,10 +1236,10 @@ public class ConnectivityServiceTest { mNetworkCapabilities); mMockNetworkAgent.waitForIdle(TIMEOUT_MS); - verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()), + verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetwork().getNetId()), eq(toUidRangeStableParcels(uids))); verify(mMockNetd, never()) - .networkRemoveUidRanges(eq(mMockVpn.getNetId()), any()); + .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), any()); mAgentRegistered = true; updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent"); mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); @@ -1378,10 +1408,21 @@ public class ConnectivityServiceTest { } private void mockUidNetworkingBlocked() { - doAnswer(i -> NetworkPolicyManager.isUidBlocked(mBlockedReasons, i.getArgument(1)) + doAnswer(i -> isUidBlocked(mBlockedReasons, i.getArgument(1)) ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean()); } + private boolean isUidBlocked(int blockedReasons, boolean meteredNetwork) { + final int blockedOnAllNetworksReason = (blockedReasons & ~BLOCKED_METERED_REASON_MASK); + if (blockedOnAllNetworksReason != BLOCKED_REASON_NONE) { + return true; + } + if (meteredNetwork) { + return blockedReasons != BLOCKED_REASON_NONE; + } + return false; + } + private void setBlockedReasonChanged(int blockedReasons) { mBlockedReasons = blockedReasons; mPolicyCallback.onUidBlockedReasonChanged(Process.myUid(), blockedReasons); @@ -1451,6 +1492,23 @@ public class ConnectivityServiceTest { }); } + private interface ExceptionalRunnable { + void run() throws Exception; + } + + private void withPermission(String permission, ExceptionalRunnable r) throws Exception { + if (mServiceContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { + r.run(); + return; + } + try { + mServiceContext.setPermission(permission, PERMISSION_GRANTED); + r.run(); + } finally { + mServiceContext.setPermission(permission, PERMISSION_DENIED); + } + } + private static final int PRIMARY_USER = 0; private static final UidRange PRIMARY_UIDRANGE = UidRange.createForUser(UserHandle.of(PRIMARY_USER)); @@ -1559,7 +1617,7 @@ public class ConnectivityServiceTest { doReturn(mNetworkStack).when(deps).getNetworkStack(); doReturn(mSystemProperties).when(deps).getSystemProperties(); doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); - doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt()); + doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any()); doAnswer(inv -> { mPolicyTracker = new WrappedMultinetworkPolicyTracker( inv.getArgument(0), inv.getArgument(1), inv.getArgument(2)); @@ -2426,8 +2484,7 @@ public class ConnectivityServiceTest { public void networkCallbacksSanitizationTest_Sanitize() throws Exception { mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED); - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, - PERMISSION_DENIED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); doNetworkCallbacksSanitizationTest(true /* sanitized */); } @@ -2435,7 +2492,7 @@ public class ConnectivityServiceTest { public void networkCallbacksSanitizationTest_NoSanitize_NetworkStack() throws Exception { mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED); - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); doNetworkCallbacksSanitizationTest(false /* sanitized */); } @@ -2443,7 +2500,7 @@ public class ConnectivityServiceTest { public void networkCallbacksSanitizationTest_NoSanitize_Settings() throws Exception { mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED); - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); doNetworkCallbacksSanitizationTest(false /* sanitized */); } @@ -2787,6 +2844,94 @@ public class ConnectivityServiceTest { } @Test + public void testNetworkAgentCallbacks() throws Exception { + // Keeps track of the order of events that happen in this test. + final LinkedBlockingQueue<String> eventOrder = new LinkedBlockingQueue<>(); + + final NetworkRequest request = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final TestNetworkCallback callback = new TestNetworkCallback(); + final AtomicReference<Network> wifiNetwork = new AtomicReference<>(); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + // Expectations for state when various callbacks fire. These expectations run on the handler + // thread and not on the test thread because they need to prevent the handler thread from + // advancing while they examine state. + + // 1. When onCreated fires, netd has been told to create the network. + mWiFiNetworkAgent.setCreatedCallback(() -> { + eventOrder.offer("onNetworkCreated"); + wifiNetwork.set(mWiFiNetworkAgent.getNetwork()); + assertNotNull(wifiNetwork.get()); + try { + verify(mMockNetd).networkCreatePhysical(wifiNetwork.get().getNetId(), + INetd.PERMISSION_NONE); + } catch (RemoteException impossible) { + fail(); + } + }); + + // 2. onNetworkUnwanted isn't precisely ordered with respect to any particular events. Just + // check that it is fired at some point after disconnect. + mWiFiNetworkAgent.setUnwantedCallback(() -> eventOrder.offer("onNetworkUnwanted")); + + // 3. While the teardown timer is running, connectivity APIs report the network is gone, but + // netd has not yet been told to destroy it. + final Runnable duringTeardown = () -> { + eventOrder.offer("timePasses"); + assertNull(mCm.getLinkProperties(wifiNetwork.get())); + try { + verify(mMockNetd, never()).networkDestroy(wifiNetwork.get().getNetId()); + } catch (RemoteException impossible) { + fail(); + } + }; + + // 4. After onNetworkDisconnected is called, connectivity APIs report the network is gone, + // and netd has been told to destroy it. + mWiFiNetworkAgent.setDisconnectedCallback(() -> { + eventOrder.offer("onNetworkDisconnected"); + assertNull(mCm.getLinkProperties(wifiNetwork.get())); + try { + verify(mMockNetd).networkDestroy(wifiNetwork.get().getNetId()); + } catch (RemoteException impossible) { + fail(); + } + }); + + // Connect a network, and file a request for it after it has come up, to ensure the nascent + // timer is cleared and the test does not have to wait for it. Filing the request after the + // network has come up is necessary because ConnectivityService does not appear to clear the + // nascent timer if the first request satisfied by the network was filed before the network + // connected. + // TODO: fix this bug, file the request before connecting, and remove the waitForIdle. + mWiFiNetworkAgent.connectWithoutInternet(); + waitForIdle(); + mCm.requestNetwork(request, callback); + callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + + // Set teardown delay and make sure CS has processed it. + mWiFiNetworkAgent.getNetworkAgent().setTeardownDelayMs(300); + waitForIdle(); + + // Post the duringTeardown lambda to the handler so it fires while teardown is in progress. + // The delay must be long enough it will run after the unregisterNetworkCallback has torn + // down the network and started the teardown timer, and short enough that the lambda is + // scheduled to run before the teardown timer. + final Handler h = new Handler(mCsHandlerThread.getLooper()); + h.postDelayed(duringTeardown, 150); + + // Disconnect the network and check that events happened in the right order. + mCm.unregisterNetworkCallback(callback); + assertEquals("onNetworkCreated", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertEquals("onNetworkUnwanted", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertEquals("timePasses", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertEquals("onNetworkDisconnected", eventOrder.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + + mCm.unregisterNetworkCallback(callback); + } + + @Test public void testExplicitlySelected() throws Exception { NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET) @@ -3427,8 +3572,7 @@ public class ConnectivityServiceTest { @Test public void testCaptivePortalApi() throws Exception { - mServiceContext.setPermission( - android.Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() @@ -3462,8 +3606,7 @@ public class ConnectivityServiceTest { 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); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() @@ -3813,8 +3956,9 @@ public class ConnectivityServiceTest { NetworkCapabilities networkCapabilities = new NetworkCapabilities(); networkCapabilities.addTransportType(TRANSPORT_WIFI) .setNetworkSpecifier(new MatchAllNetworkSpecifier()); - mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(), - null, 0, null, ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE, + mService.requestNetwork(Process.INVALID_UID, networkCapabilities, + NetworkRequest.Type.REQUEST.ordinal(), null, 0, null, + ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE, mContext.getPackageName(), getAttributionTag()); }); @@ -3896,8 +4040,7 @@ public class ConnectivityServiceTest { @Test public void testRegisterDefaultNetworkCallback() throws Exception { // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, - PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultNetworkCallback); @@ -4043,7 +4186,7 @@ public class ConnectivityServiceTest { } @Test - public void testRegisterSystemDefaultCallbackRequiresNetworkSettings() throws Exception { + public void testRegisterPrivilegedDefaultCallbacksRequireNetworkSettings() throws Exception { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false /* validated */); @@ -4052,12 +4195,18 @@ public class ConnectivityServiceTest { assertThrows(SecurityException.class, () -> mCm.registerSystemDefaultNetworkCallback(callback, handler)); callback.assertNoCallback(); + assertThrows(SecurityException.class, + () -> mCm.registerDefaultNetworkCallbackAsUid(APP1_UID, callback, handler)); + callback.assertNoCallback(); - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, - PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); mCm.registerSystemDefaultNetworkCallback(callback, handler); callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); mCm.unregisterNetworkCallback(callback); + + mCm.registerDefaultNetworkCallbackAsUid(APP1_UID, callback, handler); + callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + mCm.unregisterNetworkCallback(callback); } private void setCaptivePortalMode(int mode) { @@ -5371,10 +5520,11 @@ public class ConnectivityServiceTest { } @Test - public void testNetworkCallbackMaximum() { + public void testNetworkCallbackMaximum() throws Exception { final int MAX_REQUESTS = 100; final int CALLBACKS = 89; final int INTENTS = 11; + final int SYSTEM_ONLY_MAX_REQUESTS = 250; assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS); NetworkRequest networkRequest = new NetworkRequest.Builder().build(); @@ -5423,6 +5573,33 @@ public class ConnectivityServiceTest { new Intent("d"), FLAG_IMMUTABLE)) ); + // The system gets another SYSTEM_ONLY_MAX_REQUESTS slots. + final Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { + ArrayList<NetworkCallback> systemRegistered = new ArrayList<>(); + for (int i = 0; i < SYSTEM_ONLY_MAX_REQUESTS - 1; i++) { + NetworkCallback cb = new NetworkCallback(); + if (i % 2 == 0) { + mCm.registerDefaultNetworkCallbackAsUid(1000000 + i, cb, handler); + } else { + mCm.registerNetworkCallback(networkRequest, cb); + } + systemRegistered.add(cb); + } + waitForIdle(); + + assertThrows(TooManyRequestsException.class, () -> + mCm.registerDefaultNetworkCallbackAsUid(1001042, new NetworkCallback(), + handler)); + assertThrows(TooManyRequestsException.class, () -> + mCm.registerNetworkCallback(networkRequest, new NetworkCallback())); + + for (NetworkCallback callback : systemRegistered) { + mCm.unregisterNetworkCallback(callback); + } + waitForIdle(); // Wait for requests to be unregistered before giving up the permission. + }); + for (Object o : registered) { if (o instanceof NetworkCallback) { mCm.unregisterNetworkCallback((NetworkCallback)o); @@ -5449,6 +5626,30 @@ public class ConnectivityServiceTest { waitForIdle(); for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerDefaultNetworkCallback(networkCallback); + mCm.unregisterNetworkCallback(networkCallback); + } + waitForIdle(); + + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerDefaultNetworkCallback(networkCallback); + mCm.unregisterNetworkCallback(networkCallback); + } + waitForIdle(); + + withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> { + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerDefaultNetworkCallbackAsUid(1000000 + i, networkCallback, + new Handler(ConnectivityThread.getInstanceLooper())); + mCm.unregisterNetworkCallback(networkCallback); + } + }); + waitForIdle(); + + for (int i = 0; i < MAX_REQUESTS; i++) { final PendingIntent pendingIntent = PendingIntent.getBroadcast( mContext, 0 /* requestCode */, new Intent("e" + i), FLAG_IMMUTABLE); mCm.requestNetwork(networkRequest, pendingIntent); @@ -6441,8 +6642,7 @@ public class ConnectivityServiceTest { @Test public void testVpnNetworkActive() throws Exception { // NETWORK_SETTINGS is necessary to call registerSystemDefaultNetworkCallback. - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, - PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final int uid = Process.myUid(); @@ -6934,8 +7134,7 @@ public class ConnectivityServiceTest { @Test public void testRestrictedProfileAffectsVpnUidRanges() throws Exception { // NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities. - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, - PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final NetworkRequest request = new NetworkRequest.Builder() .removeCapability(NET_CAPABILITY_NOT_VPN) @@ -7021,8 +7220,7 @@ public class ConnectivityServiceTest { mServiceContext.setPermission( Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); // Necessary to see the UID ranges in NetworkCapabilities. - mServiceContext.setPermission( - Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final NetworkRequest request = new NetworkRequest.Builder() .removeCapability(NET_CAPABILITY_NOT_VPN) @@ -7257,6 +7455,20 @@ public class ConnectivityServiceTest { mMockVpn.disconnect(); } + private class DetailedBlockedStatusCallback extends TestNetworkCallback { + public void expectAvailableThenValidatedCallbacks(HasNetwork n, int blockedStatus) { + super.expectAvailableThenValidatedCallbacks(n.getNetwork(), blockedStatus, TIMEOUT_MS); + } + public void expectBlockedStatusCallback(HasNetwork n, int blockedStatus) { + // This doesn't work: + // super.expectBlockedStatusCallback(blockedStatus, n.getNetwork()); + super.expectBlockedStatusCallback(blockedStatus, n.getNetwork(), TIMEOUT_MS); + } + public void onBlockedStatusChanged(Network network, int blockedReasons) { + getHistory().add(new CallbackEntry.BlockedStatusInt(network, blockedReasons)); + } + } + @Test public void testNetworkBlockedStatus() throws Exception { final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); @@ -7264,11 +7476,16 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_CELLULAR) .build(); mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); + final DetailedBlockedStatusCallback detailedCallback = new DetailedBlockedStatusCallback(); + mCm.registerNetworkCallback(cellRequest, detailedCallback); + mockUidNetworkingBlocked(); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(true); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + detailedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent, + BLOCKED_REASON_NONE); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); @@ -7276,17 +7493,23 @@ public class ConnectivityServiceTest { setBlockedReasonChanged(BLOCKED_REASON_BATTERY_SAVER); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_REASON_BATTERY_SAVER); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertExtraInfoFromCmBlocked(mCellNetworkAgent); - // ConnectivityService should cache it not to invoke the callback again. + // If blocked state does not change but blocked reason does, the boolean callback is called. + // TODO: investigate de-duplicating. setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED); - cellNetworkCallback.assertNoCallback(); + cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_METERED_REASON_USER_RESTRICTED); setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); @@ -7294,6 +7517,8 @@ public class ConnectivityServiceTest { setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_METERED_REASON_DATA_SAVER); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); @@ -7303,6 +7528,8 @@ public class ConnectivityServiceTest { mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + detailedCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); @@ -7312,6 +7539,10 @@ public class ConnectivityServiceTest { cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, + mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_METERED_REASON_DATA_SAVER); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); @@ -7319,6 +7550,7 @@ public class ConnectivityServiceTest { setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); @@ -7326,10 +7558,13 @@ public class ConnectivityServiceTest { setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.assertNoCallback(); + detailedCallback.assertNoCallback(); // Restrict background data. Networking is not blocked because the network is unmetered. setBlockedReasonChanged(BLOCKED_METERED_REASON_DATA_SAVER); cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, + BLOCKED_METERED_REASON_DATA_SAVER); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED); @@ -7339,12 +7574,14 @@ public class ConnectivityServiceTest { setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent); + detailedCallback.expectBlockedStatusCallback(mCellNetworkAgent, BLOCKED_REASON_NONE); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertExtraInfoFromCmPresent(mCellNetworkAgent); setBlockedReasonChanged(BLOCKED_REASON_NONE); cellNetworkCallback.assertNoCallback(); + detailedCallback.assertNoCallback(); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED); @@ -7471,8 +7708,7 @@ public class ConnectivityServiceTest { Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED); mServiceContext.setPermission( Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); - mServiceContext.setPermission( - Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final TestNetworkCallback callback = new TestNetworkCallback(); final NetworkRequest request = new NetworkRequest.Builder() @@ -7490,6 +7726,10 @@ public class ConnectivityServiceTest { final TestNetworkCallback vpnUidDefaultCallback = new TestNetworkCallback(); registerDefaultNetworkCallbackAsUid(vpnUidDefaultCallback, VPN_UID); + final TestNetworkCallback vpnDefaultCallbackAsUid = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallbackAsUid(VPN_UID, vpnDefaultCallbackAsUid, + new Handler(ConnectivityThread.getInstanceLooper())); + final int uid = Process.myUid(); final int userId = UserHandle.getUserId(uid); final ArrayList<String> allowList = new ArrayList<>(); @@ -7509,6 +7749,7 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); vpnUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); vpnUidDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + vpnDefaultCallbackAsUid.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); @@ -7522,6 +7763,7 @@ public class ConnectivityServiceTest { defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); @@ -7537,6 +7779,7 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); // The following requires that the UID of this test package is greater than VPN_UID. This // is always true in practice because a plain AOSP build with no apps installed has almost @@ -7558,6 +7801,7 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnUidCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7579,6 +7823,7 @@ public class ConnectivityServiceTest { assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); @@ -7591,6 +7836,7 @@ public class ConnectivityServiceTest { assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7606,6 +7852,7 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7618,6 +7865,7 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7631,6 +7879,7 @@ public class ConnectivityServiceTest { assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); @@ -7642,6 +7891,7 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability. vpnUidDefaultCallback.assertNoCallback(); // VPN does not apply to VPN_UID + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7654,12 +7904,14 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertNull(mCm.getActiveNetwork()); mCm.unregisterNetworkCallback(callback); mCm.unregisterNetworkCallback(defaultCallback); mCm.unregisterNetworkCallback(vpnUidCallback); mCm.unregisterNetworkCallback(vpnUidDefaultCallback); + mCm.unregisterNetworkCallback(vpnDefaultCallbackAsUid); } private void setupLegacyLockdownVpn() { @@ -7692,8 +7944,7 @@ public class ConnectivityServiceTest { mServiceContext.setPermission( Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED); // For LockdownVpnTracker to call registerSystemDefaultNetworkCallback. - mServiceContext.setPermission( - Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); final TestNetworkCallback callback = new TestNetworkCallback(); @@ -8823,8 +9074,7 @@ public class ConnectivityServiceTest { private void denyAllLocationPrivilegedPermissions() { mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED); - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, - PERMISSION_DENIED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); mServiceContext.setPermission(Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); mServiceContext.setPermission(Manifest.permission.NETWORK_SETUP_WIZARD, @@ -9080,7 +9330,7 @@ public class ConnectivityServiceTest { @Test public void testCreateForCallerWithLocalMacAddressSanitizedWithSettingsPermission() throws Exception { - mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); final TransportInfo transportInfo = mock(TransportInfo.class); when(transportInfo.getApplicableRedactions()) @@ -9761,10 +10011,12 @@ public class ConnectivityServiceTest { assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); if (add) { - inOrder.verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()), + inOrder.verify(mMockNetd, times(1)) + .networkAddUidRanges(eq(mMockVpn.getNetwork().getNetId()), eq(toUidRangeStableParcels(vpnRanges))); } else { - inOrder.verify(mMockNetd, times(1)).networkRemoveUidRanges(eq(mMockVpn.getNetId()), + inOrder.verify(mMockNetd, times(1)) + .networkRemoveUidRanges(eq(mMockVpn.getNetwork().getNetId()), eq(toUidRangeStableParcels(vpnRanges))); } @@ -9805,8 +10057,8 @@ public class ConnectivityServiceTest { for (int reqTypeInt : invalidReqTypeInts) { assertThrows("Expect throws for invalid request type " + reqTypeInt, IllegalArgumentException.class, - () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null, - ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE, + () -> mService.requestNetwork(Process.INVALID_UID, nc, reqTypeInt, null, 0, + null, ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE, mContext.getPackageName(), getAttributionTag()) ); } @@ -9925,7 +10177,7 @@ public class ConnectivityServiceTest { && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes)); mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() - .sendQosSessionLost(qosCallbackId, sessionId); + .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_EPS_BEARER); waitForIdle(); verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session -> session.getSessionId() == sessionId @@ -9933,6 +10185,36 @@ public class ConnectivityServiceTest { } @Test + public void testNrQosCallbackAvailableAndLost() 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 NrQosSessionAttributes attributes = new NrQosSessionAttributes( + 1, 2, 3, 4, 5, 6, 7, new ArrayList<>()); + mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() + .sendQosSessionAvailable(qosCallbackId, sessionId, attributes); + waitForIdle(); + + verify(mQosCallbackMockHelper.mCallback).onNrQosSessionAvailable(argThat(session -> + session.getSessionId() == sessionId + && session.getSessionType() == QosSession.TYPE_NR_BEARER), eq(attributes)); + + mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent() + .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_NR_BEARER); + waitForIdle(); + verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session -> + session.getSessionId() == sessionId + && session.getSessionType() == QosSession.TYPE_NR_BEARER)); + } + + @Test public void testQosCallbackTooManyRequests() throws Exception { mQosCallbackMockHelper = new QosCallbackMockHelper(); @@ -10367,8 +10649,7 @@ public class ConnectivityServiceTest { private void registerDefaultNetworkCallbacks() { // Using Manifest.permission.NETWORK_SETTINGS for registerSystemDefaultNetworkCallback() - mServiceContext.setPermission( - Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED); mSystemDefaultNetworkCallback = new TestNetworkCallback(); mDefaultNetworkCallback = new TestNetworkCallback(); mProfileDefaultNetworkCallback = new TestNetworkCallback(); @@ -10377,8 +10658,8 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback); registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback, TEST_WORK_PROFILE_APP_UID); - mServiceContext.setPermission( - Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED); + // TODO: test using ConnectivityManager#registerDefaultNetworkCallbackAsUid as well. + mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_DENIED); } private void unregisterDefaultNetworkCallbacks() { @@ -10396,7 +10677,7 @@ public class ConnectivityServiceTest { private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest( @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup) throws Exception { - final int testPackageNameUid = 123; + final int testPackageNameUid = TEST_PACKAGE_UID; final String testPackageName = "per.app.defaults.package"; setupMultipleDefaultNetworksForOemNetworkPreferenceTest( networkPrefToSetup, testPackageNameUid, testPackageName); @@ -10532,6 +10813,11 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(defaultNetworkCallback); defaultNetworkCallback.assertNoCallback(); + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + // Setup the test process to use networkPref for their default network. setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); @@ -10542,19 +10828,22 @@ public class ConnectivityServiceTest { null, mEthernetNetworkAgent.getNetwork()); - // At this point with a restricted network used, the available callback should trigger + // At this point with a restricted network used, the available callback should trigger. defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mEthernetNetworkAgent.getNetwork()); + otherUidDefaultCallback.assertNoCallback(); // Now bring down the default network which should trigger a LOST callback. setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); // At this point, with no network is available, the lost callback should trigger defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); // Confirm we can unregister without issues. mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); } @Test @@ -10572,6 +10861,11 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(defaultNetworkCallback); defaultNetworkCallback.assertNoCallback(); + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. // The active nai for the default is null at this point as this is a restricted network. setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); @@ -10583,15 +10877,19 @@ public class ConnectivityServiceTest { defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mEthernetNetworkAgent.getNetwork()); + otherUidDefaultCallback.assertNoCallback(); // Now bring down the default network which should trigger a LOST callback. setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + otherUidDefaultCallback.assertNoCallback(); // At this point, with no network is available, the lost callback should trigger defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); // Confirm we can unregister without issues. mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); } @Test @@ -10605,6 +10903,11 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(defaultNetworkCallback); defaultNetworkCallback.assertNoCallback(); + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + // Setup a process different than the test process to use the default network. This means // that the defaultNetworkCallback won't be tracked by the per-app policy. setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref); @@ -10620,6 +10923,9 @@ public class ConnectivityServiceTest { defaultNetworkCallback.assertNoCallback(); assertDefaultNetworkCapabilities(userId /* no networks */); + // The other UID does have access, and gets a callback. + otherUidDefaultCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); + // Bring up unrestricted cellular. This should now satisfy the default network. setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, @@ -10627,25 +10933,31 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.getNetwork()); // At this point with an unrestricted network used, the available callback should trigger + // The other UID is unaffected and remains on the paid network. defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCellNetworkAgent.getNetwork()); assertDefaultNetworkCapabilities(userId, mCellNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); // Now bring down the per-app network. setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - // Since the callback didn't use the per-app network, no callback should fire. + // Since the callback didn't use the per-app network, only the other UID gets a callback. + // Because the preference specifies no fallback, it does not switch to cellular. defaultNetworkCallback.assertNoCallback(); + otherUidDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); // Now bring down the default network. setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); // As this callback was tracking the default, this should now trigger. defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); // Confirm we can unregister without issues. mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); } /** diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index 8c5d1d6d05e5..8b072c49de82 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -22,7 +22,9 @@ import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -56,6 +58,7 @@ import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -80,6 +83,12 @@ public class IpConnectivityMetricsTest { IpConnectivityMetrics mService; NetdEventListenerService mNetdListener; + private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build(); + private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build(); @Before public void setUp() { @@ -263,14 +272,6 @@ public class IpConnectivityMetricsTest { // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto. IpConnectivityLog logger = new IpConnectivityLog(mService.impl); - NetworkCapabilities ncWifi = new NetworkCapabilities(); - NetworkCapabilities ncCell = new NetworkCapabilities(); - ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); - ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - - when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi); - when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell); - ApfStats apfStats = new ApfStats.Builder() .setDurationMs(45000) .setReceivedRas(10) @@ -584,11 +585,21 @@ public class IpConnectivityMetricsTest { return buffer.toString(); } - void connectEvent(int netid, int error, int latencyMs, String ipAddr) throws Exception { - mNetdListener.onConnectEvent(netid, error, latencyMs, ipAddr, 80, 1); + private void setCapabilities(int netId) { + final ArgumentCaptor<ConnectivityManager.NetworkCallback> networkCallback = + ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); + verify(mCm).registerNetworkCallback(any(), networkCallback.capture()); + networkCallback.getValue().onCapabilitiesChanged(new Network(netId), + netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL); + } + + void connectEvent(int netId, int error, int latencyMs, String ipAddr) throws Exception { + setCapabilities(netId); + mNetdListener.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1); } void dnsEvent(int netId, int type, int result, int latency) throws Exception { + setCapabilities(netId); mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0); } diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java index 8ccea1aa3474..50aaaee24418 100644 --- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java +++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java @@ -23,8 +23,9 @@ import static com.android.testutils.MiscAsserts.assertStringContains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; import android.content.Context; import android.net.ConnectivityManager; @@ -42,6 +43,7 @@ import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpCon import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import java.io.FileOutputStream; import java.io.PrintWriter; @@ -61,18 +63,16 @@ public class NetdEventListenerServiceTest { NetdEventListenerService mService; ConnectivityManager mCm; + private static final NetworkCapabilities CAPABILITIES_WIFI = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build(); + private static final NetworkCapabilities CAPABILITIES_CELL = new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build(); @Before public void setUp() { - NetworkCapabilities ncWifi = new NetworkCapabilities(); - NetworkCapabilities ncCell = new NetworkCapabilities(); - ncWifi.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); - ncCell.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - mCm = mock(ConnectivityManager.class); - when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi); - when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell); - mService = new NetdEventListenerService(mCm); } @@ -470,7 +470,16 @@ public class NetdEventListenerServiceTest { assertEquals(want, got); } + private void setCapabilities(int netId) { + final ArgumentCaptor<ConnectivityManager.NetworkCallback> networkCallback = + ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); + verify(mCm).registerNetworkCallback(any(), networkCallback.capture()); + networkCallback.getValue().onCapabilitiesChanged(new Network(netId), + netId == 100 ? CAPABILITIES_WIFI : CAPABILITIES_CELL); + } + Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) { + setCapabilities(netId); return new Thread(() -> { try { mService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1); @@ -481,6 +490,7 @@ public class NetdEventListenerServiceTest { } void dnsEvent(int netId, int type, int result, int latency) throws Exception { + setCapabilities(netId); mService.onDnsEvent(netId, type, result, latency, "", null, 0, 0); } diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 9334e2c4ad77..eeeb4fb1d929 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -89,6 +89,7 @@ import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.UnderlyingNetworkInfo; +import android.net.TelephonyNetworkSpecifier; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.os.ConditionVariable; import android.os.Handler; @@ -1280,6 +1281,77 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } @Test + public void testDualVilteProviderStats() throws Exception { + // Pretend that network comes online. + expectDefaultSettings(); + final int subId1 = 1; + final int subId2 = 2; + final NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{ + buildImsState(IMSI_1, subId1, TEST_IFACE), + buildImsState(IMSI_2, subId2, TEST_IFACE2)}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + + // Register custom provider and retrieve callback. + final TestableNetworkStatsProviderBinder provider = + new TestableNetworkStatsProviderBinder(); + final INetworkStatsProviderCallback cb = + mService.registerNetworkStatsProvider("TEST", provider); + assertNotNull(cb); + + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Verifies that one requestStatsUpdate will be called during iface update. + provider.expectOnRequestStatsUpdate(0 /* unused */); + + // Create some initial traffic and report to the service. + incrementCurrentTime(HOUR_IN_MILLIS); + final String vtIface1 = NetworkStats.IFACE_VT + subId1; + final String vtIface2 = NetworkStats.IFACE_VT + subId2; + final NetworkStats expectedStats = new NetworkStats(0L, 1) + .addEntry(new NetworkStats.Entry(vtIface1, UID_RED, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 128L, 2L, 128L, 2L, 1L)) + .addEntry(new NetworkStats.Entry(vtIface2, UID_RED, SET_DEFAULT, + TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES, + 64L, 1L, 64L, 1L, 1L)); + cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats); + + // Make another empty mutable stats object. This is necessary since the new NetworkStats + // object will be used to compare with the old one in NetworkStatsRecoder, two of them + // cannot be the same object. + expectNetworkStatsUidDetail(buildEmptyStats()); + + forcePollAndWaitForIdle(); + + // Verifies that one requestStatsUpdate and setAlert will be called during polling. + provider.expectOnRequestStatsUpdate(0 /* unused */); + provider.expectOnSetAlert(MB_IN_BYTES); + + // Verifies that service recorded history, does not verify uid tag part. + assertUidTotal(sTemplateImsi1, UID_RED, 128L, 2L, 128L, 2L, 1); + + // Verifies that onStatsUpdated updates the stats accordingly. + NetworkStats stats = mSession.getSummaryForAllUid( + sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(1, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 1L); + + stats = mSession.getSummaryForAllUid( + sTemplateImsi2, Long.MIN_VALUE, Long.MAX_VALUE, true); + assertEquals(1, stats.size()); + assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO, + DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 1L); + + // Verifies that unregister the callback will remove the provider from service. + cb.unregister(); + forcePollAndWaitForIdle(); + provider.assertNoCallback(); + } + + @Test public void testStatsProviderSetAlert() throws Exception { // Pretend that network comes online. expectDefaultSettings(); @@ -1616,6 +1688,20 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { TYPE_MOBILE); } + private static NetworkStateSnapshot buildImsState( + String subscriberId, int subId, String ifaceName) { + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(ifaceName); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, true); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_IMS, true); + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + capabilities.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId)); + return new NetworkStateSnapshot( + MOBILE_NETWORK, capabilities, prop, subscriberId, TYPE_MOBILE); + } + private long getElapsedRealtime() { return mElapsedRealtime; } diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java index 7515971b8307..516c206672d2 100644 --- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -204,7 +204,7 @@ public class VcnManagerTest { new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback); cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE); - verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE); + verify(mMockStatusCallback).onStatusChanged(VCN_STATUS_CODE_ACTIVE); cbBinder.onGatewayConnectionError( UNDERLYING_NETWORK_CAPABILITIES, diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index a9d5822be226..f15d4204d125 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -19,6 +19,8 @@ package com.android.server; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; +import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; @@ -47,7 +49,9 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.AppOpsManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; @@ -238,9 +242,14 @@ public class VcnManagementServiceTest { doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO)) .when(mSubMgr) .getSubscriptionsInGroup(any()); - doReturn(isPrivileged) + doReturn(mTelMgr) .when(mTelMgr) - .hasCarrierPrivileges(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId())); + .createForSubscriptionId(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId())); + doReturn(isPrivileged + ? CARRIER_PRIVILEGE_STATUS_HAS_ACCESS + : CARRIER_PRIVILEGE_STATUS_NO_ACCESS) + .when(mTelMgr) + .checkCarrierPrivilegesForPackage(eq(TEST_PACKAGE_NAME)); } @Test @@ -329,6 +338,13 @@ public class VcnManagementServiceTest { return captor.getValue(); } + private BroadcastReceiver getPackageChangeReceiver() { + final ArgumentCaptor<BroadcastReceiver> captor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mMockContext).registerReceiver(captor.capture(), any(), any(), any()); + return captor.getValue(); + } + private Vcn startAndGetVcnInstance(ParcelUuid uuid) { mVcnMgmtSvc.setVcnConfig(uuid, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); return mVcnMgmtSvc.getAllVcns().get(uuid); @@ -391,7 +407,7 @@ public class VcnManagementServiceTest { mTestLooper.moveTimeForward( VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2); mTestLooper.dispatchAll(); - mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2); + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME); final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2); // Verify that new instance was different, and the old one was torn down @@ -405,6 +421,42 @@ public class VcnManagementServiceTest { } @Test + public void testPackageChangeListenerRegistered() throws Exception { + verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> { + return filter.hasAction(Intent.ACTION_PACKAGE_ADDED) + && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED) + && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED); + }), any(), any()); + } + + @Test + public void testPackageChangeListener_packageAdded() throws Exception { + final BroadcastReceiver receiver = getPackageChangeReceiver(); + + verify(mMockContext).registerReceiver(any(), argThat(filter -> { + return filter.hasAction(Intent.ACTION_PACKAGE_ADDED) + && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED) + && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED); + }), any(), any()); + + receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_ADDED)); + verify(mSubscriptionTracker).handleSubscriptionsChanged(); + } + + @Test + public void testPackageChangeListener_packageRemoved() throws Exception { + final BroadcastReceiver receiver = getPackageChangeReceiver(); + + verify(mMockContext).registerReceiver(any(), argThat(filter -> { + return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED) + && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED); + }), any(), any()); + + receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_REMOVED)); + verify(mSubscriptionTracker).handleSubscriptionsChanged(); + } + + @Test public void testSetVcnConfigRequiresNonSystemServer() throws Exception { doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); @@ -492,7 +544,7 @@ public class VcnManagementServiceTest { doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); try { - mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME); fail("Expected IllegalStateException exception for system server"); } catch (IllegalStateException expected) { } @@ -505,7 +557,7 @@ public class VcnManagementServiceTest { .getBinderCallingUid(); try { - mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME); fail("Expected security exception for non system user"); } catch (SecurityException expected) { } @@ -516,15 +568,24 @@ public class VcnManagementServiceTest { setupMockedCarrierPrivilege(false); try { - mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME); fail("Expected security exception for missing carrier privileges"); } catch (SecurityException expected) { } } @Test + public void testClearVcnConfigMismatchedPackages() throws Exception { + try { + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, "IncorrectPackage"); + fail("Expected security exception due to mismatched packages"); + } catch (SecurityException expected) { + } + } + + @Test public void testClearVcnConfig() throws Exception { - mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME); assertTrue(mVcnMgmtSvc.getConfigs().isEmpty()); verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class)); } @@ -535,7 +596,7 @@ public class VcnManagementServiceTest { mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME); verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE); - mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2); + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME); verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED); } @@ -564,7 +625,7 @@ public class VcnManagementServiceTest { verify(vcnInstance).updateConfig(TEST_VCN_CONFIG); // Verify Vcn is stopped if it was already started - mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2); + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME); verify(vcnInstance).teardownAsynchronously(); } @@ -781,7 +842,7 @@ public class VcnManagementServiceTest { mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); - mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2); + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME); verify(mMockPolicyListener).onPolicyChanged(); } diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java index 1ef1a61f17ea..631c862d2bfd 100644 --- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java @@ -328,7 +328,7 @@ public class UnderlyingNetworkTrackerTest { public void testRecordTrackerCallbackNotifiedForNetworkSuspended() { RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(); - cb.onNetworkSuspended(mNetwork); + cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES); UnderlyingNetworkRecord expectedRecord = new UnderlyingNetworkRecord( @@ -336,7 +336,11 @@ public class UnderlyingNetworkTrackerTest { SUSPENDED_NETWORK_CAPABILITIES, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + // onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't + // change. + cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES); + verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); } @Test @@ -344,7 +348,7 @@ public class UnderlyingNetworkTrackerTest { RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES); - cb.onNetworkResumed(mNetwork); + cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES); UnderlyingNetworkRecord expectedRecord = new UnderlyingNetworkRecord( @@ -352,7 +356,11 @@ public class UnderlyingNetworkTrackerTest { INITIAL_NETWORK_CAPABILITIES, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + // onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't + // change. + cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES); + verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index 4fa63d4ff640..c853fc50fdf7 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -29,6 +29,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -139,8 +140,7 @@ public class VcnTest { mTestLooper.dispatchAll(); } - @Test - public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() { + private void verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(boolean isActive) { final NetworkRequestListener requestListener = verifyAndGetRequestListener(); startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]); @@ -150,14 +150,27 @@ public class VcnTest { final TelephonySubscriptionSnapshot updatedSnapshot = mock(TelephonySubscriptionSnapshot.class); + mVcn.setIsActive(isActive); + mVcn.updateSubscriptionSnapshot(updatedSnapshot); mTestLooper.dispatchAll(); for (final VcnGatewayConnection gateway : gatewayConnections) { - verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot)); + verify(gateway, isActive ? times(1) : never()) + .updateSubscriptionSnapshot(eq(updatedSnapshot)); } } + @Test + public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() { + verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(true /* isActive */); + } + + @Test + public void testSubscriptionSnapshotUpdatesVcnGatewayConnectionsWhileInactive() { + verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(false /* isActive */); + } + private void triggerVcnRequestListeners(NetworkRequestListener requestListener) { for (final int[] caps : TEST_CAPS) { startVcnGatewayWithCapabilities(requestListener, caps); @@ -187,7 +200,6 @@ public class VcnTest { NetworkRequestListener requestListener, Set<VcnGatewayConnection> expectedGatewaysTornDown) { assertFalse(mVcn.isActive()); - assertTrue(mVcn.getVcnGatewayConnections().isEmpty()); for (final VcnGatewayConnection gatewayConnection : expectedGatewaysTornDown) { verify(gatewayConnection).teardownAsynchronously(); } @@ -238,6 +250,51 @@ public class VcnTest { } @Test + public void testGatewayQuitWhileInactive() { + final NetworkRequestListener requestListener = verifyAndGetRequestListener(); + final Set<VcnGatewayConnection> gatewayConnections = + new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener)); + + mVcn.teardownAsynchronously(); + mTestLooper.dispatchAll(); + + final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue(); + statusCallback.onQuit(); + mTestLooper.dispatchAll(); + + // Verify that the VCN requests the networkRequests be resent + assertEquals(1, mVcn.getVcnGatewayConnections().size()); + verify(mVcnNetworkProvider, never()).resendAllRequests(requestListener); + } + + @Test + public void testUpdateConfigReevaluatesGatewayConnections() { + final NetworkRequestListener requestListener = verifyAndGetRequestListener(); + startGatewaysAndGetGatewayConnections(requestListener); + assertEquals(2, mVcn.getVcnGatewayConnectionConfigMap().size()); + + // Create VcnConfig with only one VcnGatewayConnectionConfig so a gateway connection is torn + // down + final VcnGatewayConnectionConfig activeConfig = + VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(TEST_CAPS[0]); + final VcnGatewayConnectionConfig removedConfig = + VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(TEST_CAPS[1]); + final VcnConfig updatedConfig = + new VcnConfig.Builder(mContext).addGatewayConnectionConfig(activeConfig).build(); + + mVcn.updateConfig(updatedConfig); + mTestLooper.dispatchAll(); + + final VcnGatewayConnection activeGatewayConnection = + mVcn.getVcnGatewayConnectionConfigMap().get(activeConfig); + final VcnGatewayConnection removedGatewayConnection = + mVcn.getVcnGatewayConnectionConfigMap().get(removedConfig); + verify(activeGatewayConnection, never()).teardownAsynchronously(); + verify(removedGatewayConnection).teardownAsynchronously(); + verify(mVcnNetworkProvider).resendAllRequests(requestListener); + } + + @Test public void testUpdateConfigExitsSafeMode() { final NetworkRequestListener requestListener = verifyAndGetRequestListener(); final Set<VcnGatewayConnection> gatewayConnections = @@ -261,8 +318,8 @@ public class VcnTest { verify(mVcnNetworkProvider, times(2)).registerListener(eq(requestListener)); assertTrue(mVcn.isActive()); for (final int[] caps : TEST_CAPS) { - // Expect each gateway connection created on initial startup, and again with new configs - verify(mDeps, times(2)) + // Expect each gateway connection created only on initial startup + verify(mDeps) .newVcnGatewayConnection( eq(mVcnContext), eq(TEST_SUB_GROUP), @@ -271,4 +328,14 @@ public class VcnTest { any()); } } + + @Test + public void testIgnoreNetworkRequestWhileInactive() { + mVcn.setIsActive(false /* isActive */); + + final NetworkRequestListener requestListener = verifyAndGetRequestListener(); + triggerVcnRequestListeners(requestListener); + + verify(mDeps, never()).newVcnGatewayConnection(any(), any(), any(), any(), any()); + } } |