diff options
327 files changed, 12169 insertions, 5475 deletions
diff --git a/Android.bp b/Android.bp index 00b419897f34..f8a9e0f0f393 100644 --- a/Android.bp +++ b/Android.bp @@ -69,6 +69,7 @@ filegroup { // Java/AIDL sources under frameworks/base ":framework-annotations", ":framework-blobstore-sources", + ":framework-connectivity-nsd-sources", ":framework-core-sources", ":framework-drm-sources", ":framework-graphics-nonupdatable-sources", @@ -82,6 +83,7 @@ filegroup { ":framework-mca-filterpacks-sources", ":framework-media-sources", ":framework-mms-sources", + ":framework-omapi-sources", ":framework-opengl-sources", ":framework-rs-sources", ":framework-sax-sources", @@ -268,6 +270,7 @@ java_library { "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", "android.hardware.vibrator-V2-java", + "android.se.omapi-V1-java", "android.system.suspend.control.internal-java", "devicepolicyprotosnano", @@ -328,6 +331,7 @@ java_defaults { "TeleService-platform-compat-config", "documents-ui-compat-config", "calendar-provider-compat-config", + "contacts-provider-platform-compat-config", ], libs: [ "app-compat-annotations", @@ -433,11 +437,8 @@ filegroup { "core/java/android/util/LocalLog.java", "core/java/com/android/internal/util/HexDump.java", "core/java/com/android/internal/util/IndentingPrintWriter.java", - "core/java/com/android/internal/util/IState.java", "core/java/com/android/internal/util/MessageUtils.java", "core/java/com/android/internal/util/RingBufferIndices.java", - "core/java/com/android/internal/util/State.java", - "core/java/com/android/internal/util/StateMachine.java", "core/java/com/android/internal/util/WakeupMessage.java", "core/java/com/android/internal/util/TokenBucket.java", ], @@ -566,6 +567,7 @@ stubs_defaults { "android.hardware.vibrator-V1.3-java", "framework-protos", "art.module.public.api", + "sdk_module-lib_current_framework-tethering", // There are a few classes from modules used by the core that // need to be resolved by metalava. We use a prebuilt stub of the // full sdk to ensure we can resolve them. If a new class gets added, diff --git a/GAME_MANAGER_OWNERS b/GAME_MANAGER_OWNERS new file mode 100644 index 000000000000..502a9e362202 --- /dev/null +++ b/GAME_MANAGER_OWNERS @@ -0,0 +1,2 @@ +lpy@google.com +timvp@google.com diff --git a/StubLibraries.bp b/StubLibraries.bp index 44c55c26153d..d090296523ed 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -318,6 +318,7 @@ java_library { defaults: ["android-non-updatable_defaults_stubs_current"], srcs: [":module-lib-api-stubs-docs-non-updatable"], libs: [ + "sdk_module-lib_current_framework-tethering", "sdk_system_current_android", // NOTE: The below can be removed once the prebuilt stub contains IKE. "sdk_system_current_android.net.ipsec.ike", diff --git a/apex/blobstore/README.md b/apex/blobstore/README.md new file mode 100644 index 000000000000..69af436bf325 --- /dev/null +++ b/apex/blobstore/README.md @@ -0,0 +1,125 @@ +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License +--> + +# BlobStore Manager + +## Introduction +* BlobStoreManager is a system service added in Android R release that facilitates sharing of + data blobs among apps. +* Apps that would like to share data blobs with other apps can do so by contributing those + data blobs with the System and can choose how they would like the System to share the data blobs + with other apps. +* Apps can access data blobs shared by other apps from the System using checksum of the data blobs + (plus some other data attributes. More details [below](#blob-handle)). +* The APIs provided by the BlobStoreManager are meant to reduce storage and network usage by + reusing the data available on the device instead of downloading the same data again and having + multiple copies of the same data on disk. +* It is not meant to provide access to the data which apps otherwise would not be able to access. + In other words, if an app’s only means of obtaining access to certain data is through + BlobStoreManager, then that use case is not really intended or supported. +* For example, if earlier an app was downloading certain shared data from a server, then by using + BlobStoreManager, it can first check whether or not the data is already available on the device + before downloading. + +## Concepts +### Blob handle +Blob handle is the identifier of the data and it is what apps need to use for referring to the +data blobs. Currently, this is made of following bits of information: +* SHA256 checksum of data +* Data label: A user readable string that indicates what the data blob is. + This is meant to be used when surfacing a list of blobs to the user. +* Data expiry time: A timestamp after which the data blob should be considered invalid and not + allowed to be accessed by any app. +* Data tag: An opaque string associated with the blob. System does not interpret this in any way or + use it for any purposes other than when checking whether two Blob handle identifiers are referring + to the same data blob. This is meant to be used by the apps, either for categorization for + data blobs or for adding additional identifiers. For example, an app can add tags like + *machine_learning* or *media* depending on the data blob if necessary. + +When comparing two Blob handles, the System will compare all the pieces of information above and +only when two Blob handles are equal, the data blobs corresponding to those identifiers are +considered equal. + +### Blob sharing session +Session is a way to allow apps to contribute data over multiple time intervals. Each session is +associated with a unique Identifier that is created and obtained by the apps by calling +[BlobStoreManager#createSession](https://developer.android.com/reference/android/app/blob/BlobStoreManager#createSession(android.app.blob.BlobHandle)). +Apps can save the Identifier associated with a session and use it to open and close it +multiple times for contributing the data. For example, if an app is downloading +some content over the network, it can start a Session and start contributing this data to the +System immediately and if the network connection is lost for any reason, the app can close this +session. When the download resumes, the app can reopen the session and start contributing again. +Note that once the entire data is contributed, the app has no reason to hold on to the Session Id. + +### Blob commit +Since a data blob can be contributed in a session over multiple time intervals, an app closing a +session does not imply that the contribution is completed. So, *commit* is added as an explicit +event / signal for the app to indicate that the contribution of the data blob is completed. +At this point, the System can verify the data blob does indeed correspond to the Blob handle used +by the app and prevent the app from making any further modifications to the data blob. Once the +data blob is committed and verified by the System, it is available for other applications to access. + +### Access modes +When an application contributes a data blob to the System, it can choose to specify how it would +like the System to share this data blob with other applications. Access modes refer to the type of +access that apps specified when contributing a data blob. As of Android S release, there are +four access modes: +* Allow specific packages: Apps can specify a specific set of applications that are allowed to + access their data blob. +* Allow packages with the same signature: Apps can specify that only the applications that are + signed with the same certificate as them can access their data blob. +* Allow public access: Apps can specify that any other app on the device can access their data blob. +* Allow private access: Apps can specify that no other app can access their data blob unless they + happen to contribute the same data blob. + * Note that in this case, two apps might download the same data blob and contribute to the System + in which case we are not saving anything in terms of bandwidth usage, but we would still be + saving disk usage since we would be keeping only one copy of data on disk. + +### Lease +Leasing a blob is a way to specify that an application is interested in using a data blob +and would like the System to not delete this data blob. Applications can also access a blob +without holding a lease on it, in which case the System can choose to delete the data blob at any +time. So, if an application wants to make sure a data blob is available for access for a certain +period, it is recommended that the application acquire a lease on the data blob. Applications can +either specify upfront how long they would like to hold the lease for (which is called the lease +expiry time), or they can acquire a lease without specifying a time period and release the lease +when they are done with the data blob. + +## Sharing data blobs across users +By default, data blobs are only accessible to applications in the user in which the data blob was +contributed, but if an application holds the permission +[ACCESS_BLOBS_ACROSS_USERS](https://developer.android.com/reference/android/Manifest.permission#ACCESS_BLOBS_ACROSS_USERS), +then they are allowed to access blobs that are contributed by the applications in the other users. +As of Android S, this permission is only available to following set of applications: +* Apps signed with the platform certificate +* Privileged applications +* Applications holding the + [ASSISTANT](https://developer.android.com/reference/android/app/role/RoleManager#ROLE_ASSISTANT) + role +* Development applications + +Note that the access modes that applications choose while committing the data blobs still apply +when these data blobs are accessed across users. So for example, if *appA* contributed a +data blob in *user0* and specified to share this data blob with only a specific set of +applications [*appB*, *appC*], then *appD* on *user10* will not be able to access this data blob +even if the app is granted the `ACCESS_BLOBS_ACROSS_USERS` permission. + +When apps that are allowed to access blobs across users +(i.e. those holding the permission `ACCESS_BLOBS_ACROSS_USERS`) try to access a data blob, +they can do so as if it is any other data blob. In other words, the applications don’t need to +know where the data blob is contributed, because the System will automatically check and will +allow access if this data blob is available either on the user in which the calling application +is running in or other users.
\ No newline at end of file diff --git a/apex/media/framework/java/android/media/MediaTranscodingManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java index 93d58d07f81a..1a84929c678f 100644 --- a/apex/media/framework/java/android/media/MediaTranscodingManager.java +++ b/apex/media/framework/java/android/media/MediaTranscodingManager.java @@ -952,6 +952,8 @@ public final class MediaTranscodingManager { * * @return the video track format to be used if transcoding should be performed, * and null otherwise. + * @throws IllegalArgumentException if the hinted source video format contains invalid + * parameters. */ @Nullable public MediaFormat resolveVideoFormat() { @@ -962,20 +964,19 @@ public final class MediaTranscodingManager { MediaFormat videoTrackFormat = new MediaFormat(mSrcVideoFormatHint); videoTrackFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC); - int width = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_WIDTH); - int height = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_HEIGHT); + int width = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_WIDTH, -1); + int height = mSrcVideoFormatHint.getInteger(MediaFormat.KEY_HEIGHT, -1); if (width <= 0 || height <= 0) { throw new IllegalArgumentException( "Source Width and height must be larger than 0"); } - float frameRate = 30.0f; // default to 30fps. - if (mSrcVideoFormatHint.containsKey(MediaFormat.KEY_FRAME_RATE)) { - frameRate = mSrcVideoFormatHint.getFloat(MediaFormat.KEY_FRAME_RATE); - if (frameRate <= 0) { - throw new IllegalArgumentException( - "frameRate must be larger than 0"); - } + float frameRate = + mSrcVideoFormatHint.getNumber(MediaFormat.KEY_FRAME_RATE, 30.0) + .floatValue(); + if (frameRate <= 0) { + throw new IllegalArgumentException( + "frameRate must be larger than 0"); } int bitrate = getAVCBitrate(width, height, frameRate); diff --git a/boot/boot-image-profile.txt b/boot/boot-image-profile.txt index 5f27cc722105..82269d4a2467 100644 --- a/boot/boot-image-profile.txt +++ b/boot/boot-image-profile.txt @@ -2899,10 +2899,8 @@ HSPLandroid/app/assist/AssistStructure$ViewNode;->getAutofillId()Landroid/view/a HSPLandroid/app/assist/AssistStructure$ViewNode;->getChildCount()I HSPLandroid/app/assist/AssistStructure$ViewNode;->writeSelfToParcel(Landroid/os/Parcel;Landroid/os/PooledStringWriter;Z[FZ)I+]Landroid/view/autofill/AutofillId;Landroid/view/autofill/AutofillId;]Landroid/graphics/Matrix;Landroid/graphics/Matrix;]Landroid/app/assist/AssistStructure$ViewNodeText;Landroid/app/assist/AssistStructure$ViewNodeText;]Landroid/os/Parcel;Landroid/os/Parcel; HSPLandroid/app/assist/AssistStructure$ViewNode;->writeString(Landroid/os/Parcel;Landroid/os/PooledStringWriter;Ljava/lang/String;)V+]Landroid/os/PooledStringWriter;Landroid/os/PooledStringWriter;]Landroid/os/Parcel;Landroid/os/Parcel; -HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;-><init>()V HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getChildCount()I HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getNodeText()Landroid/app/assist/AssistStructure$ViewNodeText; -HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getViewNode()Landroid/app/assist/AssistStructure$ViewNode; HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->newChild(I)Landroid/view/ViewStructure; HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->setAutofillHints([Ljava/lang/String;)V HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->setAutofillId(Landroid/view/autofill/AutofillId;)V @@ -3883,8 +3881,6 @@ HSPLandroid/content/ContentResolver$ResultListener;-><init>()V HSPLandroid/content/ContentResolver$ResultListener;-><init>(Landroid/content/ContentResolver$1;)V HSPLandroid/content/ContentResolver$ResultListener;->onResult(Landroid/os/Bundle;)V+]Landroid/os/ParcelableException;Landroid/os/ParcelableException;]Ljava/lang/Object;Landroid/content/ContentResolver$StringResultListener;,Landroid/content/ContentResolver$UriResultListener;]Landroid/os/Bundle;Landroid/os/Bundle;]Landroid/content/ContentResolver$ResultListener;Landroid/content/ContentResolver$UriResultListener;,Landroid/content/ContentResolver$StringResultListener; HSPLandroid/content/ContentResolver$ResultListener;->waitForResult(J)V+]Ljava/lang/Object;Landroid/content/ContentResolver$StringResultListener; -HSPLandroid/content/ContentResolver$StringResultListener;-><init>()V -HSPLandroid/content/ContentResolver$StringResultListener;-><init>(Landroid/content/ContentResolver$1;)V HSPLandroid/content/ContentResolver$StringResultListener;->getResultFromBundle(Landroid/os/Bundle;)Ljava/lang/Object;+]Landroid/content/ContentResolver$StringResultListener;Landroid/content/ContentResolver$StringResultListener; HSPLandroid/content/ContentResolver$StringResultListener;->getResultFromBundle(Landroid/os/Bundle;)Ljava/lang/String;+]Landroid/os/Bundle;Landroid/os/Bundle; HSPLandroid/content/ContentResolver;-><init>(Landroid/content/Context;)V @@ -7029,7 +7025,6 @@ HSPLandroid/graphics/RectF;->width()F HSPLandroid/graphics/Region$1;->createFromParcel(Landroid/os/Parcel;)Landroid/graphics/Region; HSPLandroid/graphics/Region$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;+]Landroid/graphics/Region$1;Landroid/graphics/Region$1; HSPLandroid/graphics/Region;-><init>()V -HSPLandroid/graphics/Region;-><init>(IIII)V HSPLandroid/graphics/Region;-><init>(J)V HSPLandroid/graphics/Region;->access$000(Landroid/os/Parcel;)J HSPLandroid/graphics/Region;->equals(Ljava/lang/Object;)Z @@ -7075,7 +7070,6 @@ HSPLandroid/graphics/RenderNode;->getTranslationZ()F HSPLandroid/graphics/RenderNode;->hasDisplayList()Z HSPLandroid/graphics/RenderNode;->hasIdentityMatrix()Z HSPLandroid/graphics/RenderNode;->isAttached()Z+]Landroid/graphics/RenderNode$AnimationHost;Landroid/view/ViewAnimationHostBridge; -HSPLandroid/graphics/RenderNode;->isPivotExplicitlySet()Z HSPLandroid/graphics/RenderNode;->offsetTopAndBottom(I)Z HSPLandroid/graphics/RenderNode;->setAlpha(F)Z HSPLandroid/graphics/RenderNode;->setAnimationMatrix(Landroid/graphics/Matrix;)Z+]Landroid/graphics/Matrix;Landroid/graphics/Matrix; @@ -8283,7 +8277,6 @@ HSPLandroid/hardware/CameraStatus$1;->newArray(I)[Ljava/lang/Object; HSPLandroid/hardware/GeomagneticField$LegendreTable;-><init>(IF)V HSPLandroid/hardware/GeomagneticField;-><init>(FFFJ)V HSPLandroid/hardware/GeomagneticField;->computeGeocentricCoordinates(FFF)V -HSPLandroid/hardware/GeomagneticField;->getDeclination()F HSPLandroid/hardware/HardwareBuffer$1;->createFromParcel(Landroid/os/Parcel;)Landroid/hardware/HardwareBuffer; HSPLandroid/hardware/HardwareBuffer$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object; HSPLandroid/hardware/HardwareBuffer;-><init>(J)V+]Llibcore/util/NativeAllocationRegistry;Llibcore/util/NativeAllocationRegistry;]Ljava/lang/Class;Ljava/lang/Class;]Ldalvik/system/CloseGuard;Ldalvik/system/CloseGuard; @@ -11353,7 +11346,6 @@ HSPLandroid/media/SoundPool$Builder;->setMaxStreams(I)Landroid/media/SoundPool$B HSPLandroid/media/SoundPool$EventHandler;->handleMessage(Landroid/os/Message;)V HSPLandroid/media/SoundPool;-><init>(ILandroid/media/AudioAttributes;)V HSPLandroid/media/SoundPool;-><init>(ILandroid/media/AudioAttributes;Landroid/media/SoundPool$1;)V -HSPLandroid/media/SoundPool;->load(Ljava/lang/String;I)I HSPLandroid/media/SoundPool;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V HSPLandroid/media/SoundPool;->setOnLoadCompleteListener(Landroid/media/SoundPool$OnLoadCompleteListener;)V HSPLandroid/media/SubtitleController$1;->handleMessage(Landroid/os/Message;)Z @@ -11389,10 +11381,8 @@ HSPLandroid/media/Utils;->parseSizeRange(Ljava/lang/Object;)Landroid/util/Pair; HSPLandroid/media/Utils;->sortDistinctRanges([Landroid/util/Range;)V HSPLandroid/media/audiofx/AudioEffect$Descriptor;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V HSPLandroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;-><init>(II[Landroid/media/AudioAttributes;)V -HSPLandroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;->supportsStreamType(I)Z HSPLandroid/media/audiopolicy/AudioProductStrategy;-><init>(Ljava/lang/String;I[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;)V HSPLandroid/media/audiopolicy/AudioProductStrategy;->attributesMatches(Landroid/media/AudioAttributes;Landroid/media/AudioAttributes;)Z+]Landroid/media/AudioAttributes;Landroid/media/AudioAttributes; -HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioAttributesForLegacyStreamType(I)Landroid/media/AudioAttributes;+]Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup; HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioAttributesForStrategyWithLegacyStreamType(I)Landroid/media/AudioAttributes; HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioProductStrategies()Ljava/util/List; HSPLandroid/media/audiopolicy/AudioProductStrategy;->getLegacyStreamTypeForStrategyWithAudioAttributes(Landroid/media/AudioAttributes;)I+]Landroid/media/audiopolicy/AudioProductStrategy;Landroid/media/audiopolicy/AudioProductStrategy;]Ljava/util/List;Ljava/util/ArrayList;]Ljava/util/Iterator;Ljava/util/ArrayList$Itr; @@ -13125,7 +13115,6 @@ HSPLandroid/os/ParcelableException;-><init>(Ljava/lang/Throwable;)V HSPLandroid/os/ParcelableParcel$1;->createFromParcel(Landroid/os/Parcel;)Landroid/os/ParcelableParcel; HSPLandroid/os/ParcelableParcel$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object; HSPLandroid/os/ParcelableParcel;-><init>(Landroid/os/Parcel;Ljava/lang/ClassLoader;)V -HSPLandroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V HSPLandroid/os/ParcelableParcel;->getClassLoader()Ljava/lang/ClassLoader; HSPLandroid/os/ParcelableParcel;->getParcel()Landroid/os/Parcel; HSPLandroid/os/ParcelableParcel;->writeToParcel(Landroid/os/Parcel;I)V @@ -13481,7 +13470,6 @@ HSPLandroid/os/TelephonyServiceManager;->getTelephonyServiceRegisterer()Landroid HSPLandroid/os/Temperature;-><init>(FILjava/lang/String;I)V HSPLandroid/os/Temperature;->getStatus()I HSPLandroid/os/Temperature;->isValidStatus(I)Z -HSPLandroid/os/Temperature;->isValidType(I)Z HSPLandroid/os/ThreadLocalWorkSource$$ExternalSyntheticLambda0;->get()Ljava/lang/Object; HSPLandroid/os/ThreadLocalWorkSource;->getToken()J+]Ljava/lang/Integer;Ljava/lang/Integer;]Ljava/lang/ThreadLocal;Ljava/lang/ThreadLocal$SuppliedThreadLocal; HSPLandroid/os/ThreadLocalWorkSource;->getUid()I+]Ljava/lang/Integer;Ljava/lang/Integer;]Ljava/lang/ThreadLocal;Ljava/lang/ThreadLocal$SuppliedThreadLocal; @@ -18488,7 +18476,6 @@ HSPLandroid/view/ViewRootImpl$HighContrastTextManager;-><init>(Landroid/view/Vie HSPLandroid/view/ViewRootImpl$ImeInputStage;-><init>(Landroid/view/ViewRootImpl;Landroid/view/ViewRootImpl$InputStage;Ljava/lang/String;)V HSPLandroid/view/ViewRootImpl$ImeInputStage;->onFinishedInputEvent(Ljava/lang/Object;Z)V HSPLandroid/view/ViewRootImpl$ImeInputStage;->onProcess(Landroid/view/ViewRootImpl$QueuedInputEvent;)I -HSPLandroid/view/ViewRootImpl$InputMetricsListener;-><init>(Landroid/view/ViewRootImpl;)V HSPLandroid/view/ViewRootImpl$InputMetricsListener;->onFrameMetricsAvailable(I)V+]Landroid/view/ViewRootImpl$WindowInputEventReceiver;Landroid/view/ViewRootImpl$WindowInputEventReceiver;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Landroid/view/InputEventReceiver;Landroid/view/ViewRootImpl$WindowInputEventReceiver; HSPLandroid/view/ViewRootImpl$InputStage;-><init>(Landroid/view/ViewRootImpl;Landroid/view/ViewRootImpl$InputStage;)V HSPLandroid/view/ViewRootImpl$InputStage;->apply(Landroid/view/ViewRootImpl$QueuedInputEvent;I)V+]Landroid/view/ViewRootImpl$InputStage;megamorphic_types @@ -20395,7 +20382,6 @@ HSPLandroid/widget/OverScroller$SplineOverScroller;->finish()V HSPLandroid/widget/OverScroller$SplineOverScroller;->fling(IIIII)V HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineDeceleration(I)D HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineFlingDistance(I)D -HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineFlingDuration(I)I HSPLandroid/widget/OverScroller$SplineOverScroller;->onEdgeReached()V HSPLandroid/widget/OverScroller$SplineOverScroller;->springback(III)Z HSPLandroid/widget/OverScroller$SplineOverScroller;->startScroll(III)V @@ -22568,10 +22554,7 @@ HSPLcom/android/internal/widget/ILockSettings$Stub;->asInterface(Landroid/os/IBi HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$1;->onIsNonStrongBiometricAllowedChanged(ZI)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$1;->onStrongAuthRequiredChanged(II)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$H;->handleMessage(Landroid/os/Message;)V -HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;-><init>(Landroid/content/Context;)V -HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;-><init>(Landroid/content/Context;Landroid/os/Looper;)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->getStrongAuthForUser(I)I+]Landroid/util/SparseIntArray;Landroid/util/SparseIntArray; -HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->getStub()Landroid/app/trust/IStrongAuthTracker$Stub; HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->handleIsNonStrongBiometricAllowedChanged(ZI)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->handleStrongAuthRequiredChanged(II)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->isNonStrongBiometricAllowedAfterIdleTimeout(I)Z diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java index 1e72ddf8ecfc..0b439df403e0 100644 --- a/cmds/am/src/com/android/commands/am/Instrument.java +++ b/cmds/am/src/com/android/commands/am/Instrument.java @@ -545,6 +545,8 @@ public class Instrument { mWm.setAnimationScales(oldAnims); } } + // Exit from here instead of going down the path of normal shutdown which is slow. + System.exit(0); } private static String readLogcat(long startTimeMs) { diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 3109c5c1e075..6b8a775ef838 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -1348,7 +1348,7 @@ bool BootAnimation::playAnimation(const Animation& animation) { int err; do { err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr); - } while (err<0 && errno == EINTR); + } while (err == EINTR); } checkExit(); diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS index 69dfcc98340d..062ffd41348a 100644 --- a/cmds/idmap2/OWNERS +++ b/cmds/idmap2/OWNERS @@ -1,4 +1,4 @@ set noparent toddke@google.com -rtmitchell@google.com -patb@google.com
\ No newline at end of file +patb@google.com +zyy@google.com diff --git a/config/README.md b/config/README.md new file mode 100644 index 000000000000..450a5c695c82 --- /dev/null +++ b/config/README.md @@ -0,0 +1,13 @@ +# Configuration files for ART compiling the framework + +* boot-image-profile.txt: A list of methods from the boot classpath to be compiled by dex2oat. + The order in the file is not relevant. +* boot-profile.txt: An ordered list of methods from the boot classpath to be compiled by + the JIT in the order provided in the file. Used by JIT zygote, when on-device + signing failed. +* dirty-image-objects: List of objects in the boot image which are known to + become dirty. This helps binning objects in the image file. +* preloaded-classes: classes that will be allocated in the boot image, and + initialized by the zygote. +* preloaded-classes-denylist: Classes that should not be initialized in the + zygote, as they have app-specific behavior. diff --git a/config/generate-preloaded-classes.sh b/config/generate-preloaded-classes.sh deleted file mode 100755 index b17a3660e1f1..000000000000 --- a/config/generate-preloaded-classes.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -if [ "$#" -lt 2 ]; then - echo "Usage $0 <input classes file> <denylist file> [extra classes files]" - exit 1 -fi - -# Write file headers first -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cat "$DIR/copyright-header" -echo "# Preloaded-classes filter file for phones. -# -# Classes in this file will be allocated into the boot image, and forcibly initialized in -# the zygote during initialization. This is a trade-off, using virtual address space to share -# common heap between apps. -# -# This file has been derived for mainline phone (and tablet) usage. -#" - -input=$1 -denylist=$2 -shift 2 -extra_classes_files=("$@") - -# Disable locale to enable lexicographical sorting -LC_ALL=C sort "$input" "${extra_classes_files[@]}" | uniq | grep -f "$denylist" -v -F -x | grep -v "\$NoPreloadHolder" diff --git a/config/preloaded-classes-extra b/config/preloaded-classes-extra deleted file mode 100644 index 09f393ad4844..000000000000 --- a/config/preloaded-classes-extra +++ /dev/null @@ -1,14 +0,0 @@ -android.icu.impl.coll.CollationRoot -android.icu.impl.IDNA2003 -android.icu.impl.number.Parse -android.icu.util.TimeZone -android.media.ImageReader -android.media.MediaCodecList -android.media.MediaPlayer -android.media.SoundPool -android.text.format.Formatter -android.text.Html$HtmlParser -android.util.Log$PreloadHolder -com.android.org.conscrypt.TrustedCertificateStore -org.ccil.cowan.tagsoup.HTMLScanner -sun.security.jca.Providers diff --git a/core/api/Android.bp b/core/api/Android.bp index 170febb4f766..114a957674ae 100644 --- a/core/api/Android.bp +++ b/core/api/Android.bp @@ -51,11 +51,17 @@ filegroup { filegroup { name: "non-updatable-module-lib-current.txt", srcs: ["module-lib-current.txt"], - visibility: ["//frameworks/base/api"], + visibility: [ + "//frameworks/base/api", + "//cts/tests/signature/api", + ], } filegroup { name: "non-updatable-module-lib-removed.txt", srcs: ["module-lib-removed.txt"], - visibility: ["//frameworks/base/api"], + visibility: [ + "//frameworks/base/api", + "//cts/tests/signature/api", + ], } diff --git a/core/api/current.txt b/core/api/current.txt index 1c0144546239..8c313a25ccec 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -120,6 +120,7 @@ package android { field @Deprecated public static final String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY"; field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"; field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES"; + field public static final String READ_BASIC_PHONE_STATE = "android.permission.READ_BASIC_PHONE_STATE"; field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR"; field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG"; field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS"; @@ -9023,6 +9024,72 @@ package android.bluetooth { field public static final int TELEPHONY = 4194304; // 0x400000 } + public final class BluetoothCodecConfig implements android.os.Parcelable { + ctor public BluetoothCodecConfig(int); + method public int describeContents(); + method public int getBitsPerSample(); + method public int getChannelMode(); + method public int getCodecPriority(); + method public long getCodecSpecific1(); + method public long getCodecSpecific2(); + method public long getCodecSpecific3(); + method public long getCodecSpecific4(); + method public int getCodecType(); + method public static int getMaxCodecType(); + method public int getSampleRate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final int BITS_PER_SAMPLE_16 = 1; // 0x1 + field public static final int BITS_PER_SAMPLE_24 = 2; // 0x2 + field public static final int BITS_PER_SAMPLE_32 = 4; // 0x4 + field public static final int BITS_PER_SAMPLE_NONE = 0; // 0x0 + field public static final int CHANNEL_MODE_MONO = 1; // 0x1 + field public static final int CHANNEL_MODE_NONE = 0; // 0x0 + field public static final int CHANNEL_MODE_STEREO = 2; // 0x2 + field public static final int CODEC_PRIORITY_DEFAULT = 0; // 0x0 + field public static final int CODEC_PRIORITY_DISABLED = -1; // 0xffffffff + field public static final int CODEC_PRIORITY_HIGHEST = 1000000; // 0xf4240 + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothCodecConfig> CREATOR; + field public static final int SAMPLE_RATE_176400 = 16; // 0x10 + field public static final int SAMPLE_RATE_192000 = 32; // 0x20 + field public static final int SAMPLE_RATE_44100 = 1; // 0x1 + field public static final int SAMPLE_RATE_48000 = 2; // 0x2 + field public static final int SAMPLE_RATE_88200 = 4; // 0x4 + field public static final int SAMPLE_RATE_96000 = 8; // 0x8 + field public static final int SAMPLE_RATE_NONE = 0; // 0x0 + field public static final int SOURCE_CODEC_TYPE_AAC = 1; // 0x1 + field public static final int SOURCE_CODEC_TYPE_APTX = 2; // 0x2 + field public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; // 0x3 + field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240 + field public static final int SOURCE_CODEC_TYPE_LDAC = 4; // 0x4 + field public static final int SOURCE_CODEC_TYPE_SBC = 0; // 0x0 + } + + public static final class BluetoothCodecConfig.Builder { + ctor public BluetoothCodecConfig.Builder(); + method @NonNull public android.bluetooth.BluetoothCodecConfig build(); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setBitsPerSample(int); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setChannelMode(int); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setCodecPriority(int); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setCodecSpecific1(long); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setCodecSpecific2(long); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setCodecSpecific3(long); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setCodecSpecific4(long); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setCodecType(int); + method @NonNull public android.bluetooth.BluetoothCodecConfig.Builder setSampleRate(int); + } + + public final class BluetoothCodecStatus implements android.os.Parcelable { + ctor public BluetoothCodecStatus(@Nullable android.bluetooth.BluetoothCodecConfig, @Nullable java.util.List<android.bluetooth.BluetoothCodecConfig>, @Nullable java.util.List<android.bluetooth.BluetoothCodecConfig>); + method public int describeContents(); + method @Nullable public android.bluetooth.BluetoothCodecConfig getCodecConfig(); + method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getCodecsLocalCapabilities(); + method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getCodecsSelectableCapabilities(); + method public boolean isCodecConfigSelectable(@Nullable android.bluetooth.BluetoothCodecConfig); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothCodecStatus> CREATOR; + field public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; + } + public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { method public void close(); method protected void finalize(); @@ -9125,8 +9192,10 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestMtu(int); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(int, int, int); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor); + method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int writeCharacteristic(@NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[], int); + method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int writeDescriptor(@NonNull android.bluetooth.BluetoothGattDescriptor, @NonNull byte[]); field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0 field public static final int CONNECTION_PRIORITY_HIGH = 1; // 0x1 field public static final int CONNECTION_PRIORITY_LOW_POWER = 2; // 0x2 @@ -9145,11 +9214,14 @@ package android.bluetooth { public abstract class BluetoothGattCallback { ctor public BluetoothGattCallback(); - method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic); - method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int); + method @Deprecated public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic); + method public void onCharacteristicChanged(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[]); + method @Deprecated public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int); + method public void onCharacteristicRead(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[], int); method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int); method public void onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int); - method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int); + method @Deprecated public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int); + method public void onDescriptorRead(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattDescriptor, int, @NonNull byte[]); method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int); method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int); method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int); @@ -9166,20 +9238,20 @@ package android.bluetooth { method public int describeContents(); method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID); method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors(); - method public Float getFloatValue(int, int); + method @Deprecated public Float getFloatValue(int, int); method public int getInstanceId(); - method public Integer getIntValue(int, int); + method @Deprecated public Integer getIntValue(int, int); method public int getPermissions(); method public int getProperties(); method public android.bluetooth.BluetoothGattService getService(); - method public String getStringValue(int); + method @Deprecated public String getStringValue(int); method public java.util.UUID getUuid(); - method public byte[] getValue(); + method @Deprecated public byte[] getValue(); method public int getWriteType(); - method public boolean setValue(byte[]); - method public boolean setValue(int, int, int); - method public boolean setValue(int, int, int, int); - method public boolean setValue(String); + method @Deprecated public boolean setValue(byte[]); + method @Deprecated public boolean setValue(int, int, int); + method @Deprecated public boolean setValue(int, int, int, int); + method @Deprecated public boolean setValue(String); method public void setWriteType(int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattCharacteristic> CREATOR; @@ -9219,8 +9291,8 @@ package android.bluetooth { method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic(); method public int getPermissions(); method public java.util.UUID getUuid(); - method public byte[] getValue(); - method public boolean setValue(byte[]); + method @Deprecated public byte[] getValue(); + method @Deprecated public boolean setValue(byte[]); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattDescriptor> CREATOR; field public static final byte[] DISABLE_NOTIFICATION_VALUE; @@ -9247,7 +9319,8 @@ package android.bluetooth { method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]); method public android.bluetooth.BluetoothGattService getService(java.util.UUID); method public java.util.List<android.bluetooth.BluetoothGattService> getServices(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int notifyCharacteristicChanged(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothGattCharacteristic, boolean, @NonNull byte[]); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void readPhy(android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeService(android.bluetooth.BluetoothGattService); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]); @@ -9508,8 +9581,12 @@ package android.bluetooth { field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2 field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1 field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3 - field public static final int ERROR_FEATURE_NOT_SUPPORTED = 8; // 0x8 + field public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; // 0xa + field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; // 0x65 + field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; // 0x66 field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6 + field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8 + field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9 field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff field public static final int SUCCESS = 0; // 0x0 } @@ -9537,6 +9614,7 @@ package android.bluetooth.le { method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData(); method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids(); method public java.util.List<android.os.ParcelUuid> getServiceUuids(); + method @NonNull public java.util.List<android.bluetooth.le.TransportDiscoveryData> getTransportDiscoveryData(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR; } @@ -9547,6 +9625,7 @@ package android.bluetooth.le { method public android.bluetooth.le.AdvertiseData.Builder addServiceData(android.os.ParcelUuid, byte[]); method @NonNull public android.bluetooth.le.AdvertiseData.Builder addServiceSolicitationUuid(@NonNull android.os.ParcelUuid); method public android.bluetooth.le.AdvertiseData.Builder addServiceUuid(android.os.ParcelUuid); + method @NonNull public android.bluetooth.le.AdvertiseData.Builder addTransportDiscoveryData(@NonNull android.bluetooth.le.TransportDiscoveryData); method public android.bluetooth.le.AdvertiseData build(); method public android.bluetooth.le.AdvertiseData.Builder setIncludeDeviceName(boolean); method public android.bluetooth.le.AdvertiseData.Builder setIncludeTxPowerLevel(boolean); @@ -9806,6 +9885,31 @@ package android.bluetooth.le { method public android.bluetooth.le.ScanSettings.Builder setScanMode(int); } + public final class TransportBlock implements android.os.Parcelable { + ctor public TransportBlock(int, int, int, @Nullable byte[]); + method public int describeContents(); + method public int getOrgId(); + method public int getTdsFlags(); + method @Nullable public byte[] getTransportData(); + method public int getTransportDataLength(); + method @Nullable public byte[] toByteArray(); + method public int totalBytes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.TransportBlock> CREATOR; + } + + public final class TransportDiscoveryData implements android.os.Parcelable { + ctor public TransportDiscoveryData(int, @NonNull java.util.List<android.bluetooth.le.TransportBlock>); + ctor public TransportDiscoveryData(@NonNull byte[]); + method public int describeContents(); + method @NonNull public java.util.List<android.bluetooth.le.TransportBlock> getTransportBlocks(); + method public int getTransportDataType(); + method @Nullable public byte[] toByteArray(); + method public int totalBytes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.TransportDiscoveryData> CREATOR; + } + } package android.companion { @@ -27377,6 +27481,7 @@ package android.nfc.cardemulation { field public static final String CATEGORY_PAYMENT = "payment"; field public static final String EXTRA_CATEGORY = "category"; field public static final String EXTRA_SERVICE_COMPONENT = "component"; + field public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID"; field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1 field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2 field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0 @@ -31418,6 +31523,8 @@ package android.os { method @Nullable public double[] createDoubleArray(); method @Nullable public float[] createFloatArray(); method @Nullable public int[] createIntArray(); + method @Nullable public <T extends android.os.IInterface> T[] createInterfaceArray(@NonNull java.util.function.IntFunction<T[]>, @NonNull java.util.function.Function<android.os.IBinder,T>); + method @Nullable public <T extends android.os.IInterface> java.util.ArrayList<T> createInterfaceArrayList(@NonNull java.util.function.Function<android.os.IBinder,T>); method @Nullable public long[] createLongArray(); method @Nullable public String[] createStringArray(); method @Nullable public java.util.ArrayList<java.lang.String> createStringArrayList(); @@ -31430,14 +31537,15 @@ package android.os { method public int dataPosition(); method public int dataSize(); method public void enforceInterface(@NonNull String); + method public void enforceNoDataAvail(); method public boolean hasFileDescriptors(); method public boolean hasFileDescriptors(int, int); method public byte[] marshall(); method @NonNull public static android.os.Parcel obtain(); method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder); - method @Nullable public Object[] readArray(@Nullable ClassLoader); + method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader); method @Nullable public <T> T[] readArray(@Nullable ClassLoader, @NonNull Class<T>); - method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader); + method @Deprecated @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader); method @Nullable public <T> java.util.ArrayList<T> readArrayList(@Nullable ClassLoader, @NonNull Class<? extends T>); method public void readBinderArray(@NonNull android.os.IBinder[]); method public void readBinderList(@NonNull java.util.List<android.os.IBinder>); @@ -31455,28 +31563,33 @@ package android.os { method public android.os.ParcelFileDescriptor readFileDescriptor(); method public float readFloat(); method public void readFloatArray(@NonNull float[]); - method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader); + method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader); + method @Nullable public <K, V> java.util.HashMap<K,V> readHashMap(@Nullable ClassLoader, @NonNull Class<? extends K>, @NonNull Class<? extends V>); method public int readInt(); method public void readIntArray(@NonNull int[]); - method public void readList(@NonNull java.util.List, @Nullable ClassLoader); + method public <T extends android.os.IInterface> void readInterfaceArray(@NonNull T[], @NonNull java.util.function.Function<android.os.IBinder,T>); + method public <T extends android.os.IInterface> void readInterfaceList(@NonNull java.util.List<T>, @NonNull java.util.function.Function<android.os.IBinder,T>); + method @Deprecated public void readList(@NonNull java.util.List, @Nullable ClassLoader); method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>); method public long readLong(); method public void readLongArray(@NonNull long[]); - method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader); - method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader); + method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader); + method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>); + method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader); method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>); method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader); method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>); - method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader); + method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader); method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>); - method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader); + method @Deprecated @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader); + method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<T>); method @Nullable public android.os.PersistableBundle readPersistableBundle(); method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader); - method @Nullable public java.io.Serializable readSerializable(); + method @Deprecated @Nullable public java.io.Serializable readSerializable(); method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>); method @NonNull public android.util.Size readSize(); method @NonNull public android.util.SizeF readSizeF(); - method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader); + method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader); method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader, @NonNull Class<? extends T>); method @Nullable public android.util.SparseBooleanArray readSparseBooleanArray(); method @Nullable public String readString(); @@ -31510,6 +31623,8 @@ package android.os { method public void writeFloatArray(@Nullable float[]); method public void writeInt(int); method public void writeIntArray(@Nullable int[]); + method public <T extends android.os.IInterface> void writeInterfaceArray(@Nullable T[]); + method public <T extends android.os.IInterface> void writeInterfaceList(@Nullable java.util.List<T>); method public void writeInterfaceToken(@NonNull String); method public void writeList(@Nullable java.util.List); method public void writeLong(long); @@ -40687,6 +40802,7 @@ package android.telephony { method @NonNull public java.util.List<java.lang.Integer> getBands(); method @NonNull public java.util.List<java.lang.String> getMccMncs(); method public int getPriority(); + method @NonNull public java.util.List<android.telephony.RadioAccessSpecifier> getRadioAccessSpecifiers(); method public int getSubId(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.AvailableNetworkInfo> CREATOR; @@ -40695,6 +40811,14 @@ package android.telephony { field public static final int PRIORITY_MED = 2; // 0x2 } + public static final class AvailableNetworkInfo.Builder { + ctor public AvailableNetworkInfo.Builder(int); + method @NonNull public android.telephony.AvailableNetworkInfo build(); + method @NonNull public android.telephony.AvailableNetworkInfo.Builder setMccMncs(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.telephony.AvailableNetworkInfo.Builder setPriority(int); + method @NonNull public android.telephony.AvailableNetworkInfo.Builder setRadioAccessSpecifiers(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>); + } + public final class BarringInfo implements android.os.Parcelable { method public int describeContents(); method @NonNull public android.telephony.BarringInfo.BarringServiceInfo getBarringServiceInfo(int); @@ -40728,11 +40852,11 @@ package android.telephony { } public class CarrierConfigManager { - method @Nullable public android.os.PersistableBundle getConfig(); - method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(@NonNull String, int); - method @Nullable public android.os.PersistableBundle getConfigForSubId(int); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfig(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfigByComponentForSubId(@NonNull String, int); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfigForSubId(int); method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle); - method public void notifyConfigChangedForSubId(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyConfigChangedForSubId(int); field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1 field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2 @@ -40803,6 +40927,7 @@ package android.telephony { field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool"; field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string"; field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; + field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool"; field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool"; field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool"; field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int"; @@ -40864,6 +40989,8 @@ package android.telephony { field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool"; field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool"; field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int"; + field public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT = "esim_download_retry_backoff_timer_sec_int"; + field public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT = "esim_max_download_retry_attempts_int"; field public static final String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool"; field public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int"; field public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array"; @@ -40966,6 +41093,7 @@ package android.telephony { field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool"; field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; + field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool"; @@ -42662,11 +42790,11 @@ package android.telephony { method public int getCarrierIdFromSimMccMnc(); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation(); method public int getDataActivity(); - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getDataNetworkType(); + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public int getDataNetworkType(); method public int getDataState(); method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDeviceId(); method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDeviceId(int); - method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion(); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public String getDeviceSoftwareVersion(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getEmergencyNumberList(int); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<java.lang.String> getEquivalentHomePlmns(); @@ -42719,26 +42847,26 @@ package android.telephony { method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVisualVoicemailPackageName(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(); - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getVoiceNetworkType(); + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public int getVoiceNetworkType(); method @Nullable public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle); method public boolean hasCarrierPrivileges(); method public boolean hasIccCard(); - method @Deprecated public boolean iccCloseLogicalChannel(int); - method @Deprecated public byte[] iccExchangeSimIO(int, int, int, int, int, String); + method public boolean iccCloseLogicalChannel(int); + method public byte[] iccExchangeSimIO(int, int, int, int, int, String); method @Deprecated public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String); - method @Deprecated public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String, int); - method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String); - method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String); + method public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannel(String, int); + method public String iccTransmitApduBasicChannel(int, int, int, int, int, String); + method public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String); method public boolean isConcurrentVoiceAndDataSupported(); method public boolean isDataCapable(); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed(); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled(); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int); - method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled(); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataConnectionAllowed(); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabled(); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabledForReason(int); + method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataRoamingEnabled(); method public boolean isEmergencyNumber(@NonNull String); method public boolean isHearingAidCompatibilitySupported(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed(); - method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isModemEnabledForSlot(int); + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isModemEnabledForSlot(int); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported(); method public boolean isNetworkRoaming(); method public boolean isRadioInterfaceCapabilitySupported(@NonNull String); @@ -42753,7 +42881,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback); method public void sendDialerSpecialCode(String); - method @Deprecated public String sendEnvelopeWithStatus(String); + method public String sendEnvelopeWithStatus(String); method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallComposerStatus(int); @@ -43444,8 +43572,10 @@ package android.telephony.ims { method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVtSettingEnabled(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback); field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1 field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0 @@ -43462,7 +43592,9 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter(); method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; } @@ -43661,6 +43793,19 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR; } + public abstract class ImsStateCallback { + ctor public ImsStateCallback(); + method public abstract void onAvailable(); + method public abstract void onError(); + method public abstract void onUnavailable(int); + field public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; // 0x3 + field public static final int REASON_IMS_SERVICE_NOT_READY = 6; // 0x6 + field public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; // 0x4 + field public static final int REASON_SUBSCRIPTION_INACTIVE = 5; // 0x5 + field public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; // 0x2 + field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1 + } + public class RcsUceAdapter { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException; } @@ -47514,6 +47659,10 @@ package android.view { field public static final int KEYCODE_CUT = 277; // 0x115 field public static final int KEYCODE_D = 32; // 0x20 field public static final int KEYCODE_DEL = 67; // 0x43 + field public static final int KEYCODE_DEMO_APP_1 = 301; // 0x12d + field public static final int KEYCODE_DEMO_APP_2 = 302; // 0x12e + field public static final int KEYCODE_DEMO_APP_3 = 303; // 0x12f + field public static final int KEYCODE_DEMO_APP_4 = 304; // 0x130 field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17 field public static final int KEYCODE_DPAD_DOWN = 20; // 0x14 field public static final int KEYCODE_DPAD_DOWN_LEFT = 269; // 0x10d @@ -47545,6 +47694,10 @@ package android.view { field public static final int KEYCODE_F7 = 137; // 0x89 field public static final int KEYCODE_F8 = 138; // 0x8a field public static final int KEYCODE_F9 = 139; // 0x8b + field public static final int KEYCODE_FEATURED_APP_1 = 297; // 0x129 + field public static final int KEYCODE_FEATURED_APP_2 = 298; // 0x12a + field public static final int KEYCODE_FEATURED_APP_3 = 299; // 0x12b + field public static final int KEYCODE_FEATURED_APP_4 = 300; // 0x12c field public static final int KEYCODE_FOCUS = 80; // 0x50 field public static final int KEYCODE_FORWARD = 125; // 0x7d field public static final int KEYCODE_FORWARD_DEL = 112; // 0x70 @@ -47710,6 +47863,14 @@ package android.view { field public static final int KEYCODE_U = 49; // 0x31 field public static final int KEYCODE_UNKNOWN = 0; // 0x0 field public static final int KEYCODE_V = 50; // 0x32 + field public static final int KEYCODE_VIDEO_APP_1 = 289; // 0x121 + field public static final int KEYCODE_VIDEO_APP_2 = 290; // 0x122 + field public static final int KEYCODE_VIDEO_APP_3 = 291; // 0x123 + field public static final int KEYCODE_VIDEO_APP_4 = 292; // 0x124 + field public static final int KEYCODE_VIDEO_APP_5 = 293; // 0x125 + field public static final int KEYCODE_VIDEO_APP_6 = 294; // 0x126 + field public static final int KEYCODE_VIDEO_APP_7 = 295; // 0x127 + field public static final int KEYCODE_VIDEO_APP_8 = 296; // 0x128 field public static final int KEYCODE_VOICE_ASSIST = 231; // 0xe7 field public static final int KEYCODE_VOLUME_DOWN = 25; // 0x19 field public static final int KEYCODE_VOLUME_MUTE = 164; // 0xa4 diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 49df45ac0c4f..3a35f249d177 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -113,10 +113,29 @@ package android.media { public class AudioManager { method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpSamplingRate(int); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpVolume(int); method public void setStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); field public static final int FLAG_FROM_KEY = 4096; // 0x1000 } + public final class BtProfileConnectionInfo implements android.os.Parcelable { + method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int); + method public int describeContents(); + method public boolean getIsLeOutput(); + method public int getProfile(); + method public boolean getSuppressNoisyIntent(); + method public int getVolume(); + method @NonNull public static android.media.BtProfileConnectionInfo hearingAidInfo(boolean); + method @NonNull public static android.media.BtProfileConnectionInfo leAudio(boolean, boolean); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.media.BtProfileConnectionInfo> CREATOR; + } + public class MediaMetadataRetriever implements java.lang.AutoCloseable { field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28 } @@ -292,6 +311,10 @@ package android.os { method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException; } + public class SystemConfigManager { + method @NonNull public java.util.List<android.content.ComponentName> getEnabledComponentOverrides(@NonNull String); + } + } package android.os.storage { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ac6943c3915e..91b8786cc615 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -8581,7 +8581,6 @@ package android.os { public class SystemConfigManager { method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); - method @NonNull public java.util.List<java.lang.String> getEnabledComponentOverrides(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public int[] getSystemPermissionUids(@NonNull String); } @@ -12056,10 +12055,10 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoiceActivationState(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmi(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handlePinMmiForSubscriber(int, String); - method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean iccCloseLogicalChannelBySlot(int, int); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int); - method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String); - method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String); + method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int); @@ -13742,7 +13741,9 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); field public static final int DENIED_REASON_INVALID = 4; // 0x4 field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1 field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 6ccdf9185517..d0e659bf36e9 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2748,7 +2748,7 @@ package android.view { method public static String actionToString(int); method public final void setDisplayId(int); field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800 - field public static final int LAST_KEYCODE = 288; // 0x120 + field public static final int LAST_KEYCODE = 304; // 0x130 } public final class KeyboardShortcutGroup implements android.os.Parcelable { diff --git a/core/java/Android.bp b/core/java/Android.bp index e08a4931fc7d..ca9a46847dea 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -343,7 +343,6 @@ aidl_interface { filegroup { name: "framework-telephony-common-shared-srcs", srcs: [ - ":modules-utils-preconditions-srcs", "android/os/RegistrantList.java", "android/os/Registrant.java", "android/util/IndentingPrintWriter.java", @@ -355,10 +354,7 @@ filegroup { "com/android/internal/util/BitwiseInputStream.java", "com/android/internal/util/FastXmlSerializer.java", "com/android/internal/util/HexDump.java", - "com/android/internal/util/IState.java", "com/android/internal/util/IndentingPrintWriter.java", - "com/android/internal/util/State.java", - "com/android/internal/util/StateMachine.java", "com/android/internal/util/UserIcons.java", ], } diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index 0d6a07938e95..e6cdcc0ee742 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -31,6 +31,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; +import java.util.Objects; import java.util.Set; /** @@ -86,6 +87,12 @@ public class Account implements Parcelable { if (TextUtils.isEmpty(type)) { throw new IllegalArgumentException("the type must not be empty: " + type); } + if (name.length() > 200) { + throw new IllegalArgumentException("account name is longer than 200 characters"); + } + if (type.length() > 200) { + throw new IllegalArgumentException("account type is longer than 200 characters"); + } this.name = name; this.type = type; this.accessId = accessId; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index db5dcc5c264b..af59ea1d22ff 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -77,7 +77,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.Parcelable; import android.os.PersistableBundle; -import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager.ServiceNotFoundException; @@ -8788,9 +8787,7 @@ public class Activity extends ContextThemeWrapper * the activity is visible after the screen is turned on when the lockscreen is up. In addition, * if this flag is set and the activity calls {@link * KeyguardManager#requestDismissKeyguard(Activity, KeyguardManager.KeyguardDismissCallback)} - * the screen will turn on. If the screen is off and device is not secured, this flag can turn - * screen on and dismiss keyguard to make this activity visible and resume, which can be used to - * replace {@link PowerManager#ACQUIRE_CAUSES_WAKEUP} + * the screen will turn on. * * @param turnScreenOn {@code true} to turn on the screen; {@code false} otherwise. * diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 6c3795d7c607..bfff27a82f5b 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -464,11 +464,7 @@ public final class ActivityThread extends ClientTransactionHandler @Override public int hashCode() { - return hashCode(authority, userId); - } - - public static int hashCode(final String auth, final int userIdent) { - return ((auth != null) ? auth.hashCode() : 0) ^ userIdent; + return ((authority != null) ? authority.hashCode() : 0) ^ userId; } } @@ -490,7 +486,7 @@ public final class ActivityThread extends ClientTransactionHandler // Note we never removes items from this map but that's okay because there are only so many // users and so many authorities. @GuardedBy("mGetProviderKeys") - final SparseArray<ProviderKey> mGetProviderKeys = new SparseArray<>(); + final ArrayMap<ProviderKey, ProviderKey> mGetProviderKeys = new ArrayMap<>(); final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>(); @@ -7020,11 +7016,11 @@ public final class ActivityThread extends ClientTransactionHandler } private ProviderKey getGetProviderKey(String auth, int userId) { - final int key = ProviderKey.hashCode(auth, userId); + final ProviderKey key = new ProviderKey(auth, userId); synchronized (mGetProviderKeys) { ProviderKey lock = mGetProviderKeys.get(key); if (lock == null) { - lock = new ProviderKey(auth, userId); + lock = key; mGetProviderKeys.put(key, lock); } return lock; diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index 4da51c13045b..2564228f23aa 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -29,6 +29,8 @@ per-file Service* = file:/services/core/java/com/android/server/am/OWNERS per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS per-file UiAutomation.java = file:/services/accessibility/OWNERS +per-file GameManager* = file:/GAME_MANAGER_OWNERS +per-file IGameManager* = file:/GAME_MANAGER_OWNERS # ActivityThread per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 8aa27853b462..51435796502e 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -552,6 +552,11 @@ public class AppWidgetHostView extends FrameLayout { inflateAsync(rvToApply); return; } + + // Prepare a local reference to the remote Context so we're ready to + // inflate any requested LayoutParams + mRemoteContext = getRemoteContext(); + int layoutId = rvToApply.getLayoutId(); if (rvToApply.canRecycleView(mView)) { try { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index dac8ffe5e008..2c875fee101b 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -51,7 +51,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; -import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.IBinder; @@ -59,7 +58,6 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; -import android.os.SynchronousResultReceiver; import android.os.SystemProperties; import android.util.Log; import android.util.Pair; @@ -82,7 +80,6 @@ import java.util.Set; import java.util.UUID; import java.util.WeakHashMap; import java.util.concurrent.Executor; -import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -2022,6 +2019,7 @@ public final class BluetoothAdapter { * {@link BluetoothProfile#HEADSET}, * {@link BluetoothProfile#A2DP}, * {@link BluetoothProfile#HEARING_AID} + * {@link BluetoothProfile#LE_AUDIO} * @return A list of active bluetooth devices * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile} * @hide @@ -2034,12 +2032,14 @@ public final class BluetoothAdapter { public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) { if (profile != BluetoothProfile.HEADSET && profile != BluetoothProfile.A2DP - && profile != BluetoothProfile.HEARING_AID) { + && profile != BluetoothProfile.HEARING_AID + && profile != BluetoothProfile.LE_AUDIO) { Log.e(TAG, "Invalid profile param value in getActiveDevices"); throw new IllegalArgumentException("Profiles must be one of " + "BluetoothProfile.A2DP, " + "BluetoothProfile.HEARING_AID, or" - + "BluetoothProfile.HEARING_AID"); + + "BluetoothProfile.HEARING_AID" + + "BluetoothProfile.LE_AUDIO"); } try { mServiceLock.readLock().lock(); @@ -2421,38 +2421,6 @@ public final class BluetoothAdapter { } /** - * Return the record of {@link BluetoothActivityEnergyInfo} object that - * has the activity and energy info. This can be used to ascertain what - * the controller has been up to, since the last sample. - * - * @param updateType Type of info, cached vs refreshed. - * @return a record with {@link BluetoothActivityEnergyInfo} or null if report is unavailable or - * unsupported - * @hide - * @deprecated use the asynchronous {@link #requestControllerActivityEnergyInfo(ResultReceiver)} - * instead. - */ - @Deprecated - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { - SynchronousResultReceiver receiver = new SynchronousResultReceiver(); - requestControllerActivityEnergyInfo(receiver); - try { - SynchronousResultReceiver.Result result = receiver.awaitResult(1000); - if (result.bundle != null) { - return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY); - } - } catch (TimeoutException e) { - Log.e(TAG, "getControllerActivityEnergyInfo timed out"); - } - return null; - } - - /** * Request the record of {@link BluetoothActivityEnergyInfo} object that * has the activity and energy info. This can be used to ascertain what * the controller has been up to, since the last sample. diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 1d0bf97c34eb..9a4151adffc7 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -29,16 +29,14 @@ import java.util.Objects; /** * Represents the codec configuration for a Bluetooth A2DP source device. + * <p>Contains the source codec type, the codec priority, the codec sample + * rate, the codec bits per sample, and the codec channel mode. + * <p>The source codec type values are the same as those supported by the + * device hardware. * * {@see BluetoothA2dp} - * - * {@hide} */ public final class BluetoothCodecConfig implements Parcelable { - // Add an entry for each source codec here. - // NOTE: The values should be same as those listed in the following file: - // hardware/libhardware/include/hardware/bt_av.h - /** @hide */ @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = { SOURCE_CODEC_TYPE_SBC, @@ -46,33 +44,49 @@ public final class BluetoothCodecConfig implements Parcelable { SOURCE_CODEC_TYPE_APTX, SOURCE_CODEC_TYPE_APTX_HD, SOURCE_CODEC_TYPE_LDAC, - SOURCE_CODEC_TYPE_MAX, SOURCE_CODEC_TYPE_INVALID }) @Retention(RetentionPolicy.SOURCE) public @interface SourceCodecType {} - @UnsupportedAppUsage + /** + * Source codec type SBC. This is the mandatory source codec + * type. + */ public static final int SOURCE_CODEC_TYPE_SBC = 0; - @UnsupportedAppUsage + /** + * Source codec type AAC. + */ public static final int SOURCE_CODEC_TYPE_AAC = 1; - @UnsupportedAppUsage + /** + * Source codec type APTX. + */ public static final int SOURCE_CODEC_TYPE_APTX = 2; - @UnsupportedAppUsage + /** + * Source codec type APTX HD. + */ public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; - @UnsupportedAppUsage + /** + * Source codec type LDAC. + */ public static final int SOURCE_CODEC_TYPE_LDAC = 4; - @UnsupportedAppUsage - public static final int SOURCE_CODEC_TYPE_MAX = 5; - - @UnsupportedAppUsage + /** + * Source codec type invalid. This is the default value used for codec + * type. + */ public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; + /** + * Represents the count of valid source codec types. Can be accessed via + * {@link #getMaxCodecType}. + */ + private static final int SOURCE_CODEC_TYPE_MAX = 5; + /** @hide */ @IntDef(prefix = "CODEC_PRIORITY_", value = { CODEC_PRIORITY_DISABLED, @@ -82,16 +96,24 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface CodecPriority {} - @UnsupportedAppUsage + /** + * Codec priority disabled. + * Used to indicate that this codec is disabled and should not be used. + */ public static final int CODEC_PRIORITY_DISABLED = -1; - @UnsupportedAppUsage + /** + * Codec priority default. + * Default value used for codec priority. + */ public static final int CODEC_PRIORITY_DEFAULT = 0; - @UnsupportedAppUsage + /** + * Codec priority highest. + * Used to indicate the highest priority a codec can have. + */ public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; - /** @hide */ @IntDef(prefix = "SAMPLE_RATE_", value = { SAMPLE_RATE_NONE, @@ -105,28 +127,42 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface SampleRate {} - @UnsupportedAppUsage + /** + * Codec sample rate 0 Hz. Default value used for + * codec sample rate. + */ public static final int SAMPLE_RATE_NONE = 0; - @UnsupportedAppUsage + /** + * Codec sample rate 44100 Hz. + */ public static final int SAMPLE_RATE_44100 = 0x1 << 0; - @UnsupportedAppUsage + /** + * Codec sample rate 48000 Hz. + */ public static final int SAMPLE_RATE_48000 = 0x1 << 1; - @UnsupportedAppUsage + /** + * Codec sample rate 88200 Hz. + */ public static final int SAMPLE_RATE_88200 = 0x1 << 2; - @UnsupportedAppUsage + /** + * Codec sample rate 96000 Hz. + */ public static final int SAMPLE_RATE_96000 = 0x1 << 3; - @UnsupportedAppUsage + /** + * Codec sample rate 176400 Hz. + */ public static final int SAMPLE_RATE_176400 = 0x1 << 4; - @UnsupportedAppUsage + /** + * Codec sample rate 192000 Hz. + */ public static final int SAMPLE_RATE_192000 = 0x1 << 5; - /** @hide */ @IntDef(prefix = "BITS_PER_SAMPLE_", value = { BITS_PER_SAMPLE_NONE, @@ -137,19 +173,27 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface BitsPerSample {} - @UnsupportedAppUsage + /** + * Codec bits per sample 0. Default value of the codec + * bits per sample. + */ public static final int BITS_PER_SAMPLE_NONE = 0; - @UnsupportedAppUsage + /** + * Codec bits per sample 16. + */ public static final int BITS_PER_SAMPLE_16 = 0x1 << 0; - @UnsupportedAppUsage + /** + * Codec bits per sample 24. + */ public static final int BITS_PER_SAMPLE_24 = 0x1 << 1; - @UnsupportedAppUsage + /** + * Codec bits per sample 32. + */ public static final int BITS_PER_SAMPLE_32 = 0x1 << 2; - /** @hide */ @IntDef(prefix = "CHANNEL_MODE_", value = { CHANNEL_MODE_NONE, @@ -159,13 +203,20 @@ public final class BluetoothCodecConfig implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ChannelMode {} - @UnsupportedAppUsage + /** + * Codec channel mode NONE. Default value of the + * codec channel mode. + */ public static final int CHANNEL_MODE_NONE = 0; - @UnsupportedAppUsage + /** + * Codec channel mode MONO. + */ public static final int CHANNEL_MODE_MONO = 0x1 << 0; - @UnsupportedAppUsage + /** + * Codec channel mode STEREO. + */ public static final int CHANNEL_MODE_STEREO = 0x1 << 1; private final @SourceCodecType int mCodecType; @@ -178,6 +229,21 @@ public final class BluetoothCodecConfig implements Parcelable { private final long mCodecSpecific3; private final long mCodecSpecific4; + /** + * Creates a new BluetoothCodecConfig. + * + * @param codecType the source codec type + * @param codecPriority the priority of this codec + * @param sampleRate the codec sample rate + * @param bitsPerSample the bits per sample of this codec + * @param channelMode the channel mode of this codec + * @param codecSpecific1 the specific value 1 + * @param codecSpecific2 the specific value 2 + * @param codecSpecific3 the specific value 3 + * @param codecSpecific4 the specific value 4 + * values to 0. + * @hide + */ @UnsupportedAppUsage public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority, @SampleRate int sampleRate, @BitsPerSample int bitsPerSample, @@ -195,17 +261,34 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific4 = codecSpecific4; } - @UnsupportedAppUsage + /** + * Creates a new BluetoothCodecConfig. + * <p> By default, the codec priority will be set + * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to + * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to + * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to + * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific + * values to 0. + * + * @param codecType the source codec type + */ public BluetoothCodecConfig(@SourceCodecType int codecType) { - mCodecType = codecType; - mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; - mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE; - mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; - mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE; - mCodecSpecific1 = 0; - mCodecSpecific2 = 0; - mCodecSpecific3 = 0; - mCodecSpecific4 = 0; + this(codecType, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, + BluetoothCodecConfig.SAMPLE_RATE_NONE, + BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, + BluetoothCodecConfig.CHANNEL_MODE_NONE, 0, 0, 0, 0); + } + + private BluetoothCodecConfig(Parcel in) { + mCodecType = in.readInt(); + mCodecPriority = in.readInt(); + mSampleRate = in.readInt(); + mBitsPerSample = in.readInt(); + mChannelMode = in.readInt(); + mCodecSpecific1 = in.readLong(); + mCodecSpecific2 = in.readLong(); + mCodecSpecific3 = in.readLong(); + mCodecSpecific4 = in.readLong(); } @Override @@ -226,10 +309,8 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a hash based on the config values - * - * @return a hash based on the config values - * @hide + * Returns a hash representation of this BluetoothCodecConfig + * based on all the config values. */ @Override public int hashCode() { @@ -239,32 +320,24 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the object contains valid codec configuration. - * - * @return true if the object contains valid codec configuration, otherwise false. - * @hide - */ - public boolean isValid() { - return (mSampleRate != SAMPLE_RATE_NONE) - && (mBitsPerSample != BITS_PER_SAMPLE_NONE) - && (mChannelMode != CHANNEL_MODE_NONE); - } - - /** * Adds capability string to an existing string. * - * @param prevStr the previous string with the capabilities. Can be a null pointer. - * @param capStr the capability string to append to prevStr argument. - * @return the result string in the form "prevStr|capStr". + * @param prevStr the previous string with the capabilities. Can be a {@code null} pointer + * @param capStr the capability string to append to prevStr argument + * @return the result string in the form "prevStr|capStr" */ - private static String appendCapabilityToString(String prevStr, - String capStr) { + private static String appendCapabilityToString(@Nullable String prevStr, + @NonNull String capStr) { if (prevStr == null) { return capStr; } return prevStr + "|" + capStr; } + /** + * Returns a {@link String} that describes each BluetoothCodecConfig parameter + * current value. + */ @Override public String toString() { String sampleRateStr = null; @@ -331,8 +404,6 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Always returns 0 - * * @return 0 * @hide */ @@ -344,20 +415,7 @@ public final class BluetoothCodecConfig implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecConfig> CREATOR = new Parcelable.Creator<BluetoothCodecConfig>() { public BluetoothCodecConfig createFromParcel(Parcel in) { - final int codecType = in.readInt(); - final int codecPriority = in.readInt(); - final int sampleRate = in.readInt(); - final int bitsPerSample = in.readInt(); - final int channelMode = in.readInt(); - final long codecSpecific1 = in.readLong(); - final long codecSpecific2 = in.readLong(); - final long codecSpecific3 = in.readLong(); - final long codecSpecific4 = in.readLong(); - return new BluetoothCodecConfig(codecType, codecPriority, - sampleRate, bitsPerSample, - channelMode, codecSpecific1, - codecSpecific2, codecSpecific3, - codecSpecific4); + return new BluetoothCodecConfig(in); } public BluetoothCodecConfig[] newArray(int size) { @@ -368,8 +426,8 @@ public final class BluetoothCodecConfig implements Parcelable { /** * Flattens the object to a parcel * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. + * @param out The Parcel in which the object should be written + * @param flags Additional flags about how the object should be written * * @hide */ @@ -387,9 +445,8 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Gets the codec name. - * - * @return the codec name + * Returns the codec name converted to {@link String}. + * @hide */ public @NonNull String getCodecName() { switch (mCodecType) { @@ -412,137 +469,100 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Gets the codec type. - * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}. - * - * @return the codec type + * Returns the source codec type of this config. */ - @UnsupportedAppUsage public @SourceCodecType int getCodecType() { return mCodecType; } /** + * Returns the valid codec types count. + */ + public static int getMaxCodecType() { + return SOURCE_CODEC_TYPE_MAX; + } + + /** * Checks whether the codec is mandatory. + * <p> The actual mandatory codec type for Android Bluetooth audio is SBC. + * See {@link #SOURCE_CODEC_TYPE_SBC}. * - * @return true if the codec is mandatory, otherwise false. + * @return {@code true} if the codec is mandatory, {@code false} otherwise + * @hide */ public boolean isMandatoryCodec() { return mCodecType == SOURCE_CODEC_TYPE_SBC; } /** - * Gets the codec selection priority. - * The codec selection priority is relative to other codecs: larger value - * means higher priority. If 0, reset to default. - * - * @return the codec priority + * Returns the codec selection priority. + * <p>The codec selection priority is relative to other codecs: larger value + * means higher priority. */ - @UnsupportedAppUsage public @CodecPriority int getCodecPriority() { return mCodecPriority; } /** * Sets the codec selection priority. - * The codec selection priority is relative to other codecs: larger value - * means higher priority. If 0, reset to default. + * <p>The codec selection priority is relative to other codecs: larger value + * means higher priority. * - * @param codecPriority the codec priority + * @param codecPriority the priority this codec should have * @hide */ - @UnsupportedAppUsage public void setCodecPriority(@CodecPriority int codecPriority) { mCodecPriority = codecPriority; } /** - * Gets the codec sample rate. The value can be a bitmask with all - * supported sample rates: - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_48000} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_88200} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_96000} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_176400} or - * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_192000} - * - * @return the codec sample rate + * Returns the codec sample rate. The value can be a bitmask with all + * supported sample rates. */ - @UnsupportedAppUsage public @SampleRate int getSampleRate() { return mSampleRate; } /** - * Gets the codec bits per sample. The value can be a bitmask with all - * bits per sample supported: - * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or - * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or - * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_24} or - * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_32} - * - * @return the codec bits per sample + * Returns the codec bits per sample. The value can be a bitmask with all + * bits per sample supported. */ - @UnsupportedAppUsage public @BitsPerSample int getBitsPerSample() { return mBitsPerSample; } /** - * Gets the codec channel mode. The value can be a bitmask with all - * supported channel modes: - * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or - * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or - * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO} - * - * @return the codec channel mode - * @hide + * Returns the codec channel mode. The value can be a bitmask with all + * supported channel modes. */ - @UnsupportedAppUsage public @ChannelMode int getChannelMode() { return mChannelMode; } /** - * Gets a codec specific value1. - * - * @return a codec specific value1. + * Returns the codec specific value1. */ - @UnsupportedAppUsage public long getCodecSpecific1() { return mCodecSpecific1; } /** - * Gets a codec specific value2. - * - * @return a codec specific value2 - * @hide + * Returns the codec specific value2. */ - @UnsupportedAppUsage public long getCodecSpecific2() { return mCodecSpecific2; } /** - * Gets a codec specific value3. - * - * @return a codec specific value3 - * @hide + * Returns the codec specific value3. */ - @UnsupportedAppUsage public long getCodecSpecific3() { return mCodecSpecific3; } /** - * Gets a codec specific value4. - * - * @return a codec specific value4 - * @hide + * Returns the codec specific value4. */ - @UnsupportedAppUsage public long getCodecSpecific4() { return mCodecSpecific4; } @@ -551,7 +571,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Checks whether a value set presented by a bitmask has zero or single bit * * @param valueSet the value set presented by a bitmask - * @return true if the valueSet contains zero or single bit, otherwise false. + * @return {@code true} if the valueSet contains zero or single bit, {@code false} otherwise * @hide */ private static boolean hasSingleBit(int valueSet) { @@ -559,9 +579,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the object contains none or single sample rate. - * - * @return true if the object contains none or single sample rate, otherwise false. + * Returns whether the object contains none or single sample rate. * @hide */ public boolean hasSingleSampleRate() { @@ -569,9 +587,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the object contains none or single bits per sample. - * - * @return true if the object contains none or single bits per sample, otherwise false. + * Returns whether the object contains none or single bits per sample. * @hide */ public boolean hasSingleBitsPerSample() { @@ -579,9 +595,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the object contains none or single channel mode. - * - * @return true if the object contains none or single channel mode, otherwise false. + * Returns whether the object contains none or single channel mode. * @hide */ public boolean hasSingleChannelMode() { @@ -589,10 +603,10 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Checks whether the audio feeding parameters are same. + * Checks whether the audio feeding parameters are the same. * * @param other the codec config to compare against - * @return true if the audio feeding parameters are same, otherwise false + * @return {@code true} if the audio feeding parameters are same, {@code false} otherwise * @hide */ public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) { @@ -606,7 +620,7 @@ public final class BluetoothCodecConfig implements Parcelable { * Any parameters with NONE value will be considered to be a wildcard matching. * * @param other the codec config to compare against - * @return true if the audio feeding parameters are similar, otherwise false. + * @return {@code true} if the audio feeding parameters are similar, {@code false} otherwise * @hide */ public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { @@ -614,18 +628,18 @@ public final class BluetoothCodecConfig implements Parcelable { return false; } int sampleRate = other.mSampleRate; - if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE - || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { + if (mSampleRate == SAMPLE_RATE_NONE + || sampleRate == SAMPLE_RATE_NONE) { sampleRate = mSampleRate; } int bitsPerSample = other.mBitsPerSample; - if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE - || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + if (mBitsPerSample == BITS_PER_SAMPLE_NONE + || bitsPerSample == BITS_PER_SAMPLE_NONE) { bitsPerSample = mBitsPerSample; } int channelMode = other.mChannelMode; - if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE - || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { + if (mChannelMode == CHANNEL_MODE_NONE + || channelMode == CHANNEL_MODE_NONE) { channelMode = mChannelMode; } return sameAudioFeedingParameters(new BluetoothCodecConfig( @@ -636,25 +650,158 @@ public final class BluetoothCodecConfig implements Parcelable { /** * Checks whether the codec specific parameters are the same. + * <p> Currently, only AAC VBR and LDAC Playback Quality on CodecSpecific1 + * are compared. * * @param other the codec config to compare against - * @return true if the codec specific parameters are the same, otherwise false. + * @return {@code true} if the codec specific parameters are the same, {@code false} otherwise * @hide */ public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { if (other == null && mCodecType != other.mCodecType) { return false; } - // Currently we only care about the AAC VBR and LDAC Playback Quality at CodecSpecific1 switch (mCodecType) { case SOURCE_CODEC_TYPE_AAC: case SOURCE_CODEC_TYPE_LDAC: if (mCodecSpecific1 != other.mCodecSpecific1) { return false; } - // fall through default: return true; } } + + /** + * Builder for {@link BluetoothCodecConfig}. + * <p> By default, the codec type will be set to + * {@link BluetoothCodecConfig#SOURCE_CODEC_TYPE_INVALID}, the codec priority + * to {@link BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to + * {@link BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to + * {@link BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to + * {@link BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific + * values to 0. + */ + public static final class Builder { + private int mCodecType = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID; + private int mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; + private int mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE; + private int mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; + private int mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE; + private long mCodecSpecific1 = 0; + private long mCodecSpecific2 = 0; + private long mCodecSpecific3 = 0; + private long mCodecSpecific4 = 0; + + /** + * Set codec type for Bluetooth codec config. + * + * @param codecType of this codec + * @return the same Builder instance + */ + public @NonNull Builder setCodecType(@SourceCodecType int codecType) { + mCodecType = codecType; + return this; + } + + /** + * Set codec priority for Bluetooth codec config. + * + * @param codecPriority of this codec + * @return the same Builder instance + */ + public @NonNull Builder setCodecPriority(@CodecPriority int codecPriority) { + mCodecPriority = codecPriority; + return this; + } + + /** + * Set sample rate for Bluetooth codec config. + * + * @param sampleRate of this codec + * @return the same Builder instance + */ + public @NonNull Builder setSampleRate(@SampleRate int sampleRate) { + mSampleRate = sampleRate; + return this; + } + + /** + * Set the bits per sample for Bluetooth codec config. + * + * @param bitsPerSample of this codec + * @return the same Builder instance + */ + public @NonNull Builder setBitsPerSample(@BitsPerSample int bitsPerSample) { + mBitsPerSample = bitsPerSample; + return this; + } + + /** + * Set the channel mode for Bluetooth codec config. + * + * @param channelMode of this codec + * @return the same Builder instance + */ + public @NonNull Builder setChannelMode(@ChannelMode int channelMode) { + mChannelMode = channelMode; + return this; + } + + /** + * Set the first codec specific values for Bluetooth codec config. + * + * @param codecSpecific1 codec specific value or 0 if default + * @return the same Builder instance + */ + public @NonNull Builder setCodecSpecific1(long codecSpecific1) { + mCodecSpecific1 = codecSpecific1; + return this; + } + + /** + * Set the second codec specific values for Bluetooth codec config. + * + * @param codecSpecific2 codec specific value or 0 if default + * @return the same Builder instance + */ + public @NonNull Builder setCodecSpecific2(long codecSpecific2) { + mCodecSpecific2 = codecSpecific2; + return this; + } + + /** + * Set the third codec specific values for Bluetooth codec config. + * + * @param codecSpecific3 codec specific value or 0 if default + * @return the same Builder instance + */ + public @NonNull Builder setCodecSpecific3(long codecSpecific3) { + mCodecSpecific3 = codecSpecific3; + return this; + } + + /** + * Set the fourth codec specific values for Bluetooth codec config. + * + * @param codecSpecific4 codec specific value or 0 if default + * @return the same Builder instance + */ + public @NonNull Builder setCodecSpecific4(long codecSpecific4) { + mCodecSpecific4 = codecSpecific4; + return this; + } + + /** + * Build {@link BluetoothCodecConfig}. + * @return new BluetoothCodecConfig built + */ + public @NonNull BluetoothCodecConfig build() { + return new BluetoothCodecConfig(mCodecType, mCodecPriority, + mSampleRate, mBitsPerSample, + mChannelMode, mCodecSpecific1, + mCodecSpecific2, mCodecSpecific3, + mCodecSpecific4); + } + } } diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 7764ebeb2e33..02606feb3b3f 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -16,12 +16,13 @@ package android.bluetooth; +import android.annotation.NonNull; import android.annotation.Nullable; -import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -29,8 +30,6 @@ import java.util.Objects; * A2DP source device. * * {@see BluetoothA2dp} - * - * {@hide} */ public final class BluetoothCodecStatus implements Parcelable { /** @@ -39,22 +38,27 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; private final @Nullable BluetoothCodecConfig mCodecConfig; - private final BluetoothCodecConfig[] mCodecsLocalCapabilities; - private final BluetoothCodecConfig[] mCodecsSelectableCapabilities; + private final @Nullable List<BluetoothCodecConfig> mCodecsLocalCapabilities; + private final @Nullable List<BluetoothCodecConfig> mCodecsSelectableCapabilities; public BluetoothCodecStatus(@Nullable BluetoothCodecConfig codecConfig, - @Nullable BluetoothCodecConfig[] codecsLocalCapabilities, - @Nullable BluetoothCodecConfig[] codecsSelectableCapabilities) { + @Nullable List<BluetoothCodecConfig> codecsLocalCapabilities, + @Nullable List<BluetoothCodecConfig> codecsSelectableCapabilities) { mCodecConfig = codecConfig; mCodecsLocalCapabilities = codecsLocalCapabilities; mCodecsSelectableCapabilities = codecsSelectableCapabilities; } + private BluetoothCodecStatus(Parcel in) { + mCodecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR); + mCodecsLocalCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR); + mCodecsSelectableCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR); + } + @Override public boolean equals(@Nullable Object o) { if (o instanceof BluetoothCodecStatus) { @@ -68,26 +72,25 @@ public final class BluetoothCodecStatus implements Parcelable { } /** - * Checks whether two arrays of capabilities contain same capabilities. - * The order of the capabilities in each array is ignored. + * Checks whether two lists of capabilities contain same capabilities. + * The order of the capabilities in each list is ignored. * - * @param c1 the first array of capabilities to compare - * @param c2 the second array of capabilities to compare - * @return true if both arrays contain same capabilities - * @hide + * @param c1 the first list of capabilities to compare + * @param c2 the second list of capabilities to compare + * @return {@code true} if both lists contain same capabilities */ - public static boolean sameCapabilities(BluetoothCodecConfig[] c1, - BluetoothCodecConfig[] c2) { + private static boolean sameCapabilities(@Nullable List<BluetoothCodecConfig> c1, + @Nullable List<BluetoothCodecConfig> c2) { if (c1 == null) { return (c2 == null); } if (c2 == null) { return false; } - if (c1.length != c2.length) { + if (c1.size() != c2.size()) { return false; } - return Arrays.asList(c1).containsAll(Arrays.asList(c2)); + return c1.containsAll(c2); } /** @@ -95,10 +98,9 @@ public final class BluetoothCodecStatus implements Parcelable { * Any parameters of the codec config with NONE value will be considered a wildcard matching. * * @param codecConfig the codec config to compare against - * @return true if the codec config matches, otherwise false - * @hide + * @return {@code true} if the codec config matches, {@code false} otherwise */ - public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) { + public boolean isCodecConfigSelectable(@Nullable BluetoothCodecConfig codecConfig) { if (codecConfig == null || !codecConfig.hasSingleSampleRate() || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) { return false; @@ -128,10 +130,7 @@ public final class BluetoothCodecStatus implements Parcelable { } /** - * Returns a hash based on the codec config and local capabilities - * - * @return a hash based on the config values - * @hide + * Returns a hash based on the codec config and local capabilities. */ @Override public int hashCode() { @@ -139,17 +138,19 @@ public final class BluetoothCodecStatus implements Parcelable { mCodecsLocalCapabilities); } + /** + * Returns a {@link String} that describes each BluetoothCodecStatus parameter + * current value. + */ @Override public String toString() { return "{mCodecConfig:" + mCodecConfig - + ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) - + ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) + + ",mCodecsLocalCapabilities:" + mCodecsLocalCapabilities + + ",mCodecsSelectableCapabilities:" + mCodecsSelectableCapabilities + "}"; } /** - * Always returns 0 - * * @return 0 * @hide */ @@ -161,16 +162,7 @@ public final class BluetoothCodecStatus implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecStatus> CREATOR = new Parcelable.Creator<BluetoothCodecStatus>() { public BluetoothCodecStatus createFromParcel(Parcel in) { - final BluetoothCodecConfig codecConfig = in.readTypedObject( - BluetoothCodecConfig.CREATOR); - final BluetoothCodecConfig[] codecsLocalCapabilities = in.createTypedArray( - BluetoothCodecConfig.CREATOR); - final BluetoothCodecConfig[] codecsSelectableCapabilities = in.createTypedArray( - BluetoothCodecConfig.CREATOR); - - return new BluetoothCodecStatus(codecConfig, - codecsLocalCapabilities, - codecsSelectableCapabilities); + return new BluetoothCodecStatus(in); } public BluetoothCodecStatus[] newArray(int size) { @@ -179,47 +171,38 @@ public final class BluetoothCodecStatus implements Parcelable { }; /** - * Flattens the object to a parcel + * Flattens the object to a parcel. * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. - * - * @hide + * @param out The Parcel in which the object should be written + * @param flags Additional flags about how the object should be written */ @Override - public void writeToParcel(Parcel out, int flags) { + public void writeToParcel(@NonNull Parcel out, int flags) { out.writeTypedObject(mCodecConfig, 0); - out.writeTypedArray(mCodecsLocalCapabilities, 0); - out.writeTypedArray(mCodecsSelectableCapabilities, 0); + out.writeTypedList(mCodecsLocalCapabilities); + out.writeTypedList(mCodecsSelectableCapabilities); } /** - * Gets the current codec configuration. - * - * @return the current codec configuration + * Returns the current codec configuration. */ - @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } /** - * Gets the codecs local capabilities. - * - * @return an array with the codecs local capabilities + * Returns the codecs local capabilities. */ - @UnsupportedAppUsage - public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { - return mCodecsLocalCapabilities; + public @NonNull List<BluetoothCodecConfig> getCodecsLocalCapabilities() { + return (mCodecsLocalCapabilities == null) + ? Collections.emptyList() : mCodecsLocalCapabilities; } /** - * Gets the codecs selectable capabilities. - * - * @return an array with the codecs selectable capabilities + * Returns the codecs selectable capabilities. */ - @UnsupportedAppUsage - public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { - return mCodecsSelectableCapabilities; + public @NonNull List<BluetoothCodecConfig> getCodecsSelectableCapabilities() { + return (mCodecsSelectableCapabilities == null) + ? Collections.emptyList() : mCodecsSelectableCapabilities; } } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index c71fcc637cb9..6e918bd6243d 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1340,7 +1340,10 @@ public final class BluetoothDevice implements Parcelable, Attributable { if (alias == null) { return getName(); } - return alias; + return alias + .replace('\t', ' ') + .replace('\n', ' ') + .replace('\r', ' '); } catch (RemoteException e) { Log.e(TAG, "", e); } diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index ca898bd6a5ef..4e7c01ad2db1 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; @@ -29,6 +31,8 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -140,27 +144,6 @@ public final class BluetoothGatt implements BluetoothProfile { public static final int CONNECTION_PRIORITY_LOW_POWER = 2; /** - * A GATT writeCharacteristic request is started successfully. - * - * @hide - */ - public static final int GATT_WRITE_REQUEST_SUCCESS = 0; - - /** - * A GATT writeCharacteristic request failed to start. - * - * @hide - */ - public static final int GATT_WRITE_REQUEST_FAIL = 1; - - /** - * A GATT writeCharacteristic request is issued to a busy remote device. - * - * @hide - */ - public static final int GATT_WRITE_REQUEST_BUSY = 2; - - /** * No authentication required. * * @hide @@ -430,6 +413,9 @@ public final class BluetoothGatt implements BluetoothProfile { if (callback != null) { if (status == 0) characteristic.setValue(value); callback.onCharacteristicRead(BluetoothGatt.this, characteristic, + value, status); + // Keep calling deprecated callback to maintain app compatibility + callback.onCharacteristicRead(BluetoothGatt.this, characteristic, status); } } @@ -443,7 +429,8 @@ public final class BluetoothGatt implements BluetoothProfile { */ @Override @SuppressLint("AndroidFrameworkRequiresPermission") - public void onCharacteristicWrite(String address, int status, int handle) { + public void onCharacteristicWrite(String address, int status, int handle, + byte[] value) { if (VDBG) { Log.d(TAG, "onCharacteristicWrite() - Device=" + address + " handle=" + handle + " Status=" + status); @@ -467,12 +454,13 @@ public final class BluetoothGatt implements BluetoothProfile { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; - int requestStatus = GATT_WRITE_REQUEST_FAIL; + int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { requestStatus = mService.writeCharacteristic(mClientIf, address, handle, characteristic.getWriteType(), authReq, - characteristic.getValue(), mAttributionSource); - if (requestStatus != GATT_WRITE_REQUEST_BUSY) { + value, mAttributionSource); + if (requestStatus + != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) { break; } try { @@ -488,7 +476,6 @@ public final class BluetoothGatt implements BluetoothProfile { } mAuthRetryState = AUTH_RETRY_STATE_IDLE; - runOrQueueCallback(new Runnable() { @Override public void run() { @@ -525,6 +512,9 @@ public final class BluetoothGatt implements BluetoothProfile { if (callback != null) { characteristic.setValue(value); callback.onCharacteristicChanged(BluetoothGatt.this, + characteristic, value); + // Keep calling deprecated callback to maintain app compatibility + callback.onCharacteristicChanged(BluetoothGatt.this, characteristic); } } @@ -578,6 +568,9 @@ public final class BluetoothGatt implements BluetoothProfile { final BluetoothGattCallback callback = mCallback; if (callback != null) { if (status == 0) descriptor.setValue(value); + callback.onDescriptorRead(BluetoothGatt.this, descriptor, status, + value); + // Keep calling deprecated callback to maintain app compatibility callback.onDescriptorRead(BluetoothGatt.this, descriptor, status); } } @@ -590,7 +583,8 @@ public final class BluetoothGatt implements BluetoothProfile { */ @Override @SuppressLint("AndroidFrameworkRequiresPermission") - public void onDescriptorWrite(String address, int status, int handle) { + public void onDescriptorWrite(String address, int status, int handle, + byte[] value) { if (VDBG) { Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle); @@ -614,7 +608,7 @@ public final class BluetoothGatt implements BluetoothProfile { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeDescriptor(mClientIf, address, handle, - authReq, descriptor.getValue(), mAttributionSource); + authReq, value, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -1194,8 +1188,8 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the requested characteristic from the associated remote device. * * <p>This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} - * callback. + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, + * BluetoothGattCharacteristic, byte[], int)} callback. * * @param characteristic Characteristic to read from the remote device * @return true, if the read operation was initiated successfully @@ -1240,8 +1234,8 @@ public final class BluetoothGatt implements BluetoothProfile { * Reads the characteristic using its UUID from the associated remote device. * * <p>This is an asynchronous operation. The result of the read operation - * is reported by the {@link BluetoothGattCallback#onCharacteristicRead} - * callback. + * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, + * BluetoothGattCharacteristic, byte[], int)} callback. * * @param uuid UUID of characteristic to read from the remote device * @return true, if the read operation was initiated successfully @@ -1284,40 +1278,94 @@ public final class BluetoothGatt implements BluetoothProfile { * * @param characteristic Characteristic to write on the remote device * @return true, if the write operation was initiated successfully + * @throws IllegalArgumentException if characteristic or its value are null + * + * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], + * int)} as this is not memory safe. */ + @Deprecated @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { - if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 - && (characteristic.getProperties() - & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { + try { + return writeCharacteristic(characteristic, characteristic.getValue(), + characteristic.getWriteType()) == BluetoothStatusCodes.SUCCESS; + } catch (Exception e) { return false; } + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION, + BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED, + BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, + BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED, + BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY, + BluetoothStatusCodes.ERROR_UNKNOWN + }) + public @interface WriteOperationReturnValues{} + /** + * Writes a given characteristic and its values to the associated remote device. + * + * <p>Once the write operation has been completed, the + * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, + * reporting the result of the operation. + * + * @param characteristic Characteristic to write on the remote device + * @return whether the characteristic was successfully written to + * @throws IllegalArgumentException if characteristic or value are null + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @WriteOperationReturnValues + public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic, + @NonNull byte[] value, int writeType) { + if (characteristic == null) { + throw new IllegalArgumentException("characteristic must not be null"); + } + if (value == null) { + throw new IllegalArgumentException("value must not be null"); + } if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); - if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false; + if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 + && (characteristic.getProperties() + & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) { + return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED; + } + if (mService == null || mClientIf == 0) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } BluetoothGattService service = characteristic.getService(); - if (service == null) return false; + if (service == null) { + throw new IllegalArgumentException("Characteristic must have a non-null service"); + } BluetoothDevice device = service.getDevice(); - if (device == null) return false; + if (device == null) { + throw new IllegalArgumentException("Service must have a non-null device"); + } synchronized (mDeviceBusyLock) { if (mDeviceBusy) { - return false; + return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; } mDeviceBusy = true; } - int requestStatus = GATT_WRITE_REQUEST_FAIL; + int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; try { for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(), - characteristic.getInstanceId(), characteristic.getWriteType(), - AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource); - if (requestStatus != GATT_WRITE_REQUEST_BUSY) { + characteristic.getInstanceId(), writeType, AUTHENTICATION_NONE, value, + mAttributionSource); + if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) { break; } try { @@ -1330,10 +1378,10 @@ public final class BluetoothGatt implements BluetoothProfile { synchronized (mDeviceBusyLock) { mDeviceBusy = false; } - return false; + throw e.rethrowFromSystemServer(); } - return requestStatus == GATT_WRITE_REQUEST_SUCCESS; + return requestStatus; } /** @@ -1384,45 +1432,86 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Write the value of a given descriptor to the associated remote device. * - * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is - * triggered to report the result of the write operation. + * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the + * result of the write operation. * * @param descriptor Descriptor to write to the associated remote device * @return true, if the write operation was initiated successfully + * @throws IllegalArgumentException if descriptor or its value are null + * + * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as + * this is not memory safe. */ + @Deprecated @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { + try { + return writeDescriptor(descriptor, descriptor.getValue()) + == BluetoothStatusCodes.SUCCESS; + } catch (Exception e) { + return false; + } + } + + /** + * Write the value of a given descriptor to the associated remote device. + * + * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the + * result of the write operation. + * + * @param descriptor Descriptor to write to the associated remote device + * @return true, if the write operation was initiated successfully + * @throws IllegalArgumentException if descriptor or value are null + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @WriteOperationReturnValues + public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor, + @NonNull byte[] value) { + if (descriptor == null) { + throw new IllegalArgumentException("descriptor must not be null"); + } + if (value == null) { + throw new IllegalArgumentException("value must not be null"); + } if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); - if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false; + if (mService == null || mClientIf == 0) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); - if (characteristic == null) return false; + if (characteristic == null) { + throw new IllegalArgumentException("Descriptor must have a non-null characteristic"); + } BluetoothGattService service = characteristic.getService(); - if (service == null) return false; + if (service == null) { + throw new IllegalArgumentException("Characteristic must have a non-null service"); + } BluetoothDevice device = service.getDevice(); - if (device == null) return false; + if (device == null) { + throw new IllegalArgumentException("Service must have a non-null device"); + } synchronized (mDeviceBusyLock) { - if (mDeviceBusy) return false; + if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; mDeviceBusy = true; } try { - mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource); + return mService.writeDescriptor(mClientIf, device.getAddress(), + descriptor.getInstanceId(), AUTHENTICATION_NONE, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); synchronized (mDeviceBusyLock) { mDeviceBusy = false; } - return false; + e.rethrowFromSystemServer(); } - - return true; + return BluetoothStatusCodes.ERROR_UNKNOWN; } /** @@ -1431,9 +1520,9 @@ public final class BluetoothGatt implements BluetoothProfile { * <p>Once a reliable write transaction has been initiated, all calls * to {@link #writeCharacteristic} are sent to the remote device for * verification and queued up for atomic execution. The application will - * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback - * in response to every {@link #writeCharacteristic} call and is responsible - * for verifying if the value has been transmitted accurately. + * receive a {@link BluetoothGattCallback#onCharacteristicWrite} callback in response to every + * {@link #writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} call and is + * responsible for verifying if the value has been transmitted accurately. * * <p>After all characteristics have been queued up and verified, * {@link #executeReliableWrite} will execute all writes. If a characteristic @@ -1530,9 +1619,9 @@ public final class BluetoothGatt implements BluetoothProfile { * Enable or disable notifications/indications for a given characteristic. * * <p>Once notifications are enabled for a characteristic, a - * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be - * triggered if the remote device indicates that the given characteristic - * has changed. + * {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, + * BluetoothGattCharacteristic, byte[])} callback will be triggered if the remote device + * indicates that the given characteristic has changed. * * @param characteristic The characteristic for which to enable notifications * @param enable Set to true to enable notifications/indications diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java index 1c40cff076f6..d0a5a1e729fe 100644 --- a/core/java/android/bluetooth/BluetoothGattCallback.java +++ b/core/java/android/bluetooth/BluetoothGattCallback.java @@ -80,16 +80,34 @@ public abstract class BluetoothGattCallback { /** * Callback reporting the result of a characteristic read operation. * - * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic} + * @param gatt GATT client invoked + * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} * @param characteristic Characteristic that was read from the associated remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed - * successfully. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully. + * @deprecated Use {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, + * BluetoothGattCharacteristic, byte[], int)} as it is memory safe */ + @Deprecated public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } /** + * Callback reporting the result of a characteristic read operation. + * + * @param gatt GATT client invoked + * {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} + * @param characteristic Characteristic that was read from the associated remote device. + * @param value the value of the characteristic + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully. + */ + public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull + BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) { + } + + /** * Callback indicating the result of a characteristic write operation. * * <p>If this callback is invoked while a reliable write transaction is @@ -98,10 +116,13 @@ public abstract class BluetoothGattCallback { * value to the desired value to be written. If the values don't match, * the application must abort the reliable write transaction. * - * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic} + * @param gatt GATT client that invoked + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, + * byte[], int)} * @param characteristic Characteristic that was written to the associated remote device. - * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. + * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if + * the + * operation succeeds. */ public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { @@ -110,33 +131,68 @@ public abstract class BluetoothGattCallback { /** * Callback triggered as a result of a remote characteristic notification. * - * @param gatt GATT client the characteristic is associated with + * @param gatt GATT client the characteristic is associated with * @param characteristic Characteristic that has been updated as a result of a remote - * notification event. + * notification event. + * @deprecated Use {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, + * BluetoothGattCharacteristic, byte[])} as it is memory safe by providing the characteristic + * value at the time of notification. */ + @Deprecated public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { } /** + * Callback triggered as a result of a remote characteristic notification. Note that the value + * within the characteristic object may have changed since receiving the remote characteristic + * notification, so check the parameter value for the value at the time of notification. + * + * @param gatt GATT client the characteristic is associated with + * @param characteristic Characteristic that has been updated as a result of a remote + * notification event. + * @param value notified characteristic value + */ + public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, + @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) { + } + + /** * Callback reporting the result of a descriptor read operation. * - * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} * @param descriptor Descriptor that was read from the associated remote device. - * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed - * successfully + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully + * @deprecated Use {@link BluetoothGattCallback#onDescriptorRead(BluetoothGatt, + * BluetoothGattDescriptor, int, byte[])} as it is memory safe by providing the descriptor + * value at the time it was read. */ + @Deprecated public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { } /** + * Callback reporting the result of a descriptor read operation. + * + * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor} + * @param descriptor Descriptor that was read from the associated remote device. + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed + * successfully + * @param value the descriptor value at the time of the read operation + */ + public void onDescriptorRead(@NonNull BluetoothGatt gatt, + @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) { + } + + /** * Callback indicating the result of a descriptor write operation. * - * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} + * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor} * @param descriptor Descriptor that was writte to the associated remote device. - * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the - * operation succeeds. + * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the + * operation succeeds. */ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java index 8a7d4baf5add..c5e986e895b2 100644 --- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -451,8 +451,8 @@ public class BluetoothGattCharacteristic implements Parcelable { * Set the write type for this characteristic * * <p>Setting the write type of a characteristic determines how the - * {@link BluetoothGatt#writeCharacteristic} function write this - * characteristic. + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} function + * write this characteristic. * * @param writeType The write type to for this characteristic. Can be one of: {@link * #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}. @@ -504,7 +504,10 @@ public class BluetoothGattCharacteristic implements Parcelable { * operation or if a characteristic update notification has been received. * * @return Cached value of the characteristic + * + * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} instead */ + @Deprecated public byte[] getValue() { return mValue; } @@ -521,7 +524,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param formatType The format type used to interpret the characteristic value. * @param offset Offset at which the integer value can be found. * @return Cached value of the characteristic or null of offset exceeds value size. + * + * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get + * the characteristic value */ + @Deprecated public Integer getIntValue(int formatType, int offset) { if ((offset + getTypeLen(formatType)) > mValue.length) return null; @@ -558,7 +565,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param offset Offset at which the float value can be found. * @return Cached value of the characteristic at a given offset or null if the requested offset * exceeds the value size. + * + * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get + * the characteristic value */ + @Deprecated public Float getFloatValue(int formatType, int offset) { if ((offset + getTypeLen(formatType)) > mValue.length) return null; @@ -580,7 +591,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @param offset Offset at which the string value can be found. * @return Cached value of the characteristic + * + * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get + * the characteristic value */ + @Deprecated public String getStringValue(int offset) { if (mValue == null || offset > mValue.length) return null; byte[] strBytes = new byte[mValue.length - offset]; @@ -599,7 +614,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param value New value for this characteristic * @return true if the locally stored value has been set, false if the requested value could not * be stored locally. + * + * @deprecated Pass the characteristic value directly into + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} */ + @Deprecated public boolean setValue(byte[] value) { mValue = value; return true; @@ -613,7 +632,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param formatType Integer format type used to transform the value parameter * @param offset Offset at which the value should be placed * @return true if the locally stored value has been set + * + * @deprecated Pass the characteristic value directly into + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} */ + @Deprecated public boolean setValue(int value, int formatType, int offset) { int len = offset + getTypeLen(formatType); if (mValue == null) mValue = new byte[len]; @@ -660,7 +683,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * @param formatType Float format type used to transform the value parameter * @param offset Offset at which the value should be placed * @return true if the locally stored value has been set + * + * @deprecated Pass the characteristic value directly into + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} */ + @Deprecated public boolean setValue(int mantissa, int exponent, int formatType, int offset) { int len = offset + getTypeLen(formatType); if (mValue == null) mValue = new byte[len]; @@ -697,7 +724,11 @@ public class BluetoothGattCharacteristic implements Parcelable { * * @param value New value for this characteristic * @return true if the locally stored value has been set + * + * @deprecated Pass the characteristic value directly into + * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} */ + @Deprecated public boolean setValue(String value) { mValue = value.getBytes(); return true; diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java index ed5ea0873020..a35d5b99fd7b 100644 --- a/core/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java @@ -260,7 +260,10 @@ public class BluetoothGattDescriptor implements Parcelable { * operation. * * @return Cached value of the descriptor + * + * @deprecated Use {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)} instead */ + @Deprecated public byte[] getValue() { return mValue; } @@ -276,7 +279,11 @@ public class BluetoothGattDescriptor implements Parcelable { * @param value New value for this descriptor * @return true if the locally stored value has been set, false if the requested value could not * be stored locally. + * + * @deprecated Pass the descriptor value directly into + * {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} */ + @Deprecated public boolean setValue(byte[] value) { mValue = value; return true; diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 3e799defa5e9..08e0178403f1 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; @@ -26,6 +28,8 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -709,33 +713,85 @@ public final class BluetoothGattServer implements BluetoothProfile { * notification * @return true, if the notification has been triggered successfully * @throws IllegalArgumentException + * + * @deprecated Use {@link BluetoothGattServer#notifyCharacteristicChanged(BluetoothDevice, + * BluetoothGattCharacteristic, boolean, byte[])} as this is not memory safe. */ + @Deprecated @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean notifyCharacteristicChanged(BluetoothDevice device, BluetoothGattCharacteristic characteristic, boolean confirm) { + return notifyCharacteristicChanged(device, characteristic, confirm, + characteristic.getValue()) == BluetoothStatusCodes.SUCCESS; + } + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, + BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION, + BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED, + BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, + BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED, + BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY, + BluetoothStatusCodes.ERROR_UNKNOWN + }) + public @interface NotifyCharacteristicReturnValues{} + + /** + * Send a notification or indication that a local characteristic has been + * updated. + * + * <p>A notification or indication is sent to the remote device to signal + * that the characteristic has been updated. This function should be invoked + * for every client that requests notifications/indications by writing + * to the "Client Configuration" descriptor for the given characteristic. + * + * @param device the remote device to receive the notification/indication + * @param characteristic the local characteristic that has been updated + * @param confirm {@code true} to request confirmation from the client (indication) or + * {@code false} to send a notification + * @param value the characteristic value + * @return whether the notification has been triggered successfully + * @throws IllegalArgumentException if the characteristic value or service is null + */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @NotifyCharacteristicReturnValues + public int notifyCharacteristicChanged(@NonNull BluetoothDevice device, + @NonNull BluetoothGattCharacteristic characteristic, boolean confirm, + @NonNull byte[] value) { if (VDBG) Log.d(TAG, "notifyCharacteristicChanged() - device: " + device.getAddress()); - if (mService == null || mServerIf == 0) return false; + if (mService == null || mServerIf == 0) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } + if (characteristic == null) { + throw new IllegalArgumentException("characteristic must not be null"); + } + if (device == null) { + throw new IllegalArgumentException("device must not be null"); + } BluetoothGattService service = characteristic.getService(); - if (service == null) return false; - - if (characteristic.getValue() == null) { - throw new IllegalArgumentException("Chracteristic value is empty. Use " - + "BluetoothGattCharacteristic#setvalue to update"); + if (service == null) { + throw new IllegalArgumentException("Characteristic must have a non-null service"); + } + if (value == null) { + throw new IllegalArgumentException("Characteristic value must not be null"); } try { - mService.sendNotification(mServerIf, device.getAddress(), + return mService.sendNotification(mServerIf, device.getAddress(), characteristic.getInstanceId(), confirm, - characteristic.getValue(), mAttributionSource); + value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); - return false; + throw e.rethrowFromSystemServer(); } - - return true; } /** diff --git a/core/java/android/bluetooth/BluetoothLeBroadcast.java b/core/java/android/bluetooth/BluetoothLeBroadcast.java new file mode 100644 index 000000000000..fed9f911d5b3 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothLeBroadcast.java @@ -0,0 +1,287 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.annotation.IntDef; +import android.content.Context; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * This class provides the public APIs to control the Bluetooth LE Broadcast Source profile. + * + * <p>BluetoothLeBroadcast is a proxy object for controlling the Bluetooth LE Broadcast + * Source Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} + * to get the BluetoothLeBroadcast proxy object. + * + * @hide + */ +public final class BluetoothLeBroadcast implements BluetoothProfile { + private static final String TAG = "BluetoothLeBroadcast"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** + * Constants used by the LE Audio Broadcast profile for the Broadcast state + * + * @hide + */ + @IntDef(prefix = {"LE_AUDIO_BROADCAST_STATE_"}, value = { + LE_AUDIO_BROADCAST_STATE_DISABLED, + LE_AUDIO_BROADCAST_STATE_ENABLING, + LE_AUDIO_BROADCAST_STATE_ENABLED, + LE_AUDIO_BROADCAST_STATE_DISABLING, + LE_AUDIO_BROADCAST_STATE_PLAYING, + LE_AUDIO_BROADCAST_STATE_NOT_PLAYING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LeAudioBroadcastState {} + + /** + * Indicates that LE Audio Broadcast mode is currently disabled + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_DISABLED = 10; + + /** + * Indicates that LE Audio Broadcast mode is being enabled + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_ENABLING = 11; + + /** + * Indicates that LE Audio Broadcast mode is currently enabled + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_ENABLED = 12; + /** + * Indicates that LE Audio Broadcast mode is being disabled + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_DISABLING = 13; + + /** + * Indicates that an LE Audio Broadcast mode is currently playing + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_PLAYING = 14; + + /** + * Indicates that LE Audio Broadcast is currently not playing + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_STATE_NOT_PLAYING = 15; + + /** + * Constants used by the LE Audio Broadcast profile for encryption key length + * + * @hide + */ + @IntDef(prefix = {"LE_AUDIO_BROADCAST_ENCRYPTION_KEY_"}, value = { + LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT, + LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LeAudioEncryptionKeyLength {} + + /** + * Indicates that the LE Audio Broadcast encryption key size is 32 bits. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_32BIT = 16; + + /** + * Indicates that the LE Audio Broadcast encryption key size is 128 bits. + * + * @hide + */ + public static final int LE_AUDIO_BROADCAST_ENCRYPTION_KEY_128BIT = 17; + + /** + * Interface for receiving events related to broadcasts + */ + public interface Callback { + /** + * Called when broadcast state has changed + * + * @param prevState broadcast state before the change + * @param newState broadcast state after the change + */ + @LeAudioBroadcastState + void onBroadcastStateChange(int prevState, int newState); + /** + * Called when encryption key has been updated + * + * @param success true if the key was updated successfully, false otherwise + */ + void onEncryptionKeySet(boolean success); + } + + /** + * Create a BluetoothLeBroadcast proxy object for interacting with the local + * LE Audio Broadcast Source service. + * + * @hide + */ + /*package*/ BluetoothLeBroadcast(Context context, + BluetoothProfile.ServiceListener listener) { + } + + /** + * Not supported since LE Audio Broadcasts do not establish a connection + * + * @throws UnsupportedOperationException + * + * @hide + */ + @Override + public int getConnectionState(BluetoothDevice device) { + throw new UnsupportedOperationException( + "LE Audio Broadcasts are not connection-oriented."); + } + + /** + * Not supported since LE Audio Broadcasts do not establish a connection + * + * @throws UnsupportedOperationException + * + * @hide + */ + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + throw new UnsupportedOperationException( + "LE Audio Broadcasts are not connection-oriented."); + } + + /** + * Not supported since LE Audio Broadcasts do not establish a connection + * + * @throws UnsupportedOperationException + * + * @hide + */ + @Override + public List<BluetoothDevice> getConnectedDevices() { + throw new UnsupportedOperationException( + "LE Audio Broadcasts are not connection-oriented."); + } + + /** + * Enable LE Audio Broadcast mode. + * + * Generates a new broadcast ID and enables sending of encrypted or unencrypted + * isochronous PDUs + * + * @hide + */ + public int enableBroadcastMode() { + if (DBG) log("enableBroadcastMode"); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED; + } + + /** + * Disable LE Audio Broadcast mode. + * + * @hide + */ + public int disableBroadcastMode() { + if (DBG) log("disableBroadcastMode"); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED; + } + + /** + * Get the current LE Audio broadcast state + * + * @hide + */ + @LeAudioBroadcastState + public int getBroadcastState() { + if (DBG) log("getBroadcastState"); + return LE_AUDIO_BROADCAST_STATE_DISABLED; + } + + /** + * Enable LE Audio broadcast encryption + * + * @param keyLength if useExisting is true, this specifies the length of the key that should + * be generated + * @param useExisting true, if an existing key should be used + * false, if a new key should be generated + * + * @hide + */ + @LeAudioEncryptionKeyLength + public int enableEncryption(boolean useExisting, int keyLength) { + if (DBG) log("enableEncryption useExisting=" + useExisting + " keyLength=" + keyLength); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED; + } + + /** + * Disable LE Audio broadcast encryption + * + * @param removeExisting true, if the existing key should be removed + * false, otherwise + * + * @hide + */ + public int disableEncryption(boolean removeExisting) { + if (DBG) log("disableEncryption removeExisting=" + removeExisting); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED; + } + + /** + * Enable or disable LE Audio broadcast encryption + * + * @param key use the provided key if non-null, generate a new key if null + * @param keyLength 0 if encryption is disabled, 4 bytes (low security), + * 16 bytes (high security) + * + * @hide + */ + @LeAudioEncryptionKeyLength + public int setEncryptionKey(byte[] key, int keyLength) { + if (DBG) log("setEncryptionKey key=" + key + " keyLength=" + keyLength); + return BluetoothStatusCodes.ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED; + } + + + /** + * Get the encryption key that was set before + * + * @return encryption key as a byte array or null if no encryption key was set + * + * @hide + */ + public byte[] getEncryptionKey() { + if (DBG) log("getEncryptionKey"); + return null; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 0cf9f9fd6f43..e047e5d81a9d 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -233,12 +233,19 @@ public interface BluetoothProfile { int CSIP_SET_COORDINATOR = 25; /** + * LE Audio Broadcast Source + * + * @hide + */ + int LE_AUDIO_BROADCAST = 26; + + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 25; + int MAX_PROFILE_ID = 26; /** * Default priority for devices that we try to auto-connect to and @@ -436,6 +443,8 @@ public interface BluetoothProfile { return "OPP"; case HEARING_AID: return "HEARING_AID"; + case LE_AUDIO: + return "LE_AUDIO"; default: return "UNKNOWN_PROFILE"; } diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 1655b62bbfec..db5b75148e88 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; -import android.annotation.SuppressLint; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.net.LocalSocket; @@ -266,7 +265,7 @@ public final class BluetoothSocket implements Closeable { throw new IOException("bt socket acept failed"); } - as.mPfd = new ParcelFileDescriptor(fds[0]); + as.mPfd = ParcelFileDescriptor.dup(fds[0]); as.mSocket = LocalSocket.createConnectedLocalSocket(fds[0]); as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java index 63e84eddc73f..9dafa073ab3f 100644 --- a/core/java/android/bluetooth/BluetoothStatusCodes.java +++ b/core/java/android/bluetooth/BluetoothStatusCodes.java @@ -79,9 +79,31 @@ public final class BluetoothStatusCodes { public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7; /** + * Error code indicating that the caller does not have the + * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission + */ + public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; + + /** + * Error code indicating that the profile service is not bound. You can bind a profile service + * by calling {@link BluetoothAdapter#getProfileProxy} + */ + public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; + + /** * Error code indicating that the feature is not supported. */ - public static final int ERROR_FEATURE_NOT_SUPPORTED = 8; + public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; + + /** + * A GATT writeCharacteristic request is not permitted on the remote device. + */ + public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; + + /** + * A GATT writeCharacteristic request is issued to a busy remote device. + */ + public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; /** * If another application has already requested {@link OobData} then another fetch will be @@ -204,6 +226,66 @@ public final class BluetoothStatusCodes { public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109; /** + * Indicates that setting the LE Audio Broadcast mode failed. + * <p> + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_BROADCAST_MODE_FAILED = 1110; + + /** + * Indicates that setting a new encryption key for Bluetooth LE Audio Broadcast Source failed. + * <p> + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_SET_ENCRYPTION_KEY_FAILED = 1111; + + /** + * Indicates that connecting to a remote Broadcast Audio Scan Service failed. + * <p> + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_CONNECT_FAILED = 1112; + + /** + * Indicates that disconnecting from a remote Broadcast Audio Scan Service failed. + * <p> + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_AUDIO_SCAN_SERVICE_DISCONNECT_FAILED = 1113; + + /** + * Indicates that enabling LE Audio Broadcast encryption failed + * <p> + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_ENABLE_ENCRYPTION_FAILED = 1114; + + /** + * Indicates that disabling LE Audio Broadcast encryption failed + * <p> + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + * + * @hide + */ + public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115; + + /** * Indicates that an unknown error has occurred has occurred. */ public static final int ERROR_UNKNOWN = Integer.MAX_VALUE; diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java index cec658049ca5..fdf62ec3a647 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -25,6 +25,7 @@ import android.util.ArrayMap; import android.util.SparseArray; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -47,6 +48,9 @@ public final class AdvertiseData implements Parcelable { @NonNull private final List<ParcelUuid> mServiceSolicitationUuids; + @Nullable + private final List<TransportDiscoveryData> mTransportDiscoveryData; + private final SparseArray<byte[]> mManufacturerSpecificData; private final Map<ParcelUuid, byte[]> mServiceData; private final boolean mIncludeTxPowerLevel; @@ -54,12 +58,14 @@ public final class AdvertiseData implements Parcelable { private AdvertiseData(List<ParcelUuid> serviceUuids, List<ParcelUuid> serviceSolicitationUuids, + List<TransportDiscoveryData> transportDiscoveryData, SparseArray<byte[]> manufacturerData, Map<ParcelUuid, byte[]> serviceData, boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; mServiceSolicitationUuids = serviceSolicitationUuids; + mTransportDiscoveryData = transportDiscoveryData; mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; @@ -83,6 +89,17 @@ public final class AdvertiseData implements Parcelable { } /** + * Returns a list of {@link TransportDiscoveryData} within the advertisement. + */ + @NonNull + public List<TransportDiscoveryData> getTransportDiscoveryData() { + if (mTransportDiscoveryData == null) { + return Collections.emptyList(); + } + return mTransportDiscoveryData; + } + + /** * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The * manufacturer id is a non-negative number assigned by Bluetooth SIG. */ @@ -116,8 +133,8 @@ public final class AdvertiseData implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData, - mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); + return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData, + mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); } /** @@ -134,6 +151,7 @@ public final class AdvertiseData implements Parcelable { AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids) + && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData) && BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && BluetoothLeUtils.equals(mServiceData, other.mServiceData) @@ -144,7 +162,8 @@ public final class AdvertiseData implements Parcelable { @Override public String toString() { return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids=" - + mServiceSolicitationUuids + ", mManufacturerSpecificData=" + + mServiceSolicitationUuids + ", mTransportDiscoveryData=" + + mTransportDiscoveryData + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" @@ -162,6 +181,8 @@ public final class AdvertiseData implements Parcelable { dest.writeTypedArray(mServiceSolicitationUuids.toArray( new ParcelUuid[mServiceSolicitationUuids.size()]), flags); + dest.writeTypedList(mTransportDiscoveryData); + // mManufacturerSpecificData could not be null. dest.writeInt(mManufacturerSpecificData.size()); for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { @@ -197,6 +218,12 @@ public final class AdvertiseData implements Parcelable { builder.addServiceSolicitationUuid(uuid); } + List<TransportDiscoveryData> transportDiscoveryData = + in.createTypedArrayList(TransportDiscoveryData.CREATOR); + for (TransportDiscoveryData tdd : transportDiscoveryData) { + builder.addTransportDiscoveryData(tdd); + } + int manufacturerSize = in.readInt(); for (int i = 0; i < manufacturerSize; ++i) { int manufacturerId = in.readInt(); @@ -223,6 +250,9 @@ public final class AdvertiseData implements Parcelable { private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>(); @NonNull private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>(); + @Nullable + private List<TransportDiscoveryData> mTransportDiscoveryData = + new ArrayList<TransportDiscoveryData>(); private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>(); private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>(); private boolean mIncludeTxPowerLevel; @@ -256,6 +286,7 @@ public final class AdvertiseData implements Parcelable { mServiceSolicitationUuids.add(serviceSolicitationUuid); return this; } + /** * Add service data to advertise data. * @@ -274,6 +305,23 @@ public final class AdvertiseData implements Parcelable { } /** + * Add Transport Discovery Data to advertise data. + * + * @param transportDiscoveryData Transport Discovery Data, consisting of one or more + * Transport Blocks. Transport Discovery Data AD Type Code is already included. + * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty + */ + @NonNull + public Builder addTransportDiscoveryData( + @NonNull TransportDiscoveryData transportDiscoveryData) { + if (transportDiscoveryData == null) { + throw new IllegalArgumentException("transportDiscoveryData is null"); + } + mTransportDiscoveryData.add(transportDiscoveryData); + return this; + } + + /** * Add manufacturer specific data. * <p> * Please refer to the Bluetooth Assigned Numbers document provided by the <a @@ -319,8 +367,8 @@ public final class AdvertiseData implements Parcelable { */ public AdvertiseData build() { return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids, - mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, - mIncludeDeviceName); + mTransportDiscoveryData, mManufacturerSpecificData, mServiceData, + mIncludeTxPowerLevel, mIncludeDeviceName); } } } diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 58029745ab9c..b9f8a57a75ea 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -567,6 +567,9 @@ public final class BluetoothLeAdvertiser { + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } + for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) { + size += OVERHEAD_BYTES_PER_FIELD + transportDiscoveryData.totalBytes(); + } for (ParcelUuid uuid : data.getServiceData().keySet()) { int uuidLen = BluetoothUuid.uuidToBytes(uuid).length; size += OVERHEAD_BYTES_PER_FIELD + uuidLen diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java new file mode 100644 index 000000000000..b388beda6b3b --- /dev/null +++ b/core/java/android/bluetooth/le/TransportBlock.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth.le; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +/** + * Wrapper for Transport Discovery Data Transport Blocks. + * This class represents a Transport Block from a Transport Discovery Data. + * + * @see TransportDiscoveryData + * @see AdvertiseData + */ +public final class TransportBlock implements Parcelable { + private static final String TAG = "TransportBlock"; + private final int mOrgId; + private final int mTdsFlags; + private final int mTransportDataLength; + private final byte[] mTransportData; + + /** + * Creates an instance of TransportBlock from raw data. + * + * @param orgId the Organization ID + * @param tdsFlags the TDS flags + * @param transportDataLength the total length of the Transport Data + * @param transportData the Transport Data + */ + public TransportBlock(int orgId, int tdsFlags, int transportDataLength, + @Nullable byte[] transportData) { + mOrgId = orgId; + mTdsFlags = tdsFlags; + mTransportDataLength = transportDataLength; + mTransportData = transportData; + } + + private TransportBlock(Parcel in) { + mOrgId = in.readInt(); + mTdsFlags = in.readInt(); + mTransportDataLength = in.readInt(); + mTransportData = new byte[mTransportDataLength]; + in.readByteArray(mTransportData); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mOrgId); + dest.writeInt(mTdsFlags); + dest.writeInt(mTransportDataLength); + dest.writeByteArray(mTransportData); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() { + @Override + public TransportBlock createFromParcel(Parcel in) { + return new TransportBlock(in); + } + + @Override + public TransportBlock[] newArray(int size) { + return new TransportBlock[size]; + } + }; + + /** + * Gets the Organization ID of the Transport Block which corresponds to one of the + * the Bluetooth SIG Assigned Numbers. + */ + public int getOrgId() { + return mOrgId; + } + + /** + * Gets the TDS flags of the Transport Block which represents the role of the device and + * information about its state and supported features. + */ + public int getTdsFlags() { + return mTdsFlags; + } + + /** + * Gets the total number of octets in the Transport Data field in this Transport Block. + */ + public int getTransportDataLength() { + return mTransportDataLength; + } + + /** + * Gets the Transport Data of the Transport Block which contains organization-specific data. + */ + @Nullable + public byte[] getTransportData() { + return mTransportData; + } + + /** + * Converts this TransportBlock to byte array + * + * @return byte array representation of this Transport Block or null if the conversion failed + */ + @Nullable + public byte[] toByteArray() { + try { + ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); + buffer.put((byte) mOrgId); + buffer.put((byte) mTdsFlags); + buffer.put((byte) mTransportDataLength); + if (mTransportData != null) { + buffer.put(mTransportData); + } + return buffer.array(); + } catch (BufferOverflowException e) { + Log.e(TAG, "Error converting to byte array: " + e.toString()); + return null; + } + } + + /** + * @return total byte count of this TransportBlock + */ + public int totalBytes() { + // 3 uint8 + byte[] length + int size = 3 + mTransportDataLength; + return size; + } +} diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java new file mode 100644 index 000000000000..c8e97f9a823a --- /dev/null +++ b/core/java/android/bluetooth/le/TransportDiscoveryData.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth.le; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Wrapper for Transport Discovery Data AD Type. + * This class contains the Transport Discovery Data AD Type Code as well as + * a list of potential Transport Blocks. + * + * @see AdvertiseData + */ +public final class TransportDiscoveryData implements Parcelable { + private static final String TAG = "TransportDiscoveryData"; + private final int mTransportDataType; + private final List<TransportBlock> mTransportBlocks; + + /** + * Creates a TransportDiscoveryData instance. + * + * @param transportDataType the Transport Discovery Data AD Type + * @param transportBlocks the list of Transport Blocks + */ + public TransportDiscoveryData(int transportDataType, + @NonNull List<TransportBlock> transportBlocks) { + mTransportDataType = transportDataType; + mTransportBlocks = transportBlocks; + } + + /** + * Creates a TransportDiscoveryData instance from byte arrays. + * + * Uses the transport discovery data bytes and parses them into an usable class. + * + * @param transportDiscoveryData the raw discovery data + */ + public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) { + ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData); + mTransportBlocks = new ArrayList(); + if (byteBuffer.remaining() > 0) { + mTransportDataType = byteBuffer.get(); + } else { + mTransportDataType = -1; + } + try { + while (byteBuffer.remaining() > 0) { + int orgId = byteBuffer.get(); + int tdsFlags = byteBuffer.get(); + int transportDataLength = byteBuffer.get(); + byte[] transportData = new byte[transportDataLength]; + byteBuffer.get(transportData, 0, transportDataLength); + mTransportBlocks.add(new TransportBlock(orgId, tdsFlags, + transportDataLength, transportData)); + } + } catch (BufferUnderflowException e) { + Log.e(TAG, "Error while parsing data: " + e.toString()); + } + } + + private TransportDiscoveryData(Parcel in) { + mTransportDataType = in.readInt(); + mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mTransportDataType); + dest.writeTypedList(mTransportBlocks); + } + + public static final @NonNull Creator<TransportDiscoveryData> CREATOR = + new Creator<TransportDiscoveryData>() { + @Override + public TransportDiscoveryData createFromParcel(Parcel in) { + return new TransportDiscoveryData(in); + } + + @Override + public TransportDiscoveryData[] newArray(int size) { + return new TransportDiscoveryData[size]; + } + }; + + /** + * Gets the transport data type. + */ + public int getTransportDataType() { + return mTransportDataType; + } + + /** + * @return the list of {@link TransportBlock} in this TransportDiscoveryData + * or an empty list if there are no Transport Blocks + */ + @NonNull + public List<TransportBlock> getTransportBlocks() { + if (mTransportBlocks == null) { + return Collections.emptyList(); + } + return mTransportBlocks; + } + + /** + * Converts this TransportDiscoveryData to byte array + * + * @return byte array representation of this Transport Discovery Data or null if the + * conversion failed + */ + @Nullable + public byte[] toByteArray() { + try { + ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); + buffer.put((byte) mTransportDataType); + for (TransportBlock transportBlock : getTransportBlocks()) { + buffer.put(transportBlock.toByteArray()); + } + return buffer.array(); + } catch (BufferOverflowException e) { + Log.e(TAG, "Error converting to byte array: " + e.toString()); + return null; + } + } + + /** + * @return total byte count of this TransportDataDiscovery + */ + public int totalBytes() { + int size = 1; // Counting Transport Data Type here. + for (TransportBlock transportBlock : getTransportBlocks()) { + size += transportBlock.totalBytes(); + } + return size; + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6885c10b4889..ceba01ec62e0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5462,6 +5462,12 @@ public abstract class Context { public static final String STATS_COMPANION_SERVICE = "statscompanion"; /** + * Service to assist statsd in logging atoms from bootstrap atoms. + * @hide + */ + public static final String STATS_BOOTSTRAP_ATOM_SERVICE = "statsbootstrap"; + + /** * Use with {@link #getSystemService(String)} to retrieve an {@link android.app.StatsManager}. * @hide */ @@ -6441,30 +6447,24 @@ public abstract class Context { @NonNull Configuration overrideConfiguration); /** - * Return a new Context object for the current Context but whose resources - * are adjusted to match the metrics of the given Display. Each call to this method - * returns a new instance of a Context object; Context objects are not - * shared, however common state (ClassLoader, other Resources for the - * same configuration) may be so the Context itself can be fairly lightweight. - * - * To obtain an instance of a {@link WindowManager} (see {@link #getSystemService(String)}) that - * is configured to show windows on the given display call - * {@link #createWindowContext(int, Bundle)} on the returned display Context or use an - * {@link android.app.Activity}. - * + * Returns a new <code>Context</code> object from the current context but with resources + * adjusted to match the metrics of <code>display</code>. Each call to this method + * returns a new instance of a context object. Context objects are not shared; however, + * common state (such as the {@link ClassLoader} and other resources for the same + * configuration) can be shared, so the <code>Context</code> itself is lightweight. * <p> - * Note that invoking #createDisplayContext(Display) from an UI context is not regarded - * as an UI context. In other words, it is not suggested to access UI components (such as - * obtain a {@link WindowManager} by {@link #getSystemService(String)}) - * from the context created from #createDisplayContext(Display). - * </p> - * - * @param display A {@link Display} object specifying the display for whose metrics the - * Context's resources should be tailored. + * To obtain an instance of {@link WindowManager} configured to show windows on the given + * display, call {@link #createWindowContext(int, Bundle)} on the returned display context, + * then call {@link #getSystemService(String)} or {@link #getSystemService(Class)} on the + * returned window context. + * <p> + * <b>Note:</b> The context returned by <code>createDisplayContext(Display)</code> is not a UI + * context. Do not access UI components or obtain a {@link WindowManager} from the context + * created by <code>createDisplayContext(Display)</code>. * - * @return A {@link Context} for the display. + * @param display The display to which the current context's resources are adjusted. * - * @see #getSystemService(String) + * @return A context for the display. */ @DisplayContext public abstract Context createDisplayContext(@NonNull Display display); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index d811040b6bb2..e781c2fce2b2 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1147,6 +1147,10 @@ public class Intent implements Parcelable, Cloneable { * numbers. Applications can <strong>dial</strong> emergency numbers using * {@link #ACTION_DIAL}, however. * + * <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use + * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than + * relying on this intent. + * * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} * and above and declares as using the {@link android.Manifest.permission#CALL_PHONE} * permission which is not granted, then attempting to use this action will diff --git a/core/java/android/content/om/OWNERS b/core/java/android/content/om/OWNERS index 91a0abfe711a..3669817e9844 100644 --- a/core/java/android/content/om/OWNERS +++ b/core/java/android/content/om/OWNERS @@ -3,4 +3,4 @@ toddke@android.com toddke@google.com patb@google.com -rtmitchell@google.com +zyy@google.com diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 2c4ff5889263..84c9fa9b375c 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -409,7 +409,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Value for {@link #flags}: {@code true} if the application may use cleartext network traffic - * (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP + * (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, SMTP * without STARTTLS or TLS). If {@code false}, the app declares that it does not intend to use * cleartext network traffic, in which case platform components (e.g., HTTP stacks, * {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to use cleartext diff --git a/core/java/android/content/res/OWNERS b/core/java/android/content/res/OWNERS index bc2355c6af5e..d12d920b2a54 100644 --- a/core/java/android/content/res/OWNERS +++ b/core/java/android/content/res/OWNERS @@ -3,4 +3,4 @@ toddke@android.com toddke@google.com patb@google.com -rtmitchell@google.com +zyy@google.com diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS index 2b4e4a106cec..95f13b5c1d8c 100644 --- a/core/java/android/hardware/OWNERS +++ b/core/java/android/hardware/OWNERS @@ -6,3 +6,7 @@ per-file *SensorPrivacy* = file:platform/frameworks/native:/libs/sensorprivacy/O # Sensors framework per-file *Sensor*,*Trigger* = file:platform/frameworks/native:/services/sensorservice/OWNERS + +# Buffers +per-file HardwareBuffer* = file:/graphics/java/android/graphics/OWNERS +per-file DataSpace* = file:/graphics/java/android/graphics/OWNERS diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java index 7ef5bac092f6..86052484eaf6 100644 --- a/core/java/android/net/IpSecAlgorithm.java +++ b/core/java/android/net/IpSecAlgorithm.java @@ -232,11 +232,10 @@ public final class IpSecAlgorithm implements Parcelable { ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO); ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO); - // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined - ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1); - ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1); - ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1); - ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1); + ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.S); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.S); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.S); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S); } private static final Set<String> ENABLED_ALGOS = diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 74506dae329b..c906a13bf41b 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -70,9 +70,17 @@ public class NetworkTemplate implements Parcelable { private static final String TAG = "NetworkTemplate"; /** + * Initial Version of the backup serializer. + */ + public static final int BACKUP_VERSION_1_INIT = 1; + /** + * Version of the backup serializer that added carrier template support. + */ + public static final int BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE = 2; + /** * Current Version of the Backup Serializer. */ - private static final int BACKUP_VERSION = 1; + private static final int BACKUP_VERSION = BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE; public static final int MATCH_MOBILE = 1; public static final int MATCH_WIFI = 4; @@ -285,6 +293,10 @@ public class NetworkTemplate implements Parcelable { private final int mRoaming; private final int mDefaultNetwork; private final int mSubType; + /** + * The subscriber Id match rule defines how the template should match networks with + * specific subscriberId(s). See NetworkTemplate#SUBSCRIBER_ID_MATCH_RULE_* for more detail. + */ private final int mSubscriberIdMatchRule; // Bitfield containing OEM network properties{@code NetworkIdentity#OEM_*}. @@ -348,7 +360,7 @@ public class NetworkTemplate implements Parcelable { mSubscriberIdMatchRule = subscriberIdMatchRule; checkValidSubscriberIdMatchRule(); if (!isKnownMatchRule(matchRule)) { - Log.e(TAG, "Unknown network template rule " + matchRule + throw new IllegalArgumentException("Unknown network template rule " + matchRule + " will not match any identity."); } } @@ -777,8 +789,8 @@ public class NetworkTemplate implements Parcelable { } /** - * Examine the given template and normalize if it refers to a "merged" - * mobile subscriber. We pick the "lowest" merged subscriber as the primary + * Examine the given template and normalize it. + * We pick the "lowest" merged subscriber as the primary * for key purposes, and expand the template to match all other merged * subscribers. * <p> @@ -793,8 +805,8 @@ public class NetworkTemplate implements Parcelable { } /** - * Examine the given template and normalize if it refers to a "merged" - * mobile subscriber. We pick the "lowest" merged subscriber as the primary + * Examine the given template and normalize it. + * We pick the "lowest" merged subscriber as the primary * for key purposes, and expand the template to match all other merged * subscribers. * @@ -806,7 +818,12 @@ public class NetworkTemplate implements Parcelable { * A, but also matches B. */ public static NetworkTemplate normalize(NetworkTemplate template, List<String[]> mergedList) { - if (!template.isMatchRuleMobile()) return template; + // Now there are several types of network which uses SubscriberId to store network + // information. For instances: + // The TYPE_WIFI with subscriberId means that it is a merged carrier wifi network. + // The TYPE_CARRIER means that the network associate to specific carrier network. + + if (template.mSubscriberId == null) return template; for (String[] merged : mergedList) { if (ArrayUtils.contains(merged, template.mSubscriberId)) { @@ -837,11 +854,17 @@ public class NetworkTemplate implements Parcelable { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos); + if (!isPersistable()) { + Log.wtf(TAG, "Trying to backup non-persistable template: " + this); + } + out.writeInt(BACKUP_VERSION); out.writeInt(mMatchRule); BackupUtils.writeString(out, mSubscriberId); BackupUtils.writeString(out, mNetworkId); + out.writeInt(mMetered); + out.writeInt(mSubscriberIdMatchRule); return baos.toByteArray(); } @@ -849,7 +872,7 @@ public class NetworkTemplate implements Parcelable { public static NetworkTemplate getNetworkTemplateFromBackup(DataInputStream in) throws IOException, BackupUtils.BadVersionException { int version = in.readInt(); - if (version < 1 || version > BACKUP_VERSION) { + if (version < BACKUP_VERSION_1_INIT || version > BACKUP_VERSION) { throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version"); } @@ -857,11 +880,27 @@ public class NetworkTemplate implements Parcelable { String subscriberId = BackupUtils.readString(in); String networkId = BackupUtils.readString(in); - if (!isKnownMatchRule(matchRule)) { + final int metered; + final int subscriberIdMatchRule; + if (version >= BACKUP_VERSION_2_SUPPORT_CARRIER_TEMPLATE) { + metered = in.readInt(); + subscriberIdMatchRule = in.readInt(); + } else { + // For backward compatibility, fill the missing filters from match rules. + metered = (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD + || matchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL; + subscriberIdMatchRule = SUBSCRIBER_ID_MATCH_RULE_EXACT; + } + + try { + return new NetworkTemplate(matchRule, + subscriberId, new String[] { subscriberId }, + networkId, metered, NetworkStats.ROAMING_ALL, + NetworkStats.DEFAULT_NETWORK_ALL, NetworkTemplate.NETWORK_TYPE_ALL, + NetworkTemplate.OEM_MANAGED_ALL, subscriberIdMatchRule); + } catch (IllegalArgumentException e) { throw new BackupUtils.BadVersionException( - "Restored network template contains unknown match rule " + matchRule); + "Restored network template contains unknown match rule " + matchRule, e); } - - return new NetworkTemplate(matchRule, subscriberId, networkId); } } diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java new file mode 100644 index 000000000000..6b33e4f45c42 --- /dev/null +++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.vcn; + +import static com.android.internal.annotations.VisibleForTesting.Visibility; +import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER; +import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER; +import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER; +import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.PersistableBundle; +import android.telephony.SubscriptionInfo; +import android.telephony.TelephonyManager; +import android.util.ArraySet; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.vcn.util.PersistableBundleUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +// TODO: Add documents +/** @hide */ +public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority { + private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds"; + @NonNull private final Set<String> mAllowedNetworkPlmnIds; + private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds"; + @NonNull private final Set<Integer> mAllowedSpecificCarrierIds; + + private static final String ALLOW_ROAMING_KEY = "mAllowRoaming"; + private final boolean mAllowRoaming; + + private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic"; + private final boolean mRequireOpportunistic; + + private VcnCellUnderlyingNetworkPriority( + int networkQuality, + boolean allowMetered, + Set<String> allowedNetworkPlmnIds, + Set<Integer> allowedSpecificCarrierIds, + boolean allowRoaming, + boolean requireOpportunistic) { + super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered); + mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds); + mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds); + mAllowRoaming = allowRoaming; + mRequireOpportunistic = requireOpportunistic; + + validate(); + } + + /** @hide */ + @Override + protected void validate() { + super.validate(); + validatePlmnIds(mAllowedNetworkPlmnIds); + Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null"); + } + + private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) { + Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null"); + + // A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal + // digits. + for (String id : allowedNetworkPlmnIds) { + if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) { + continue; + } else { + throw new IllegalArgumentException("Found invalid PLMN ID: " + id); + } + } + } + + /** @hide */ + @NonNull + @VisibleForTesting(visibility = Visibility.PROTECTED) + public static VcnCellUnderlyingNetworkPriority fromPersistableBundle( + @NonNull PersistableBundle in) { + Objects.requireNonNull(in, "PersistableBundle is null"); + + final int networkQuality = in.getInt(NETWORK_QUALITY_KEY); + final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY); + + final PersistableBundle plmnIdsBundle = + in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY); + Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null"); + final Set<String> allowedNetworkPlmnIds = + new ArraySet<String>( + PersistableBundleUtils.toList(plmnIdsBundle, STRING_DESERIALIZER)); + + final PersistableBundle specificCarrierIdsBundle = + in.getPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY); + Objects.requireNonNull(specificCarrierIdsBundle, "specificCarrierIdsBundle is null"); + final Set<Integer> allowedSpecificCarrierIds = + new ArraySet<Integer>( + PersistableBundleUtils.toList( + specificCarrierIdsBundle, INTEGER_DESERIALIZER)); + + final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY); + final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY); + + return new VcnCellUnderlyingNetworkPriority( + networkQuality, + allowMetered, + allowedNetworkPlmnIds, + allowedSpecificCarrierIds, + allowRoaming, + requireOpportunistic); + } + + /** @hide */ + @Override + @NonNull + @VisibleForTesting(visibility = Visibility.PROTECTED) + public PersistableBundle toPersistableBundle() { + final PersistableBundle result = super.toPersistableBundle(); + + final PersistableBundle plmnIdsBundle = + PersistableBundleUtils.fromList( + new ArrayList<>(mAllowedNetworkPlmnIds), STRING_SERIALIZER); + result.putPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY, plmnIdsBundle); + + final PersistableBundle specificCarrierIdsBundle = + PersistableBundleUtils.fromList( + new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER); + result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle); + + result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming); + result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic); + + return result; + } + + /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */ + @NonNull + public Set<String> getAllowedPlmnIds() { + return Collections.unmodifiableSet(mAllowedNetworkPlmnIds); + } + + /** + * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is + * acceptable. + */ + @NonNull + public Set<Integer> getAllowedSpecificCarrierIds() { + return Collections.unmodifiableSet(mAllowedSpecificCarrierIds); + } + + /** Return if roaming is allowed. */ + public boolean allowRoaming() { + return mAllowRoaming; + } + + /** Return if requiring an opportunistic network. */ + public boolean requireOpportunistic() { + return mRequireOpportunistic; + } + + @Override + public int hashCode() { + return Objects.hash( + super.hashCode(), + mAllowedNetworkPlmnIds, + mAllowedSpecificCarrierIds, + mAllowRoaming, + mRequireOpportunistic); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!super.equals(other)) { + return false; + } + + if (!(other instanceof VcnCellUnderlyingNetworkPriority)) { + return false; + } + + final VcnCellUnderlyingNetworkPriority rhs = (VcnCellUnderlyingNetworkPriority) other; + return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds) + && Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds) + && mAllowRoaming == rhs.mAllowRoaming + && mRequireOpportunistic == rhs.mRequireOpportunistic; + } + + /** This class is used to incrementally build WifiNetworkPriority objects. */ + public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> { + @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>(); + @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>(); + + private boolean mAllowRoaming = false; + private boolean mRequireOpportunistic = false; + + /** Construct a Builder object. */ + public Builder() {} + + /** + * Set allowed operator PLMN IDs. + * + * <p>This is used to distinguish cases where roaming agreements may dictate a different + * priority from a partner's networks. + * + * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an + * empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and + * thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()} + * and {@link SubscriptionInfo#getMncString()}. + */ + @NonNull + public Builder setAllowedPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) { + validatePlmnIds(allowedNetworkPlmnIds); + + mAllowedNetworkPlmnIds.clear(); + mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds); + return this; + } + + /** + * Set allowed specific carrier IDs. + * + * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty + * set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}. + */ + @NonNull + public Builder setAllowedSpecificCarrierIds( + @NonNull Set<Integer> allowedSpecificCarrierIds) { + Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null"); + mAllowedSpecificCarrierIds.clear(); + mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds); + return this; + } + + /** + * Set if roaming is allowed. + * + * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code + * false}. + */ + @NonNull + public Builder setAllowRoaming(boolean allowRoaming) { + mAllowRoaming = allowRoaming; + return this; + } + + /** + * Set if requiring an opportunistic network. + * + * @param requireOpportunistic the flag to indicate if caller requires an opportunistic + * network. Defaults to {@code false}. + */ + @NonNull + public Builder setRequireOpportunistic(boolean requireOpportunistic) { + mRequireOpportunistic = requireOpportunistic; + return this; + } + + /** Build the VcnCellUnderlyingNetworkPriority. */ + @NonNull + public VcnCellUnderlyingNetworkPriority build() { + return new VcnCellUnderlyingNetworkPriority( + mNetworkQuality, + mAllowMetered, + mAllowedNetworkPlmnIds, + mAllowedSpecificCarrierIds, + mAllowRoaming, + mRequireOpportunistic); + } + + /** @hide */ + @Override + Builder self() { + return this; + } + } +} diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 752ef3e39ab6..de4ada2dbc26 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -16,6 +16,7 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; +import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK; import static com.android.internal.annotations.VisibleForTesting.Visibility; @@ -41,6 +42,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; import java.util.SortedSet; @@ -157,6 +159,34 @@ public final class VcnGatewayConnectionConfig { TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(15) }; + + private static final LinkedHashSet<VcnUnderlyingNetworkPriority> + DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>(); + + static { + DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( + new VcnCellUnderlyingNetworkPriority.Builder() + .setNetworkQuality(NETWORK_QUALITY_OK) + .setAllowMetered(true /* allowMetered */) + .setAllowRoaming(true /* allowRoaming */) + .setRequireOpportunistic(true /* requireOpportunistic */) + .build()); + + DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( + new VcnWifiUnderlyingNetworkPriority.Builder() + .setNetworkQuality(NETWORK_QUALITY_OK) + .setAllowMetered(true /* allowMetered */) + .build()); + + DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( + new VcnCellUnderlyingNetworkPriority.Builder() + .setNetworkQuality(NETWORK_QUALITY_OK) + .setAllowMetered(true /* allowMetered */) + .setAllowRoaming(true /* allowRoaming */) + .setRequireOpportunistic(false /* requireOpportunistic */) + .build()); + } + private static final String GATEWAY_CONNECTION_NAME_KEY = "mGatewayConnectionName"; @NonNull private final String mGatewayConnectionName; @@ -166,6 +196,9 @@ public final class VcnGatewayConnectionConfig { private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities"; @NonNull private final SortedSet<Integer> mExposedCapabilities; + private static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities"; + @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities; + private static final String MAX_MTU_KEY = "mMaxMtu"; private final int mMaxMtu; @@ -177,6 +210,7 @@ public final class VcnGatewayConnectionConfig { @NonNull String gatewayConnectionName, @NonNull IkeTunnelConnectionParams tunnelConnectionParams, @NonNull Set<Integer> exposedCapabilities, + @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities, @NonNull long[] retryIntervalsMs, @IntRange(from = MIN_MTU_V6) int maxMtu) { mGatewayConnectionName = gatewayConnectionName; @@ -185,6 +219,11 @@ public final class VcnGatewayConnectionConfig { mRetryIntervalsMs = retryIntervalsMs; mMaxMtu = maxMtu; + mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities); + if (mUnderlyingNetworkPriorities.isEmpty()) { + mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); + } + validate(); } @@ -198,12 +237,19 @@ public final class VcnGatewayConnectionConfig { final PersistableBundle exposedCapsBundle = in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY); + final PersistableBundle networkPrioritiesBundle = + in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY); mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY); mTunnelConnectionParams = TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle); mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList( exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); + mUnderlyingNetworkPriorities = + new LinkedHashSet<>( + PersistableBundleUtils.toList( + networkPrioritiesBundle, + VcnUnderlyingNetworkPriority::fromPersistableBundle)); mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY); mMaxMtu = in.getInt(MAX_MTU_KEY); @@ -221,6 +267,7 @@ public final class VcnGatewayConnectionConfig { checkValidCapability(cap); } + Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null"); validateRetryInterval(mRetryIntervalsMs); @@ -303,6 +350,18 @@ public final class VcnGatewayConnectionConfig { } /** + * Retrieve the configured VcnUnderlyingNetworkPriority list, or a default list if it is not + * configured. + * + * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkPriority>) + * @hide + */ + @NonNull + public LinkedHashSet<VcnUnderlyingNetworkPriority> getVcnUnderlyingNetworkPriorities() { + return new LinkedHashSet<>(mUnderlyingNetworkPriorities); + } + + /** * Retrieves the configured retry intervals. * * @see Builder#setRetryIntervalsMillis(long[]) @@ -338,10 +397,15 @@ public final class VcnGatewayConnectionConfig { PersistableBundleUtils.fromList( new ArrayList<>(mExposedCapabilities), PersistableBundleUtils.INTEGER_SERIALIZER); + final PersistableBundle networkPrioritiesBundle = + PersistableBundleUtils.fromList( + new ArrayList<>(mUnderlyingNetworkPriorities), + VcnUnderlyingNetworkPriority::toPersistableBundle); result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName); result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle); result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle); + result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle); result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs); result.putInt(MAX_MTU_KEY, mMaxMtu); @@ -379,6 +443,11 @@ public final class VcnGatewayConnectionConfig { @NonNull private final String mGatewayConnectionName; @NonNull private final IkeTunnelConnectionParams mTunnelConnectionParams; @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); + + @NonNull + private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities = + new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); + @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS; private int mMaxMtu = DEFAULT_MAX_MTU; @@ -450,6 +519,33 @@ public final class VcnGatewayConnectionConfig { } /** + * Set the VcnUnderlyingNetworkPriority list. + * + * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that + * are ordered from most to least preferred, or an empty list to use the default + * prioritization. The default network prioritization is Opportunistic cellular, Carrier + * WiFi and Macro cellular + * @return + */ + /** @hide */ + @NonNull + public Builder setVcnUnderlyingNetworkPriorities( + @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities) { + Objects.requireNonNull( + mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); + + mUnderlyingNetworkPriorities.clear(); + + if (underlyingNetworkPriorities.isEmpty()) { + mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); + } else { + mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities); + } + + return this; + } + + /** * Set the retry interval between VCN establishment attempts upon successive failures. * * <p>The last retry interval will be repeated until safe mode is entered, or a connection @@ -513,6 +609,7 @@ public final class VcnGatewayConnectionConfig { mGatewayConnectionName, mTunnelConnectionParams, mExposedCapabilities, + mUnderlyingNetworkPriorities, mRetryIntervalsMs, mMaxMtu); } diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java new file mode 100644 index 000000000000..82f6ae72b43c --- /dev/null +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.vcn; + +import static com.android.internal.annotations.VisibleForTesting.Visibility; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.PersistableBundle; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +// TODO: Add documents +/** @hide */ +public abstract class VcnUnderlyingNetworkPriority { + /** @hide */ + protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1; + /** @hide */ + protected static final int NETWORK_PRIORITY_TYPE_CELL = 2; + + /** Denotes that network quality needs to be OK */ + public static final int NETWORK_QUALITY_OK = 10000; + /** Denotes that any network quality is acceptable */ + public static final int NETWORK_QUALITY_ANY = Integer.MAX_VALUE; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY}) + public @interface NetworkQuality {} + + private static final String NETWORK_PRIORITY_TYPE_KEY = "mNetworkPriorityType"; + private final int mNetworkPriorityType; + + /** @hide */ + protected static final String NETWORK_QUALITY_KEY = "mNetworkQuality"; + private final int mNetworkQuality; + + /** @hide */ + protected static final String ALLOW_METERED_KEY = "mAllowMetered"; + private final boolean mAllowMetered; + + /** @hide */ + protected VcnUnderlyingNetworkPriority( + int networkPriorityType, int networkQuality, boolean allowMetered) { + mNetworkPriorityType = networkPriorityType; + mNetworkQuality = networkQuality; + mAllowMetered = allowMetered; + } + + private static void validateNetworkQuality(int networkQuality) { + Preconditions.checkArgument( + networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK, + "Invalid networkQuality:" + networkQuality); + } + + /** @hide */ + protected void validate() { + validateNetworkQuality(mNetworkQuality); + } + + /** @hide */ + @NonNull + @VisibleForTesting(visibility = Visibility.PROTECTED) + public static VcnUnderlyingNetworkPriority fromPersistableBundle( + @NonNull PersistableBundle in) { + Objects.requireNonNull(in, "PersistableBundle is null"); + + final int networkPriorityType = in.getInt(NETWORK_PRIORITY_TYPE_KEY); + switch (networkPriorityType) { + case NETWORK_PRIORITY_TYPE_WIFI: + return VcnWifiUnderlyingNetworkPriority.fromPersistableBundle(in); + case NETWORK_PRIORITY_TYPE_CELL: + return VcnCellUnderlyingNetworkPriority.fromPersistableBundle(in); + default: + throw new IllegalArgumentException( + "Invalid networkPriorityType:" + networkPriorityType); + } + } + + /** @hide */ + @NonNull + PersistableBundle toPersistableBundle() { + final PersistableBundle result = new PersistableBundle(); + + result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType); + result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality); + result.putBoolean(ALLOW_METERED_KEY, mAllowMetered); + + return result; + } + + @Override + public int hashCode() { + return Objects.hash(mNetworkPriorityType, mNetworkQuality, mAllowMetered); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof VcnUnderlyingNetworkPriority)) { + return false; + } + + final VcnUnderlyingNetworkPriority rhs = (VcnUnderlyingNetworkPriority) other; + return mNetworkPriorityType == rhs.mNetworkPriorityType + && mNetworkQuality == rhs.mNetworkQuality + && mAllowMetered == rhs.mAllowMetered; + } + + /** Retrieve the required network quality. */ + @NetworkQuality + public int getNetworkQuality() { + return mNetworkQuality; + } + + /** Return if a metered network is allowed. */ + public boolean allowMetered() { + return mAllowMetered; + } + + /** + * This class is used to incrementally build VcnUnderlyingNetworkPriority objects. + * + * @param <T> The subclass to be built. + */ + public abstract static class Builder<T extends Builder<T>> { + /** @hide */ + protected int mNetworkQuality = NETWORK_QUALITY_ANY; + /** @hide */ + protected boolean mAllowMetered = false; + + /** @hide */ + protected Builder() {} + + /** + * Set the required network quality. + * + * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY + */ + @NonNull + public T setNetworkQuality(@NetworkQuality int networkQuality) { + validateNetworkQuality(networkQuality); + + mNetworkQuality = networkQuality; + return self(); + } + + /** + * Set if a metered network is allowed. + * + * @param allowMetered the flag to indicate if a metered network is allowed, defaults to + * {@code false} + */ + @NonNull + public T setAllowMetered(boolean allowMetered) { + mAllowMetered = allowMetered; + return self(); + } + + /** @hide */ + abstract T self(); + } +} diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java new file mode 100644 index 000000000000..fc7e7e2c4e41 --- /dev/null +++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.vcn; + +import static com.android.internal.annotations.VisibleForTesting.Visibility; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.PersistableBundle; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Objects; + +// TODO: Add documents +/** @hide */ +public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority { + private static final String SSID_KEY = "mSsid"; + @Nullable private final String mSsid; + + private VcnWifiUnderlyingNetworkPriority( + int networkQuality, boolean allowMetered, String ssid) { + super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered); + mSsid = ssid; + + validate(); + } + + /** @hide */ + @NonNull + @VisibleForTesting(visibility = Visibility.PROTECTED) + public static VcnWifiUnderlyingNetworkPriority fromPersistableBundle( + @NonNull PersistableBundle in) { + Objects.requireNonNull(in, "PersistableBundle is null"); + + final int networkQuality = in.getInt(NETWORK_QUALITY_KEY); + final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY); + final String ssid = in.getString(SSID_KEY); + return new VcnWifiUnderlyingNetworkPriority(networkQuality, allowMetered, ssid); + } + + /** @hide */ + @Override + @NonNull + @VisibleForTesting(visibility = Visibility.PROTECTED) + public PersistableBundle toPersistableBundle() { + final PersistableBundle result = super.toPersistableBundle(); + result.putString(SSID_KEY, mSsid); + return result; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), mSsid); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!super.equals(other)) { + return false; + } + + if (!(other instanceof VcnWifiUnderlyingNetworkPriority)) { + return false; + } + + final VcnWifiUnderlyingNetworkPriority rhs = (VcnWifiUnderlyingNetworkPriority) other; + return mSsid == rhs.mSsid; + } + + /** Retrieve the required SSID, or {@code null} if there is no requirement on SSID. */ + @Nullable + public String getSsid() { + return mSsid; + } + + /** This class is used to incrementally build VcnWifiUnderlyingNetworkPriority objects. */ + public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> { + @Nullable private String mSsid; + + /** Construct a Builder object. */ + public Builder() {} + + /** + * Set the required SSID. + * + * @param ssid the required SSID, or {@code null} if any SSID is acceptable. + */ + @NonNull + public Builder setSsid(@Nullable String ssid) { + mSsid = ssid; + return this; + } + + /** Build the VcnWifiUnderlyingNetworkPriority. */ + @NonNull + public VcnWifiUnderlyingNetworkPriority build() { + return new VcnWifiUnderlyingNetworkPriority(mNetworkQuality, mAllowMetered, mSsid); + } + + /** @hide */ + @Override + Builder self() { + return this; + } + } +} diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 0af322e885b1..09540132fe0d 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -528,6 +528,7 @@ public final class ApduServiceInfo implements Parcelable { public String toString() { StringBuilder out = new StringBuilder("ApduService: "); out.append(getComponent()); + out.append(", UID: " + mUid); out.append(", description: " + mDescription); out.append(", Static AID Groups: "); for (AidGroup aidGroup : mStaticAidGroups.values()) { @@ -546,7 +547,8 @@ public final class ApduServiceInfo implements Parcelable { if (!(o instanceof ApduServiceInfo)) return false; ApduServiceInfo thatService = (ApduServiceInfo) o; - return thatService.getComponent().equals(this.getComponent()); + return thatService.getComponent().equals(this.getComponent()) + && thatService.getUid() == this.getUid(); } @Override @@ -619,8 +621,9 @@ public final class ApduServiceInfo implements Parcelable { }; public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(" " + getComponent() + - " (Description: " + getDescription() + ")"); + pw.println(" " + getComponent() + + " (Description: " + getDescription() + ")" + + " (UID: " + getUid() + ")"); if (mOnHost) { pw.println(" On Host Service"); } else { diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index d498535ce52c..0a9fe90f2524 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager; import android.nfc.INfcCardEmulation; import android.nfc.NfcAdapter; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Log; @@ -83,6 +84,13 @@ public final class CardEmulation { public static final String EXTRA_SERVICE_COMPONENT = "component"; /** + * The caller userId extra for {@link #ACTION_CHANGE_DEFAULT}. + * + * @see #ACTION_CHANGE_DEFAULT + */ + public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID"; + + /** * Category used for NFC payment services. */ public static final String CATEGORY_PAYMENT = "payment"; @@ -269,8 +277,8 @@ public final class CardEmulation { if (CATEGORY_PAYMENT.equals(category)) { boolean preferForeground = false; try { - preferForeground = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0; + preferForeground = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.NFC_PAYMENT_FOREGROUND, UserHandle.myUserId()) != 0; } catch (SettingNotFoundException e) { } return preferForeground; @@ -829,6 +837,28 @@ public final class CardEmulation { /** * @hide */ + public boolean setDefaultForNextTap(int userId, ComponentName service) { + try { + return sService.setDefaultForNextTap(userId, service); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.setDefaultForNextTap(userId, service); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** + * @hide + */ public List<ApduServiceInfo> getServices(String category) { try { return sService.getServices(mContext.getUserId(), category); @@ -849,6 +879,28 @@ public final class CardEmulation { } /** + * @hide + */ + public List<ApduServiceInfo> getServices(String category, int userId) { + try { + return sService.getServices(userId, category); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return null; + } + try { + return sService.getServices(userId, category); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return null; + } + } + } + + /** * A valid AID according to ISO/IEC 7816-4: * <ul> * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars) diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java index 80e8579dd73f..557e41a2b103 100644 --- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java +++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java @@ -25,7 +25,6 @@ import android.content.pm.PackageManager; import android.nfc.INfcFCardEmulation; import android.nfc.NfcAdapter; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.HashMap; diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java index c2b33dd51b0c..f8f7dfe034b5 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java @@ -237,6 +237,7 @@ public final class NfcFServiceInfo implements Parcelable { public String toString() { StringBuilder out = new StringBuilder("NfcFService: "); out.append(getComponent()); + out.append(", UID: " + mUid); out.append(", description: " + mDescription); out.append(", System Code: " + mSystemCode); if (mDynamicSystemCode != null) { @@ -257,6 +258,7 @@ public final class NfcFServiceInfo implements Parcelable { NfcFServiceInfo thatService = (NfcFServiceInfo) o; if (!thatService.getComponent().equals(this.getComponent())) return false; + if (thatService.getUid() != this.getUid()) return false; if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false; if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false; if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false; @@ -321,8 +323,9 @@ public final class NfcFServiceInfo implements Parcelable { }; public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(" " + getComponent() + - " (Description: " + getDescription() + ")"); + pw.println(" " + getComponent() + + " (Description: " + getDescription() + ")" + + " (UID: " + getUid() + ")"); pw.println(" System Code: " + getSystemCode()); pw.println(" NFCID2: " + getNfcid2()); pw.println(" T3tPmm: " + getT3tPmm()); diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 2a344f6c2b6b..ad3de25fecc2 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -103,7 +103,7 @@ public class BaseBundle { * are unparcelled, mParcelledData willbe set to null. */ @UnsupportedAppUsage - Parcel mParcelledData = null; + volatile Parcel mParcelledData = null; /** * Whether {@link #mParcelledData} was generated by native code or not. @@ -182,13 +182,56 @@ public class BaseBundle { * @param b a Bundle to be copied. */ BaseBundle(BaseBundle b) { - copyInternal(b, false); + this(b, /* deep */ false); } /** - * Special constructor that does not initialize the bundle. + * Constructs a {@link BaseBundle} containing a copy of {@code from}. + * + * @param from The bundle to be copied. + * @param deep Whether is a deep or shallow copy. + * + * @hide */ - BaseBundle(boolean doInit) { + BaseBundle(BaseBundle from, boolean deep) { + synchronized (from) { + mClassLoader = from.mClassLoader; + + if (from.mMap != null) { + if (!deep) { + mMap = new ArrayMap<>(from.mMap); + } else { + final ArrayMap<String, Object> fromMap = from.mMap; + final int n = fromMap.size(); + mMap = new ArrayMap<>(n); + for (int i = 0; i < n; i++) { + mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i))); + } + } + } else { + mMap = null; + } + + final Parcel parcelledData; + if (from.mParcelledData != null) { + if (from.isEmptyParcel()) { + parcelledData = NoImagePreloadHolder.EMPTY_PARCEL; + mParcelledByNative = false; + } else { + parcelledData = Parcel.obtain(); + parcelledData.appendFrom(from.mParcelledData, 0, + from.mParcelledData.dataSize()); + parcelledData.setDataPosition(0); + mParcelledByNative = from.mParcelledByNative; + } + } else { + parcelledData = null; + mParcelledByNative = false; + } + + // Keep as last statement to ensure visibility of other fields + mParcelledData = parcelledData; + } } /** @@ -323,8 +366,8 @@ public class BaseBundle { } else { mMap.erase(); } - mParcelledData = null; mParcelledByNative = false; + mParcelledData = null; return; } @@ -358,8 +401,8 @@ public class BaseBundle { if (recycleParcel) { recycleParcel(parcelledData); } - mParcelledData = null; mParcelledByNative = false; + mParcelledData = null; } if (DEBUG) { Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) @@ -501,44 +544,7 @@ public class BaseBundle { mMap.clear(); } - void copyInternal(BaseBundle from, boolean deep) { - synchronized (from) { - if (from.mParcelledData != null) { - if (from.isEmptyParcel()) { - mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL; - mParcelledByNative = false; - } else { - mParcelledData = Parcel.obtain(); - mParcelledData.appendFrom(from.mParcelledData, 0, - from.mParcelledData.dataSize()); - mParcelledData.setDataPosition(0); - mParcelledByNative = from.mParcelledByNative; - } - } else { - mParcelledData = null; - mParcelledByNative = false; - } - - if (from.mMap != null) { - if (!deep) { - mMap = new ArrayMap<>(from.mMap); - } else { - final ArrayMap<String, Object> fromMap = from.mMap; - final int N = fromMap.size(); - mMap = new ArrayMap<>(N); - for (int i = 0; i < N; i++) { - mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i))); - } - } - } else { - mMap = null; - } - - mClassLoader = from.mClassLoader; - } - } - - Object deepCopyValue(Object value) { + private Object deepCopyValue(Object value) { if (value == null) { return null; } @@ -570,7 +576,7 @@ public class BaseBundle { return value; } - ArrayList deepcopyArrayList(ArrayList from) { + private ArrayList deepcopyArrayList(ArrayList from) { final int N = from.size(); ArrayList out = new ArrayList(N); for (int i=0; i<N; i++) { @@ -1717,9 +1723,9 @@ public class BaseBundle { if (length < 0) { throw new RuntimeException("Bad length in parcel: " + length); } else if (length == 0) { + mParcelledByNative = false; // Empty Bundle or end of data. mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL; - mParcelledByNative = false; return; } else if (length % 4 != 0) { throw new IllegalStateException("Bundle length is not aligned by 4: " + length); @@ -1757,8 +1763,8 @@ public class BaseBundle { + ": " + length + " bundle bytes starting at " + offset); p.setDataPosition(0); - mParcelledData = p; mParcelledByNative = isNativeBundle; + mParcelledData = p; } /** {@hide} */ diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index b677b6900d5c..b069fb336d55 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -53,7 +53,7 @@ import java.lang.reflect.Modifier; * * <p>Most developers will not implement this class directly, instead using the * <a href="{@docRoot}guide/components/aidl.html">aidl</a> tool to describe the desired - * interface, having it generate the appropriate Binder subclass. You can, + * interface, having it generate the appropriate Binder subclass. You can, * however, derive directly from Binder to implement your own custom RPC * protocol or simply instantiate a raw Binder object directly to use as a * token that can be shared across processes. @@ -63,17 +63,17 @@ import java.lang.reflect.Modifier; * To use this correctly, you must be doing so within the context of a top-level * application component (a {@link android.app.Service}, {@link android.app.Activity}, * or {@link android.content.ContentProvider}) that lets the system know your process - * should remain running.</p> + * should remain running. * * <p>You must keep in mind the situations in which your process * could go away, and thus require that you later re-create a new Binder and re-attach - * it when the process starts again. For example, if you are using this within an + * it when the process starts again. For example, if you are using this within an * {@link android.app.Activity}, your activity's process may be killed any time the * activity is not started; if the activity is later re-created you will need to * create a new Binder and hand it back to the correct place again; you need to be * aware that your process may be started for another reason (for example to receive * a broadcast) that will not involve re-creating the activity and thus run its code - * to create a new Binder.</p> + * to create a new Binder. * * @see IBinder */ @@ -94,14 +94,15 @@ public class Binder implements IBinder { /** * Value to represents that a calling work source is not set. * - * This constatnt needs to be kept in sync with IPCThreadState::kUnsetWorkSource. + * <p>This constant needs to be kept in sync with IPCThreadState::kUnsetWorkSource. * * @hide */ public static final int UNSET_WORKSOURCE = -1; /** - * Control whether dump() calls are allowed. + * Control whether {@link #dump(FileDescriptor, PrintWriter, String[]) dump()} + * calls are allowed. */ private static volatile String sDumpDisabled = null; @@ -188,7 +189,7 @@ public class Binder implements IBinder { sObserver = observer; } - /** {@hide} */ + /** @hide */ static volatile boolean sWarnOnBlocking = false; /** @@ -207,8 +208,8 @@ public class Binder implements IBinder { /** * Allow blocking calls on the given interface, overriding the requested * value of {@link #setWarnOnBlocking(boolean)}. - * <p> - * This should only be rarely called when you are <em>absolutely sure</em> + * + * <p>This should only be rarely called when you are <em>absolutely sure</em> * the remote interface is a built-in system component that can never be * upgraded. In particular, this <em>must never</em> be called for * interfaces hosted by package that could be upgraded or replaced, @@ -258,7 +259,9 @@ public class Binder implements IBinder { ThreadLocal.withInitial(() -> sWarnOnBlocking); /** - * Allow blocking calls for the current thread. See {@link #allowBlocking}. + * Allow blocking calls for the current thread. + * + * @see {@link #allowBlocking}. * * @hide */ @@ -267,7 +270,9 @@ public class Binder implements IBinder { } /** - * Reset the current thread to the default blocking behavior. See {@link #defaultBlocking}. + * Reset the current thread to the default blocking behavior. + * + * @see {@link #defaultBlocking}. * * @hide */ @@ -286,10 +291,10 @@ public class Binder implements IBinder { /** * Return the ID of the process that sent you the current transaction - * that is being processed. This pid can be used with higher-level + * that is being processed. This PID can be used with higher-level * system services to determine its identity and check permissions. * If the current thread is not currently executing an incoming transaction, - * then its own pid is returned. + * then its own PID is returned. * * Warning: oneway transactions do not receive PID. */ @@ -297,11 +302,11 @@ public class Binder implements IBinder { public static final native int getCallingPid(); /** - * Return the Linux uid assigned to the process that sent you the - * current transaction that is being processed. This uid can be used with + * Return the Linux UID assigned to the process that sent you the + * current transaction that is being processed. This UID can be used with * higher-level system services to determine its identity and check - * permissions. If the current thread is not currently executing an - * incoming transaction, then its own uid is returned. + * permissions. If the current thread is not currently executing an + * incoming transaction, then its own UID is returned. */ @CriticalNative public static final native int getCallingUid(); @@ -316,11 +321,11 @@ public class Binder implements IBinder { public static final native boolean isDirectlyHandlingTransaction(); /** - * Return the Linux uid assigned to the process that sent the transaction + * Return the Linux UID assigned to the process that sent the transaction * currently being processed. * * @throws IllegalStateException if the current thread is not currently - * executing an incoming transaction. + * executing an incoming transaction. */ public static final int getCallingUidOrThrow() { if (!isDirectlyHandlingTransaction()) { @@ -332,18 +337,20 @@ public class Binder implements IBinder { /** * Return the UserHandle assigned to the process that sent you the - * current transaction that is being processed. This is the user - * of the caller. It is distinct from {@link #getCallingUid()} in that a + * current transaction that is being processed. This is the user + * of the caller. It is distinct from {@link #getCallingUid()} in that a * particular user will have multiple distinct apps running under it each - * with their own uid. If the current thread is not currently executing an + * with their own UID. If the current thread is not currently executing an * incoming transaction, then its own UserHandle is returned. + * + * @see UserHandle */ public static final @NonNull UserHandle getCallingUserHandle() { return UserHandle.of(UserHandle.getUserId(getCallingUid())); } /** - * Reset the identity of the incoming IPC on the current thread. This can + * Reset the identity of the incoming IPC on the current thread. This can * be useful if, while handling an incoming call, you will be calling * on interfaces of other objects that may be local to your process and * need to do permission checks on the calls coming into them (so they @@ -376,10 +383,10 @@ public class Binder implements IBinder { /** * Convenience method for running the provided action enclosed in - * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} + * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}. * - * Any exception thrown by the given action will be caught and rethrown after the call to - * {@link #restoreCallingIdentity} + * <p>Any exception thrown by the given action will be caught and + * rethrown after the call to {@link #restoreCallingIdentity}. * * @hide */ @@ -400,10 +407,10 @@ public class Binder implements IBinder { /** * Convenience method for running the provided action enclosed in - * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result + * {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result. * - * Any exception thrown by the given action will be caught and rethrown after the call to - * {@link #restoreCallingIdentity} + * <p>Any exception thrown by the given action will be caught and rethrown after + * the call to {@link #restoreCallingIdentity}. * * @hide */ @@ -428,12 +435,13 @@ public class Binder implements IBinder { * * <p>The StrictMode settings are kept in two places: a Java-level * threadlocal for libcore/Dalvik, and a native threadlocal (set - * here) for propagation via Binder calls. This is a little + * here) for propagation via Binder calls. This is a little * unfortunate, but necessary to break otherwise more unfortunate * dependencies either of Dalvik on Android, or Android * native-only code on Dalvik. * * @see StrictMode + * * @hide */ @CriticalNative @@ -443,6 +451,7 @@ public class Binder implements IBinder { * Gets the current native thread-local StrictMode policy mask. * * @see #setThreadStrictModePolicy + * * @hide */ @CriticalNative @@ -459,7 +468,7 @@ public class Binder implements IBinder { * reasons, we only support one UID. This UID represents the original user responsible for the * binder calls. * - * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after setting the + * <p>{@link #restoreCallingWorkSource(long)} must always be called after setting the * worksource. * * <p>A typical use case would be @@ -477,16 +486,16 @@ public class Binder implements IBinder { * * @param workSource The original UID responsible for the binder call. * @return token to restore original work source. - **/ + */ @CriticalNative public static final native long setCallingWorkSourceUid(int workSource); /** * Returns the work source set by the caller. * - * Unlike {@link Binder#getCallingUid()}, this result of this method cannot be trusted. The + * <p>Unlike {@link #getCallingUid()}, this result of this method cannot be trusted. The * caller can set the value to whatever they want. Only use this value if you trust the calling - * uid. + * UID. * * @return The original UID responsible for the binder transaction. */ @@ -499,7 +508,7 @@ public class Binder implements IBinder { * <p>The work source will be propagated for future outgoing binder transactions * executed on this thread. * - * <p>{@link Binder#restoreCallingWorkSource(long)} must always be called after clearing the + * <p>{@link #restoreCallingWorkSource(long)} must always be called after clearing the * worksource. * * <p>A typical use case would be @@ -513,13 +522,13 @@ public class Binder implements IBinder { * </pre> * * @return token to restore original work source. - **/ + */ @CriticalNative public static final native long clearCallingWorkSource(); /** * Restores the work source on this thread using a token returned by - * {@link #setCallingWorkSourceUid(int) or {@link clearCallingWorkSource()}. + * {@link #setCallingWorkSourceUid(int)} or {@link #clearCallingWorkSource()}. * * <p>A typical use case would be * <pre> @@ -530,7 +539,7 @@ public class Binder implements IBinder { * Binder.restoreCallingWorkSource(token); * } * </pre> - **/ + */ @CriticalNative public static final native void restoreCallingWorkSource(long token); @@ -553,7 +562,7 @@ public class Binder implements IBinder { * 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. + * <p>This must be called before the object is sent to another process. * * @hide */ @@ -561,7 +570,7 @@ public class Binder implements IBinder { /** * Flush any Binder commands pending in the current thread to the kernel - * driver. This can be + * driver. This can be * useful to call before performing an operation that may block for a long * time, to ensure that any pending object references have been released * in order to prevent the process from holding on to objects longer than @@ -570,7 +579,7 @@ public class Binder implements IBinder { public static final native void flushPendingCommands(); /** - * Add the calling thread to the IPC thread pool. This function does + * Add the calling thread to the IPC thread pool. This function does * not return until the current process is exiting. */ public static final void joinThreadPool() { @@ -579,6 +588,7 @@ public class Binder implements IBinder { /** * Returns true if the specified interface is a proxy. + * * @hide */ public static final boolean isProxy(IInterface iface) { @@ -588,6 +598,7 @@ public class Binder implements IBinder { /** * Call blocks until the number of executing binder threads is less * than the maximum number of binder threads allowed for this process. + * * @hide */ public static final native void blockUntilThreadAvailable(); @@ -595,7 +606,7 @@ public class Binder implements IBinder { /** * Default constructor just initializes the object. * - * If you're creating a Binder token (a Binder object without an attached interface), + * <p>If you're creating a Binder token (a Binder object without an attached interface), * you should use {@link #Binder(String)} instead. */ public Binder() { @@ -605,7 +616,7 @@ public class Binder implements IBinder { /** * Constructor for creating a raw Binder object (token) along with a descriptor. * - * The descriptor of binder objects usually specifies the interface they are implementing. + * <p>The descriptor of binder objects usually specifies the interface they are implementing. * In case of binder tokens, no interface is implemented, and the descriptor can be used * as a sort of tag to help identify the binder token. This will help identify remote * references to these objects more easily when debugging. @@ -614,7 +625,7 @@ public class Binder implements IBinder { * Instead of creating multiple tokens with the same descriptor, consider adding a suffix to * help identify them. */ - public Binder(@Nullable String descriptor) { + public Binder(@Nullable String descriptor) { mObject = getNativeBBinderHolder(); NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject); @@ -631,9 +642,9 @@ public class Binder implements IBinder { /** * Convenience method for associating a specific interface with the Binder. - * After calling, queryLocalInterface() will be implemented for you - * to return the given owner IInterface when the corresponding - * descriptor is requested. + * After calling, {@link #queryLocalInterface(String) queryLocalInterface()} + * will be implemented for you to return the given owner IInterface when + * the corresponding descriptor is requested. */ public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) { mOwner = owner; @@ -666,8 +677,8 @@ public class Binder implements IBinder { } /** - * Use information supplied to attachInterface() to return the - * associated IInterface if it matches the requested + * Use information supplied to {@link #attachInterface attachInterface()} + * to return the associated {@link IInterface} if it matches the requested * descriptor. */ public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) { @@ -678,14 +689,15 @@ public class Binder implements IBinder { } /** - * Control disabling of dump calls in this process. This is used by the system + * Control disabling of dump calls in this process. This is used by the system * process watchdog to disable incoming dump calls while it has detecting the system - * is hung and is reporting that back to the activity controller. This is to + * is hung and is reporting that back to the activity controller. This is to * prevent the controller from getting hung up on bug reports at this point. - * @hide * * @param msg The message to show instead of the dump; if null, dumps are * re-enabled. + * + * @hide */ public static void setDumpDisabled(String msg) { sDumpDisabled = msg; @@ -694,7 +706,8 @@ public class Binder implements IBinder { /** * Listener to be notified about each proxy-side binder call. * - * See {@link setProxyTransactListener}. + * @see {@link #setProxyTransactListener}. + * * @hide */ @SystemApi @@ -702,7 +715,8 @@ public class Binder implements IBinder { /** * Called before onTransact. * - * @return an object that will be passed back to #onTransactEnded (or null). + * @return an object that will be passed back to {@link #onTransactEnded} (or null)., + * * @hide */ @Nullable @@ -713,15 +727,15 @@ public class Binder implements IBinder { /** * Called before onTransact. * - * @return an object that will be passed back to #onTransactEnded (or null). + * @return an object that will be passed back to {@link #onTransactEnded} (or null). */ @Nullable Object onTransactStarted(@NonNull IBinder binder, int transactionCode); /** - * Called after onTranact (even when an exception is thrown). + * Called after onTransact (even when an exception is thrown). * - * @param session The object return by #onTransactStarted. + * @param session The object return by {@link #onTransactStarted}. */ void onTransactEnded(@Nullable Object session); } @@ -732,16 +746,17 @@ public class Binder implements IBinder { * <li>By default, this listener will propagate the worksource if the outgoing call happens on * the same thread as the incoming binder call. * <li>Custom attribution can be done by calling {@link ThreadLocalWorkSource#setUid(int)}. + * * @hide */ public static class PropagateWorkSourceTransactListener implements ProxyTransactListener { @Override public Object onTransactStarted(IBinder binder, int transactionCode) { - // Note that {@link Binder#getCallingUid()} is already set to the UID of the current - // process when this method is called. - // - // We use ThreadLocalWorkSource instead. It also allows feature owners to set - // {@link ThreadLocalWorkSource#set(int) manually to attribute resources to a UID. + // Note that {@link #getCallingUid()} is already set to the UID of the current + // process when this method is called. + // + // We use {@link ThreadLocalWorkSource} instead. It also allows feature owners to set + // {@link ThreadLocalWorkSource#set(int)} manually to attribute resources to a UID. int uid = ThreadLocalWorkSource.getUid(); if (uid != ThreadLocalWorkSource.UID_NONE) { return Binder.setCallingWorkSourceUid(uid); @@ -770,6 +785,7 @@ public class Binder implements IBinder { * <li>The listener is called on the critical path of the binder transaction so be careful about * performance. * <li>Never execute another binder transaction inside the listener. + * * @hide */ @SystemApi @@ -778,7 +794,7 @@ public class Binder implements IBinder { } /** - * Default implementation is a stub that returns false. You will want + * Default implementation is a stub that returns false. You will want * to override this to do the appropriate unmarshalling of transactions. * * <p>If you want to call this, call transact(). @@ -786,15 +802,14 @@ public class Binder implements IBinder { * <p>Implementations that are returning a result should generally use * {@link Parcel#writeNoException() Parcel.writeNoException} and * {@link Parcel#writeException(Exception) Parcel.writeException} to propagate - * exceptions back to the caller.</p> + * exceptions back to the caller. * - * @param code The action to perform. This should - * be a number between {@link #FIRST_CALL_TRANSACTION} and - * {@link #LAST_CALL_TRANSACTION}. + * @param code The action to perform. This should be a number between + * {@link #FIRST_CALL_TRANSACTION} and {@link #LAST_CALL_TRANSACTION}. * @param data Marshalled data being received from the caller. * @param reply If the caller is expecting a result back, it should be marshalled * in to here. - * @param flags Additional operation flags. Either 0 for a normal + * @param flags Additional operation flags. Either 0 for a normal * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC. * * @return Return true on a successful call; returning false is generally used to @@ -856,10 +871,12 @@ public class Binder implements IBinder { * Resolves a transaction code to a human readable name. * * <p>Default implementation is a stub that returns null. + * * <p>AIDL generated code will return the original method name. * * @param transactionCode The code to resolve. * @return A human readable name. + * * @hide */ public @Nullable String getTransactionName(int transactionCode) { @@ -925,7 +942,7 @@ public class Binder implements IBinder { * Print the object's state into the given stream. * * @param fd The raw file descriptor that the dump is being sent to. - * @param fout The file to which you should dump your state. This will be + * @param fout The file to which you should dump your state. This will be * closed for you after you return. * @param args additional arguments to the dump request. */ @@ -941,6 +958,7 @@ public class Binder implements IBinder { * @param callback Callback through which to interact with the invoking shell. * @param resultReceiver Called when the command has finished executing, with the result code. * @throws RemoteException + * * @hide */ public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, @@ -958,7 +976,8 @@ public class Binder implements IBinder { * * <p class="caution">Note: no permission checking is done before calling this method; you must * apply any security checks as appropriate for the command being executed. - * Consider using {@link ShellCommand} to help in the implementation.</p> + * Consider using {@link ShellCommand} to help in the implementation. + * * @hide */ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, @@ -1013,7 +1032,7 @@ public class Binder implements IBinder { * System services can implement this method to implement ADB shell commands. * * <p>A system binder service can implement it to handle shell commands on ADB. For example, - * the Job Scheduler service implements it to handle <code>adb shell cmd jobscheduler</code>. + * the Job Scheduler service implements it to handle {@code adb shell cmd jobscheduler}. * * <p>Commands are only executable by ADB shell; i.e. only {@link Process#SHELL_UID} and * {@link Process#ROOT_UID} can call them. @@ -1022,8 +1041,8 @@ public class Binder implements IBinder { * @param out standard output * @param err standard error * @param args arguments passed to the command. Can be empty. The first argument is typically - * a subcommand, such as {@code run} for {@code adb shell cmd jobscheduler run}. - * @return the status code returned from the <code>cmd</code> command. + * a subcommand, such as {@code run} for {@code adb shell cmd jobscheduler run}. + * @return the status code returned from the {@code cmd} command. * * @hide */ @@ -1051,7 +1070,7 @@ public class Binder implements IBinder { public final native void setExtension(@Nullable IBinder extension); /** - * Default implementation rewinds the parcels and calls onTransact. On + * Default implementation rewinds the parcels and calls onTransact. On * the remote side, transact calls into the binder to do the IPC. */ public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, @@ -1083,7 +1102,7 @@ public class Binder implements IBinder { static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) { if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) { - // Trying to send > 800k, this is way too much + // Trying to send > 800k, this is way too much. StringBuilder sb = new StringBuilder(); sb.append(msg); sb.append(": on "); @@ -1107,7 +1126,7 @@ public class Binder implements IBinder { private static native long getNativeBBinderHolder(); /** - * By default, we use the calling uid since we can always trust it. + * By default, we use the calling UID since we can always trust it. */ private static volatile BinderInternal.WorkSourceProvider sWorkSourceProvider = (x) -> Binder.getCallingUid(); @@ -1122,6 +1141,7 @@ public class Binder implements IBinder { * <li>The callback is called on the critical path of the binder transaction so be careful about * performance. * <li>Never execute another binder transaction inside the callback. + * * @hide */ public static void setWorkSourceProvider(BinderInternal.WorkSourceProvider workSourceProvider) { @@ -1131,12 +1151,12 @@ public class Binder implements IBinder { sWorkSourceProvider = workSourceProvider; } - // Entry point from android_util_Binder.cpp's onTransact + // Entry point from android_util_Binder.cpp's onTransact. @UnsupportedAppUsage private boolean execTransact(int code, long dataObj, long replyObj, int flags) { // At that point, the parcel request headers haven't been parsed so we do not know what - // WorkSource the caller has set. Use calling uid as the default. + // {@link WorkSource} the caller has set. Use calling UID as the default. final int callingUid = Binder.getCallingUid(); final long origWorkSource = ThreadLocalWorkSource.setUid(callingUid); try { @@ -1154,17 +1174,18 @@ public class Binder implements IBinder { observer != null ? observer.callStarted(this, code, UNSET_WORKSOURCE) : null; Parcel data = Parcel.obtain(dataObj); Parcel reply = Parcel.obtain(replyObj); - // theoretically, we should call transact, which will call onTransact, + // Theoretically, we should call transact, which will call onTransact, // but all that does is rewind it, and we just got these from an IPC, // so we'll just call it directly. boolean res; // Log any exceptions as warnings, don't silently suppress them. - // If the call was FLAG_ONEWAY then these exceptions disappear into the ether. + // If the call was {@link IBinder#FLAG_ONEWAY} then these exceptions + // disappear into the ether. final boolean tracingEnabled = Binder.isTracingEnabled(); try { final BinderCallHeavyHitterWatcher heavyHitterWatcher = sHeavyHitterWatcher; if (heavyHitterWatcher != null) { - // Notify the heavy hitter watcher, if it's enabled + // Notify the heavy hitter watcher, if it's enabled. heavyHitterWatcher.onTransaction(callingUid, getClass(), code); } if (tracingEnabled) { @@ -1197,7 +1218,7 @@ public class Binder implements IBinder { Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e); } } else { - // Clear the parcel before writing the exception + // Clear the parcel before writing the exception. reply.setDataSize(0); reply.setDataPosition(0); reply.writeException(e); @@ -1209,7 +1230,7 @@ public class Binder implements IBinder { } if (observer != null) { // The parcel RPC headers have been called during onTransact so we can now access - // the worksource uid from the parcel. + // the worksource UID from the parcel. final int workSourceUid = sWorkSourceProvider.resolveWorkSourceUid( data.readCallingWorkSourceUid()); observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid); @@ -1220,9 +1241,9 @@ public class Binder implements IBinder { data.recycle(); // Just in case -- we are done with the IPC, so there should be no more strict - // mode violations that have gathered for this thread. Either they have been + // mode violations that have gathered for this thread. Either they have been // parceled and are now in transport off to the caller, or we are returning back - // to the main transaction loop to wait for another incoming transaction. Either + // to the main transaction loop to wait for another incoming transaction. Either // way, strict mode begone! StrictMode.clearGatheredViolations(); return res; diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 92eb7a5d047e..b2bbfd6c163d 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -102,6 +102,18 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** + * Constructs a {@link Bundle} containing a copy of {@code from}. + * + * @param from The bundle to be copied. + * @param deep Whether is a deep or shallow copy. + * + * @hide + */ + Bundle(Bundle from, boolean deep) { + super(from, deep); + } + + /** * If {@link #mParcelledData} is not null, copy the HAS FDS bit from it because it's fast. * Otherwise (if {@link #mParcelledData} is already null), leave {@link #FLAG_HAS_FDS_KNOWN} * unset, because scanning a map is slower. We'll do it lazily in @@ -167,13 +179,6 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** - * Constructs a Bundle without initializing it. - */ - Bundle(boolean doInit) { - super(doInit); - } - - /** * Make a Bundle for a single key/value pair. * * @hide @@ -260,9 +265,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { * are referenced as-is and not copied in any way. */ public Bundle deepCopy() { - Bundle b = new Bundle(false); - b.copyInternal(this, true); - return b; + return new Bundle(this, /* deep */ true); } /** @@ -324,28 +327,10 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { */ public boolean hasFileDescriptors() { if ((mFlags & FLAG_HAS_FDS_KNOWN) == 0) { - boolean fdFound = false; // keep going until we find one or run out of data - - if (mParcelledData != null) { - if (mParcelledData.hasFileDescriptors()) { - fdFound = true; - } - } else { - // It's been unparcelled, so we need to walk the map - for (int i=mMap.size()-1; i>=0; i--) { - Object obj = mMap.valueAt(i); - if (Parcel.hasFileDescriptors(obj)) { - fdFound = true; - break; - } - } - } - - if (fdFound) { - mFlags |= FLAG_HAS_FDS; - } else { - mFlags &= ~FLAG_HAS_FDS; - } + Parcel p = mParcelledData; + mFlags = (Parcel.hasFileDescriptors((p != null) ? p : mMap)) + ? mFlags | FLAG_HAS_FDS + : mFlags & ~FLAG_HAS_FDS; mFlags |= FLAG_HAS_FDS_KNOWN; } return (mFlags & FLAG_HAS_FDS) != 0; diff --git a/core/java/android/os/IStatsBootstrapAtomService.aidl b/core/java/android/os/IStatsBootstrapAtomService.aidl new file mode 100644 index 000000000000..9d1df67fa19d --- /dev/null +++ b/core/java/android/os/IStatsBootstrapAtomService.aidl @@ -0,0 +1,36 @@ +/* + * Copyright 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.StatsBootstrapAtom; + +/** + * IBootstrapAtomService interface exposes an interface for processes that launch in the + * bootstrap namespace to push atoms to statsd. + * + * @hide + */ +oneway interface IStatsBootstrapAtomService { + /** + * Push an atom to StatsBootstrapAtomService, which will forward it to statsd. + * + * @param atom - parcelled representation of the atom to log. + * + * Errors are reported as service specific errors. + */ + void reportBootstrapAtom(in StatsBootstrapAtom atom); +}
\ No newline at end of file diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl index d83d94a8ec77..15e3ce25122b 100644 --- a/core/java/android/os/ISystemConfig.aidl +++ b/core/java/android/os/ISystemConfig.aidl @@ -16,6 +16,8 @@ package android.os; +import android.content.ComponentName; + /** * Binder interface to query SystemConfig in the system server. * {@hide} @@ -44,5 +46,5 @@ interface ISystemConfig { /** * @see SystemConfigManager#getEnabledComponentOverrides */ - List<String> getEnabledComponentOverrides(String packageName); + List<ComponentName> getEnabledComponentOverrides(String packageName); } diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 92861fba40a3..1e424d1194ae 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -1,18 +1,6 @@ # Haptics -per-file CombinedVibrationEffect.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file CombinedVibrationEffect.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file ExternalVibration.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file ExternalVibration.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file IExternalVibrationController.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file IExternalVibratorService.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file IVibratorManagerService.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file NullVibrator.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file SystemVibrator.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file SystemVibratorManager.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file VibrationEffect.aidl = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file VibrationEffect.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file Vibrator.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file VibratorManager.java = file:/services/core/java/com/android/server/vibrator/OWNERS +per-file *Vibration* = file:/services/core/java/com/android/server/vibrator/OWNERS +per-file *Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS # PowerManager per-file IPowerManager.aidl = michaelwr@google.com, santoscordon@google.com diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index dd0cb8cc62ae..8894c85fa7b4 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -62,6 +62,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Function; +import java.util.function.IntFunction; import java.util.function.Supplier; /** @@ -178,8 +180,12 @@ import java.util.function.Supplier; * {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()}, * {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])}, * {@link #createBinderArray()}, + * {@link #writeInterfaceArray(T[])}, {@link #readInterfaceArray(T[], Function)}, + * {@link #createInterfaceArray(IntFunction, Function)}, * {@link #writeBinderList(List)}, {@link #readBinderList(List)}, - * {@link #createBinderArrayList()}.</p> + * {@link #createBinderArrayList()}, + * {@link #writeInterfaceList(List)}, {@link #readInterfaceList(List, Function)}, + * {@link #createInterfaceArrayList(Function)}.</p> * * <p>FileDescriptor objects, representing raw Linux file descriptor identifiers, * can be written and {@link ParcelFileDescriptor} objects returned to operate @@ -747,57 +753,70 @@ public final class Parcel { } /** - * Check if the object used in {@link #readValue(ClassLoader)} / {@link #writeValue(Object)} - * has file descriptors. + * Check if the object has file descriptors. + * + * <p>Objects supported are {@link Parcel} and objects that can be passed to {@link + * #writeValue(Object)}} * * <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method * for that. * - * @throws IllegalArgumentException if you provide any object not supported by above methods. - * Most notably, if you pass {@link Parcel}, this method will throw, for that check - * {@link Parcel#hasFileDescriptors()} + * @throws IllegalArgumentException if you provide any object not supported by above methods + * (including if the unsupported object is inside a nested container). * * @hide */ public static boolean hasFileDescriptors(Object value) { - if (value instanceof LazyValue) { - return ((LazyValue) value).hasFileDescriptors(); + if (value instanceof Parcel) { + Parcel parcel = (Parcel) value; + if (parcel.hasFileDescriptors()) { + return true; + } + } else if (value instanceof LazyValue) { + LazyValue lazy = (LazyValue) value; + if (lazy.hasFileDescriptors()) { + return true; + } } else if (value instanceof Parcelable) { - if ((((Parcelable) value).describeContents() - & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) { + Parcelable parcelable = (Parcelable) value; + if ((parcelable.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) { return true; } - } else if (value instanceof Parcelable[]) { - Parcelable[] array = (Parcelable[]) value; - for (int n = array.length - 1; n >= 0; n--) { - Parcelable p = array[n]; - if (p != null && ((p.describeContents() - & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) { + } else if (value instanceof ArrayMap<?, ?>) { + ArrayMap<?, ?> map = (ArrayMap<?, ?>) value; + for (int i = 0, n = map.size(); i < n; i++) { + if (hasFileDescriptors(map.keyAt(i)) + || hasFileDescriptors(map.valueAt(i))) { + return true; + } + } + } else if (value instanceof Map<?, ?>) { + Map<?, ?> map = (Map<?, ?>) value; + for (Map.Entry<?, ?> entry : map.entrySet()) { + if (hasFileDescriptors(entry.getKey()) + || hasFileDescriptors(entry.getValue())) { + return true; + } + } + } else if (value instanceof List<?>) { + List<?> list = (List<?>) value; + for (int i = 0, n = list.size(); i < n; i++) { + if (hasFileDescriptors(list.get(i))) { return true; } } } else if (value instanceof SparseArray<?>) { SparseArray<?> array = (SparseArray<?>) value; - for (int n = array.size() - 1; n >= 0; n--) { - Object object = array.valueAt(n); - if (object instanceof Parcelable) { - Parcelable p = (Parcelable) object; - if (p != null && (p.describeContents() - & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) { - return true; - } + for (int i = 0, n = array.size(); i < n; i++) { + if (hasFileDescriptors(array.valueAt(i))) { + return true; } } - } else if (value instanceof ArrayList<?>) { - ArrayList<?> array = (ArrayList<?>) value; - for (int n = array.size() - 1; n >= 0; n--) { - Object object = array.get(n); - if (object instanceof Parcelable) { - Parcelable p = (Parcelable) object; - if (p != null && ((p.describeContents() - & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) { - return true; - } + } else if (value instanceof Object[]) { + Object[] array = (Object[]) value; + for (int i = 0, n = array.length; i < n; i++) { + if (hasFileDescriptors(array[i])) { + return true; } } } else { @@ -827,6 +846,19 @@ public final class Parcel { } /** + * Verify there are no bytes left to be read on the Parcel. + * + * @throws BadParcelableException If the current position hasn't reached the end of the Parcel. + * When used over binder, this exception should propagate to the caller. + */ + public void enforceNoDataAvail() { + final int n = dataAvail(); + if (n > 0) { + throw new BadParcelableException("Parcel data not fully consumed, unread size: " + n); + } + } + + /** * Writes the work source uid to the request headers. * * <p>It requires the headers to have been written/read already to replace the work source. @@ -1717,6 +1749,30 @@ public final class Parcel { } /** + * Flatten a homogeneous array containing an IInterface type into the parcel, + * at the current dataPosition() and growing dataCapacity() if needed. The + * type of the objects in the array must be one that implements IInterface. + * + * @param val The array of objects to be written. + * + * @see #createInterfaceArray + * @see #readInterfaceArray + * @see IInterface + */ + public final <T extends IInterface> void writeInterfaceArray( + @SuppressLint("ArrayReturn") @Nullable T[] val) { + if (val != null) { + int N = val.length; + writeInt(N); + for (int i=0; i<N; i++) { + writeStrongInterface(val[i]); + } + } else { + writeInt(-1); + } + } + + /** * @hide */ public final void writeCharSequenceArray(@Nullable CharSequence[] val) { @@ -1772,6 +1828,50 @@ public final class Parcel { } /** + * Read and return a new array of T (IInterface) from the parcel. + * + * @return the IInterface array of type T + * @param newArray a function to create an array of T with a given length + * @param asInterface a function to convert IBinder object into T (IInterface) + */ + @SuppressLint({"ArrayReturn", "NullableCollection", "SamShouldBeLast"}) + @Nullable + public final <T extends IInterface> T[] createInterfaceArray( + @NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) { + int N = readInt(); + if (N >= 0) { + T[] val = newArray.apply(N); + for (int i=0; i<N; i++) { + val[i] = asInterface.apply(readStrongBinder()); + } + return val; + } else { + return null; + } + } + + /** + * Read an array of T (IInterface) from a parcel. + * + * @param asInterface a function to convert IBinder object into T (IInterface) + * + * @throws BadParcelableException Throws BadParcelableException if the length of `val` + * mismatches the number of items in the parcel. + */ + public final <T extends IInterface> void readInterfaceArray( + @SuppressLint("ArrayReturn") @NonNull T[] val, + @NonNull Function<IBinder, T> asInterface) { + int N = readInt(); + if (N == val.length) { + for (int i=0; i<N; i++) { + val[i] = asInterface.apply(readStrongBinder()); + } + } else { + throw new BadParcelableException("bad array lengths"); + } + } + + /** * Flatten a List containing a particular object type into the parcel, at * the current dataPosition() and growing dataCapacity() if needed. The * type of the objects in the list must be one that implements Parcelable. @@ -1885,6 +1985,28 @@ public final class Parcel { } /** + * Flatten a {@code List} containing T (IInterface) objects into this parcel + * at the current position. They can later be retrieved with + * {@link #createInterfaceArrayList} or {@link #readInterfaceList}. + * + * @see #createInterfaceArrayList + * @see #readInterfaceList + */ + public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) { + if (val == null) { + writeInt(-1); + return; + } + int N = val.size(); + int i=0; + writeInt(N); + while (i < N) { + writeStrongInterface(val.get(i)); + i++; + } + } + + /** * Flatten a {@code List} containing arbitrary {@code Parcelable} objects into this parcel * at the current position. They can later be retrieved using * {@link #readParcelableList(List, ClassLoader)} if required. @@ -2881,17 +3003,45 @@ public final class Parcel { * Please use {@link #readBundle(ClassLoader)} instead (whose data must have * been written with {@link #writeBundle}. Read into an existing Map object * from the parcel at the current dataPosition(). + * + * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this + * method is still preferred use the type-safer version {@link #readMap(Map, ClassLoader, + * Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) { - int N = readInt(); - readMapInternal(outVal, N, loader); + int n = readInt(); + readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null); + } + + /** + * Same as {@link #readMap(Map, ClassLoader)} but accepts {@code clazzKey} and + * {@code clazzValue} parameter as the types required for each key and value pair. + * + * @throws BadParcelableException If the item to be deserialized is not an instance of that + * class or any of its children class + */ + public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal, + @Nullable ClassLoader loader, @NonNull Class<K> clazzKey, + @NonNull Class<V> clazzValue) { + Objects.requireNonNull(clazzKey); + Objects.requireNonNull(clazzValue); + int n = readInt(); + readMapInternal(outVal, n, loader, clazzKey, clazzValue); } /** * Read into an existing List object from the parcel at the current * dataPosition(), using the given class loader to load any enclosed * Parcelables. If it is null, the default class loader is used. + * + * @deprecated Use the type-safer version {@link #readList(List, ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to + * use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the items' + * class is final) since this is also more performant. Note that changing to the latter + * also requires changing the writes. */ + @Deprecated public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) { int N = readInt(); readListInternal(outVal, N, loader, /* clazz */ null); @@ -2918,20 +3068,46 @@ public final class Parcel { * object from the parcel at the current dataPosition(), using the given * class loader to load any enclosed Parcelables. Returns null if * the previously written map object was null. + * + * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this + * method is still preferred use the type-safer version {@link #readHashMap(ClassLoader, + * Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable - public final HashMap readHashMap(@Nullable ClassLoader loader) - { - int N = readInt(); - if (N < 0) { + public HashMap readHashMap(@Nullable ClassLoader loader) { + int n = readInt(); + if (n < 0) { return null; } - HashMap m = new HashMap(N); - readMapInternal(m, N, loader); + HashMap m = new HashMap(n); + readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null); return m; } /** + * Same as {@link #readHashMap(ClassLoader)} but accepts {@code clazzKey} and + * {@code clazzValue} parameter as the types required for each key and value pair. + * + * @throws BadParcelableException if the item to be deserialized is not an instance of that + * class or any of its children class + */ + @SuppressLint({"ConcreteCollection", "NullableCollection"}) + @Nullable + public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader, + @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) { + Objects.requireNonNull(clazzKey); + Objects.requireNonNull(clazzValue); + int n = readInt(); + if (n < 0) { + return null; + } + HashMap<K, V> map = new HashMap<>(n); + readMapInternal(map, n, loader, clazzKey, clazzValue); + return map; + } + + /** * Read and return a new Bundle object from the parcel at the current * dataPosition(). Returns null if the previously written Bundle object was * null. @@ -3100,7 +3276,14 @@ public final class Parcel { * dataPosition(). Returns null if the previously written list object was * null. The given class loader will be used to load any enclosed * Parcelables. + * + * @deprecated Use the type-safer version {@link #readArrayList(ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to + * use {@link #createTypedArrayList(Parcelable.Creator)} if possible (eg. if the items' + * class is final) since this is also more performant. Note that changing to the latter + * also requires changing the writes. */ + @Deprecated @Nullable public ArrayList readArrayList(@Nullable ClassLoader loader) { return readArrayListInternal(loader, /* clazz */ null); @@ -3127,7 +3310,14 @@ public final class Parcel { * dataPosition(). Returns null if the previously written array was * null. The given class loader will be used to load any enclosed * Parcelables. + * + * @deprecated Use the type-safer version {@link #readArray(ClassLoader, Class)} starting from + * Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to use + * {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the items' class is + * final) since this is also more performant. Note that changing to the latter also + * requires changing the writes. */ + @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader loader) { return readArrayInternal(loader, /* clazz */ null); @@ -3153,7 +3343,14 @@ public final class Parcel { * dataPosition(). Returns null if the previously written list object was * null. The given class loader will be used to load any enclosed * Parcelables. + * + * @deprecated Use the type-safer version {@link #readSparseArray(ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to + * use {@link #createTypedSparseArray(Parcelable.Creator)} if possible (eg. if the items' + * class is final) since this is also more performant. Note that changing to the latter + * also requires changing the writes. */ + @Deprecated @Nullable public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) { return readSparseArrayInternal(loader, /* clazz */ null); @@ -3367,6 +3564,32 @@ public final class Parcel { } /** + * Read and return a new ArrayList containing T (IInterface) objects from + * the parcel that was written with {@link #writeInterfaceList} at the + * current dataPosition(). Returns null if the + * previously written list object was null. + * + * @return A newly created ArrayList containing T (IInterface) + * + * @see #writeInterfaceList + */ + @SuppressLint({"ConcreteCollection", "NullableCollection"}) + @Nullable + public final <T extends IInterface> ArrayList<T> createInterfaceArrayList( + @NonNull Function<IBinder, T> asInterface) { + int N = readInt(); + if (N < 0) { + return null; + } + ArrayList<T> l = new ArrayList<T>(N); + while (N > 0) { + l.add(asInterface.apply(readStrongBinder())); + N--; + } + return l; + } + + /** * Read into the given List items String objects that were written with * {@link #writeStringList} at the current dataPosition(). * @@ -3409,31 +3632,85 @@ public final class Parcel { } /** + * Read into the given List items IInterface objects that were written with + * {@link #writeInterfaceList} at the current dataPosition(). + * + * @see #writeInterfaceList + */ + public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list, + @NonNull Function<IBinder, T> asInterface) { + int M = list.size(); + int N = readInt(); + int i = 0; + for (; i < M && i < N; i++) { + list.set(i, asInterface.apply(readStrongBinder())); + } + for (; i<N; i++) { + list.add(asInterface.apply(readStrongBinder())); + } + for (; i<M; i++) { + list.remove(N); + } + } + + /** * Read the list of {@code Parcelable} objects at the current data position into the * given {@code list}. The contents of the {@code list} are replaced. If the serialized * list was {@code null}, {@code list} is cleared. * * @see #writeParcelableList(List, int) + * + * @deprecated Use the type-safer version {@link #readParcelableList(List, ClassLoader, Class)} + * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the + * format to use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the + * items' class is final) since this is also more performant. Note that changing to the + * latter also requires changing the writes. */ + @Deprecated @NonNull public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list, @Nullable ClassLoader cl) { - final int N = readInt(); - if (N == -1) { + return readParcelableListInternal(list, cl, /*clazz*/ null); + } + + /** + * Same as {@link #readParcelableList(List, ClassLoader)} but accepts {@code clazz} parameter as + * the type required for each item. + * + * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized + * is not an instance of that class or any of its children classes or there was an error + * trying to instantiate an element. + */ + @NonNull + public <T> List<T> readParcelableList(@NonNull List<T> list, + @Nullable ClassLoader cl, @NonNull Class<T> clazz) { + Objects.requireNonNull(list); + Objects.requireNonNull(clazz); + return readParcelableListInternal(list, cl, clazz); + } + + /** + * @param clazz The type of the object expected or {@code null} for performing no checks. + */ + @NonNull + private <T> List<T> readParcelableListInternal(@NonNull List<T> list, + @Nullable ClassLoader cl, @Nullable Class<T> clazz) { + final int n = readInt(); + if (n == -1) { list.clear(); return list; } - final int M = list.size(); + final int m = list.size(); int i = 0; - for (; i < M && i < N; i++) { - list.set(i, (T) readParcelable(cl)); + for (; i < m && i < n; i++) { + list.set(i, (T) readParcelableInternal(cl, clazz)); } - for (; i<N; i++) { - list.add((T) readParcelable(cl)); + for (; i < n; i++) { + list.add((T) readParcelableInternal(cl, clazz)); } - for (; i<M; i++) { - list.remove(N); + for (; i < m; i++) { + list.remove(n); } return list; } @@ -3912,7 +4189,13 @@ public final class Parcel { * object has been written. * @throws BadParcelableException Throws BadParcelableException if there * was an error trying to instantiate the Parcelable. + * + * @deprecated Use the type-safer version {@link #readParcelable(ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to + * use {@link Parcelable.Creator#createFromParcel(Parcel)} if possible since this is also + * more performant. Note that changing to the latter also requires changing the writes. */ + @Deprecated @Nullable public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) { return readParcelableInternal(loader, /* clazz */ null); @@ -3981,7 +4264,11 @@ public final class Parcel { * read the {@link Parcelable.Creator}. * * @see #writeParcelableCreator + * + * @deprecated Use the type-safer version {@link #readParcelableCreator(ClassLoader, Class)} + * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) { return readParcelableCreatorInternal(loader, /* clazz */ null); @@ -4142,16 +4429,25 @@ public final class Parcel { * Read and return a new Serializable object from the parcel. * @return the Serializable object, or null if the Serializable name * wasn't found in the parcel. + * + * Unlike {@link #readSerializable(ClassLoader, Class)}, it uses the nearest valid class loader + * up the execution stack to instantiate the Serializable object. + * + * @deprecated Use the type-safer version {@link #readSerializable(ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable public Serializable readSerializable() { return readSerializableInternal(/* loader */ null, /* clazz */ null); } /** - * Same as {@link #readSerializable()} but accepts {@code loader} parameter - * as the primary classLoader for resolving the Serializable class; and {@code clazz} parameter - * as the required type. + * Same as {@link #readSerializable()} but accepts {@code loader} and {@code clazz} parameters. + * + * @param loader A ClassLoader from which to instantiate the Serializable object, + * or null for the default class loader. + * @param clazz The type of the object expected. * * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized * is not an instance of that class or any of its children class or there there was an error @@ -4161,7 +4457,8 @@ public final class Parcel { public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) { Objects.requireNonNull(clazz); - return readSerializableInternal(loader, clazz); + return readSerializableInternal( + loader == null ? getClass().getClassLoader() : loader, clazz); } /** @@ -4315,13 +4612,23 @@ public final class Parcel { destroy(); } - /* package */ void readMapInternal(@NonNull Map outVal, int N, + /** + * To be replaced by {@link #readMapInternal(Map, int, ClassLoader, Class, Class)}, but keep + * the old API for compatibility usages. + */ + /* package */ void readMapInternal(@NonNull Map outVal, int n, @Nullable ClassLoader loader) { - while (N > 0) { - Object key = readValue(loader); - Object value = readValue(loader); + readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null); + } + + /* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n, + @Nullable ClassLoader loader, @Nullable Class<K> clazzKey, + @Nullable Class<V> clazzValue) { + while (n > 0) { + K key = readValue(loader, clazzKey); + V value = readValue(loader, clazzValue); outVal.put(key, value); - N--; + n--; } } diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index 7b55e710caeb..f4edcb1317f4 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -156,10 +156,15 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa } /** - * Constructs a PersistableBundle without initializing it. + * Constructs a {@link PersistableBundle} containing a copy of {@code from}. + * + * @param from The bundle to be copied. + * @param deep Whether is a deep or shallow copy. + * + * @hide */ - PersistableBundle(boolean doInit) { - super(doInit); + PersistableBundle(PersistableBundle from, boolean deep) { + super(from, deep); } /** @@ -190,9 +195,7 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa * are referenced as-is and not copied in any way. */ public PersistableBundle deepCopy() { - PersistableBundle b = new PersistableBundle(false); - b.copyInternal(this, true); - return b; + return new PersistableBundle(this, /* deep */ true); } /** diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java index 4e8418bd60ec..ba5ed4360cd7 100644 --- a/core/java/android/os/ServiceManager.java +++ b/core/java/android/os/ServiceManager.java @@ -298,6 +298,17 @@ public final class ServiceManager { } /** + * Register callback for service registration notifications. + * + * @throws RemoteException for underlying error. + * @hide + */ + public static void registerForNotifications( + @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException { + getIServiceManager().registerForNotifications(name, callback); + } + + /** * Return a list of all currently running services. * @return an array of all currently running services, or <code>null</code> in * case of an exception diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java index 3739040eef60..2dcf67483203 100644 --- a/core/java/android/os/ServiceManagerNative.java +++ b/core/java/android/os/ServiceManagerNative.java @@ -78,7 +78,7 @@ class ServiceManagerProxy implements IServiceManager { public void registerForNotifications(String name, IServiceCallback cb) throws RemoteException { - throw new RemoteException(); + mServiceManager.registerForNotifications(name, cb); } public void unregisterForNotifications(String name, IServiceCallback cb) diff --git a/core/java/android/os/StatsBootstrapAtom.aidl b/core/java/android/os/StatsBootstrapAtom.aidl new file mode 100644 index 000000000000..47500af8eebf --- /dev/null +++ b/core/java/android/os/StatsBootstrapAtom.aidl @@ -0,0 +1,34 @@ +/* + * Copyright 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + +import android.os.StatsBootstrapAtomValue; + +/* + * Generic encapsulation of an atom for bootstrap processes to log. + * + * @hide + */ +parcelable StatsBootstrapAtom { + /* + * Atom ID. Must be between 1 - 10,000. + */ + int atomId; + /* + * Vector of fields in the order of the atom definition. + */ + StatsBootstrapAtomValue[] values; + }
\ No newline at end of file diff --git a/core/java/android/os/StatsBootstrapAtomValue.aidl b/core/java/android/os/StatsBootstrapAtomValue.aidl new file mode 100644 index 000000000000..a90dfa404ee9 --- /dev/null +++ b/core/java/android/os/StatsBootstrapAtomValue.aidl @@ -0,0 +1,29 @@ +/* + * Copyright 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; +/* + * Supported field types. + * + * @hide + */ +union StatsBootstrapAtomValue { + boolean boolValue; + int intValue; + long longValue; + float floatValue; + String stringValue; + byte[] bytesValue; +}
\ No newline at end of file diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java index a6316df0780c..cde2063fdba5 100644 --- a/core/java/android/os/SystemConfigManager.java +++ b/core/java/android/os/SystemConfigManager.java @@ -17,10 +17,10 @@ package android.os; import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.content.ComponentName; import android.content.Context; import android.util.ArraySet; import android.util.Log; @@ -138,9 +138,9 @@ public class SystemConfigManager { * @return The enabled component * {@hide} */ - @SystemApi + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull - public List<String> getEnabledComponentOverrides(@NonNull String packageName) { + public List<ComponentName> getEnabledComponentOverrides(@NonNull String packageName) { try { return mInterface.getEnabledComponentOverrides(packageName); } catch (RemoteException e) { diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index 3e01c53f0469..b7e3068a437c 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -238,7 +238,7 @@ public class UpdateEngine { public static final int DISABLED = 9; } - private IUpdateEngine mUpdateEngine; + private final IUpdateEngine mUpdateEngine; private IUpdateEngineCallback mUpdateEngineCallback = null; private final Object mUpdateEngineCallbackLock = new Object(); @@ -248,6 +248,9 @@ public class UpdateEngine { public UpdateEngine() { mUpdateEngine = IUpdateEngine.Stub.asInterface( ServiceManager.getService(UPDATE_ENGINE_SERVICE)); + if (mUpdateEngine == null) { + throw new IllegalStateException("Failed to find update_engine"); + } } /** diff --git a/core/java/android/os/health/HealthStats.java b/core/java/android/os/health/HealthStats.java index 74ce5157a548..6c648f136183 100644 --- a/core/java/android/os/health/HealthStats.java +++ b/core/java/android/os/health/HealthStats.java @@ -32,7 +32,7 @@ import java.util.Map; * Each of the keys references data in one of five data types: * * <p> - * A <b>measurement</b> metric contains a sinlge {@code long} value. That value may + * A <b>measurement</b> metric contains a single {@code long} value. That value may * be a count, a time, or some other type of value. The unit for a measurement * (COUNT, MS, etc) will always be in the name of the constant for the key to * retrieve it. For example, the diff --git a/core/java/android/os/health/UidHealthStats.java b/core/java/android/os/health/UidHealthStats.java index afc9d78dcbd1..488a5422becc 100644 --- a/core/java/android/os/health/UidHealthStats.java +++ b/core/java/android/os/health/UidHealthStats.java @@ -43,14 +43,14 @@ public final class UidHealthStats { /** * How many milliseconds this statistics report covers in wall-clock time while the - * device was on battery including both screen-on and screen-off time. + * device was on battery including only screen-off time. */ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT) public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 3; /** * How many milliseconds this statistics report covers that the CPU was running while the - * device was on battery including both screen-on and screen-off time. + * device was on battery including only screen-off time. */ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT) public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 4; @@ -65,7 +65,7 @@ public final class UidHealthStats { /** * Key for a TimerStat for the times a - * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK full wake lock} + * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock} * was acquired for this uid. */ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS) diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index f57d1574d399..39a2e1353237 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -56,11 +56,12 @@ import java.util.UUID; * <ul> * <li>{@link #MOUNT_FLAG_PRIMARY} means the volume provides primary external * storage, historically found at {@code /sdcard}. - * <li>{@link #MOUNT_FLAG_VISIBLE} means the volume is visible to third-party - * apps for direct filesystem access. The system should send out relevant - * storage broadcasts and index any media on visible volumes. Visible volumes - * are considered a more stable part of the device, which is why we take the - * time to index them. In particular, transient volumes like USB OTG devices + * <li>{@link #MOUNT_FLAG_VISIBLE_FOR_READ} and + * {@link #MOUNT_FLAG_VISIBLE_FOR_WRITE} mean the volume is visible to + * third-party apps for direct filesystem access. The system should send out + * relevant storage broadcasts and index any media on visible volumes. Visible + * volumes are considered a more stable part of the device, which is why we take + * the time to index them. In particular, transient volumes like USB OTG devices * <em>should not</em> be marked as visible; their contents should be surfaced * to apps through the Storage Access Framework. * </ul> @@ -100,7 +101,8 @@ public class VolumeInfo implements Parcelable { public static final int STATE_BAD_REMOVAL = IVold.VOLUME_STATE_BAD_REMOVAL; public static final int MOUNT_FLAG_PRIMARY = IVold.MOUNT_FLAG_PRIMARY; - public static final int MOUNT_FLAG_VISIBLE = IVold.MOUNT_FLAG_VISIBLE; + public static final int MOUNT_FLAG_VISIBLE_FOR_READ = IVold.MOUNT_FLAG_VISIBLE_FOR_READ; + public static final int MOUNT_FLAG_VISIBLE_FOR_WRITE = IVold.MOUNT_FLAG_VISIBLE_FOR_WRITE; private static SparseArray<String> sStateToEnvironment = new SparseArray<>(); private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>(); @@ -312,17 +314,31 @@ public class VolumeInfo implements Parcelable { return isPrimary() && (getType() == TYPE_PUBLIC); } + private boolean isVisibleForRead() { + return (mountFlags & MOUNT_FLAG_VISIBLE_FOR_READ) != 0; + } + + private boolean isVisibleForWrite() { + return (mountFlags & MOUNT_FLAG_VISIBLE_FOR_WRITE) != 0; + } + @UnsupportedAppUsage public boolean isVisible() { - return (mountFlags & MOUNT_FLAG_VISIBLE) != 0; + return isVisibleForRead() || isVisibleForWrite(); } - public boolean isVisibleForUser(int userId) { - if ((type == TYPE_PUBLIC || type == TYPE_STUB || type == TYPE_EMULATED) - && mountUserId == userId) { - return isVisible(); + private boolean isVolumeSupportedForUser(int userId) { + if (mountUserId != userId) { + return false; } - return false; + return type == TYPE_PUBLIC || type == TYPE_STUB || type == TYPE_EMULATED; + } + + /** + * Returns {@code true} if this volume is visible for {@code userId}, {@code false} otherwise. + */ + public boolean isVisibleForUser(int userId) { + return isVolumeSupportedForUser(userId) && isVisible(); } /** @@ -335,12 +351,12 @@ public class VolumeInfo implements Parcelable { } public boolean isVisibleForRead(int userId) { - return isVisibleForUser(userId); + return isVolumeSupportedForUser(userId) && isVisibleForRead(); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean isVisibleForWrite(int userId) { - return isVisibleForUser(userId); + return isVolumeSupportedForUser(userId) && isVisibleForWrite(); } @UnsupportedAppUsage diff --git a/core/java/android/os/vibrator/OWNERS b/core/java/android/os/vibrator/OWNERS new file mode 100644 index 000000000000..b54d6bf07818 --- /dev/null +++ b/core/java/android/os/vibrator/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file diff --git a/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS b/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS new file mode 100644 index 000000000000..cb521c842816 --- /dev/null +++ b/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS @@ -0,0 +1,8 @@ +ewol@google.com +hackbod@google.com +jsharkey@google.com +narayan@google.com +patb@google.com +svetoslavganov@google.com +yamasani@google.com +zhanghai@google.com diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 8ac5c0397734..6644f1e91f1d 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -622,6 +622,14 @@ public final class DeviceConfig { public static final String NAMESPACE_GAME_OVERLAY = "game_overlay"; /** + * Namespace for Android Virtualization Framework related features accessible by native code. + * + * @hide + */ + public static final String NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE = + "virtualization_framework_native"; + + /** * Namespace for Constrain Display APIs related features. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ac520e8b3dec..84be74669f11 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11218,22 +11218,38 @@ public final class Settings { "night_display_forced_auto_mode_available"; /** - * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment - * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been - * exceeded. - * @hide - */ + * If UTC time between two NITZ signals is greater than this value then the second signal + * cannot be ignored. + * + * <p>This value is in milliseconds. It is used for telephony-based time and time zone + * detection. + * @hide + */ @Readable public static final String NITZ_UPDATE_DIFF = "nitz_update_diff"; /** - * The length of time in milli-seconds that automatic small adjustments to - * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded. - * @hide - */ + * If the elapsed realtime between two NITZ signals is greater than this value then the + * second signal cannot be ignored. + * + * <p>This value is in milliseconds. It is used for telephony-based time and time zone + * detection. + * @hide + */ @Readable public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing"; + /** + * If the device connects to a telephony network and was disconnected from a telephony + * network for less than this time, a previously received NITZ signal can be restored. + * + * <p>This value is in milliseconds. It is used for telephony-based time and time zone + * detection. + * @hide + */ + public static final String NITZ_NETWORK_DISCONNECT_RETENTION = + "nitz_network_disconnect_retention"; + /** Preferred NTP server. {@hide} */ @Readable public static final String NTP_SERVER = "ntp_server"; diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index f3a8b5d79ea0..9f3a847e12eb 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -5365,5 +5365,14 @@ public final class Telephony { */ public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS = "d2d_sharing_contacts"; + + /** + * TelephonyProvider column name for NR Advanced calling + * Determines if the user has enabled VoNR settings for this subscription. + * + * @hide + */ + public static final String COLUMN_NR_ADVANCED_CALLING_ENABLED = + "nr_advanced_calling_enabled"; } } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index d39b56ddabed..5b9d69c2f9ff 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -191,20 +191,6 @@ public class PhoneStateListener { public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100; /** - * Listen for changes of the network signal strengths (cellular) always reported from modem, - * even in some situations such as the screen of the device is off. - * - * @see #onSignalStrengthsChanged - * - * @hide - * @deprecated Use TelephonyManager#setSignalStrengthUpdateRequest - * instead. - */ - @Deprecated - @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) - public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200; - - /** * Listen for changes to observed cell info. * * Listening to this event requires the {@link Manifest.permission#READ_PHONE_STATE} and diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index bedad7344e9d..cb1cff9cda22 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -1041,10 +1041,6 @@ public class TelephonyRegistryManager { eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED); } - if ((eventMask & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { - eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED); - } - if ((eventMask & PhoneStateListener.LISTEN_CELL_INFO) != 0) { eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED); } diff --git a/core/java/android/util/BackupUtils.java b/core/java/android/util/BackupUtils.java index 474cedaa0552..4fcb13c2bcf6 100644 --- a/core/java/android/util/BackupUtils.java +++ b/core/java/android/util/BackupUtils.java @@ -37,6 +37,10 @@ public class BackupUtils { public BadVersionException(String message) { super(message); } + + public BadVersionException(String message, Throwable throwable) { + super(message, throwable); + } } public static String readString(DataInputStream in) throws IOException { diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index 12bcd8b0aa97..b5fe4f58dfb2 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -62,6 +62,10 @@ import java.net.UnknownHostException; * another buffer allocation and copy, and even more pressure on the gc. * That means that if your log message is filtered out, you might be doing * significant work and incurring significant overhead. + * + * <p>When calling the log methods that take a Throwable parameter, + * if any of the throwables in the cause chain is an <code>UnknownHostException</code>, + * then the stack trace is not logged. */ public final class Log { /** @hide */ @@ -341,6 +345,9 @@ public final class Log { /** * Handy function to get a loggable stack trace from a Throwable + + * <p>If any of the throwables in the cause chain is an <code>UnknownHostException</code>, + * this returns an empty string. * @param tr An exception to log */ @NonNull diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index cda9b233576c..ba6f4eb7e598 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -830,13 +830,45 @@ public class KeyEvent extends InputEvent implements Parcelable { * consuming content. May be consumed by system to set account globally. */ public static final int KEYCODE_PROFILE_SWITCH = 288; - - /** + /** Key code constant: Video Application key #1. */ + public static final int KEYCODE_VIDEO_APP_1 = 289; + /** Key code constant: Video Application key #2. */ + public static final int KEYCODE_VIDEO_APP_2 = 290; + /** Key code constant: Video Application key #3. */ + public static final int KEYCODE_VIDEO_APP_3 = 291; + /** Key code constant: Video Application key #4. */ + public static final int KEYCODE_VIDEO_APP_4 = 292; + /** Key code constant: Video Application key #5. */ + public static final int KEYCODE_VIDEO_APP_5 = 293; + /** Key code constant: Video Application key #6. */ + public static final int KEYCODE_VIDEO_APP_6 = 294; + /** Key code constant: Video Application key #7. */ + public static final int KEYCODE_VIDEO_APP_7 = 295; + /** Key code constant: Video Application key #8. */ + public static final int KEYCODE_VIDEO_APP_8 = 296; + /** Key code constant: Featured Application key #1. */ + public static final int KEYCODE_FEATURED_APP_1 = 297; + /** Key code constant: Featured Application key #2. */ + public static final int KEYCODE_FEATURED_APP_2 = 298; + /** Key code constant: Featured Application key #3. */ + public static final int KEYCODE_FEATURED_APP_3 = 299; + /** Key code constant: Featured Application key #4. */ + public static final int KEYCODE_FEATURED_APP_4 = 300; + /** Key code constant: Demo Application key #1. */ + public static final int KEYCODE_DEMO_APP_1 = 301; + /** Key code constant: Demo Application key #2. */ + public static final int KEYCODE_DEMO_APP_2 = 302; + /** Key code constant: Demo Application key #3. */ + public static final int KEYCODE_DEMO_APP_3 = 303; + /** Key code constant: Demo Application key #4. */ + public static final int KEYCODE_DEMO_APP_4 = 304; + + /** * Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent. * @hide */ @TestApi - public static final int LAST_KEYCODE = KEYCODE_PROFILE_SWITCH; + public static final int LAST_KEYCODE = KEYCODE_DEMO_APP_4; // NOTE: If you add a new keycode here you must also add it to: // isSystem() diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 8143cf953f19..ffce4617eec6 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -157,6 +157,7 @@ public final class SurfaceControl implements Parcelable { private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats); private static native long[] nativeGetPhysicalDisplayIds(); + private static native long nativeGetPrimaryPhysicalDisplayId(); private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId); private static native IBinder nativeCreateDisplay(String name, boolean secure); private static native void nativeDestroyDisplay(IBinder displayToken); @@ -2266,6 +2267,15 @@ public final class SurfaceControl implements Parcelable { } /** + * Exposed to identify the correct display to apply the primary display orientation. Avoid using + * for any other purpose. + * @hide + */ + public static long getPrimaryPhysicalDisplayId() { + return nativeGetPrimaryPhysicalDisplayId(); + } + + /** * @hide */ public static IBinder getPhysicalDisplayToken(long physicalDisplayId) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3550a31f9038..2257f6cd2159 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -8448,13 +8448,13 @@ public final class ViewRootImpl implements ViewParent, MotionEvent me = (MotionEvent) event; if (me.getAction() == MotionEvent.ACTION_CANCEL) { EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Motion - Cancel", - getTitle()); + getTitle().toString()); } } else if (event instanceof KeyEvent) { KeyEvent ke = (KeyEvent) event; if (ke.isCanceled()) { EventLog.writeEvent(EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Key - Cancel", - getTitle()); + getTitle().toString()); } } // Always enqueue the input event in order, regardless of its time stamp. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 55beae0f7b3d..00f4eb83bdaa 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -127,17 +127,15 @@ import java.util.function.Consumer; /** * The interface that apps use to talk to the window manager. - * </p><p> - * Each window manager instance is bound to a particular {@link Display}. - * To obtain a {@link WindowManager} for a different display, use - * {@link Context#createDisplayContext} to obtain a {@link Context} for that - * display, then use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code> - * to get the WindowManager. - * </p><p> - * The simplest way to show a window on another display is to create a - * {@link Presentation}. The presentation will automatically obtain a - * {@link WindowManager} and {@link Context} for that display. - * </p> + * <p> + * Each window manager instance is bound to a {@link Display}. To obtain the + * <code>WindowManager</code> associated with a display, + * call {@link Context#createWindowContext(Display, int, Bundle)} to get the display's UI context, + * then call {@link Context#getSystemService(String)} or {@link Context#getSystemService(Class)} on + * the UI context. + * <p> + * The simplest way to show a window on a particular display is to create a {@link Presentation}, + * which automatically obtains a <code>WindowManager</code> and context for the display. */ @SystemService(Context.WINDOW_SERVICE) public interface WindowManager extends ViewManager { diff --git a/core/java/com/android/internal/compat/OWNERS b/core/java/com/android/internal/compat/OWNERS index cfd0a4b079ad..ee3086ab2fdb 100644 --- a/core/java/com/android/internal/compat/OWNERS +++ b/core/java/com/android/internal/compat/OWNERS @@ -1,6 +1 @@ -# Use this reviewer by default. -platform-compat-eng+reviews@google.com - -andreionea@google.com -mathewi@google.com -satayev@google.com +include tools/platform-compat:/OWNERS diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS index 050cb5c2b44e..71f997bb57c5 100644 --- a/core/java/com/android/internal/net/OWNERS +++ b/core/java/com/android/internal/net/OWNERS @@ -1,9 +1,4 @@ set noparent +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking -codewiz@google.com -ek@google.com -jchalard@google.com jsharkey@android.com -lorenzo@google.com -reminv@google.com -satk@google.com diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java index 8b0411de5477..8f5e97d40530 100644 --- a/core/java/com/android/internal/os/ClassLoaderFactory.java +++ b/core/java/com/android/internal/os/ClassLoaderFactory.java @@ -24,6 +24,7 @@ import dalvik.system.DelegateLastClassLoader; import dalvik.system.DexClassLoader; import dalvik.system.PathClassLoader; +import java.util.ArrayList; import java.util.List; /** @@ -100,14 +101,25 @@ public class ClassLoaderFactory { } /** - * Same as {@code createClassLoader} below, but passes a null list of shared - * libraries. + * Same as {@code createClassLoader} below, but passes a null list of shared libraries. This + * method is used only to load platform classes (i.e. those in framework.jar or services.jar), + * and MUST NOT be used for loading untrusted classes, especially the app classes. For the + * latter case, use the below method which accepts list of shared libraries so that the classes + * don't have unlimited access to all shared libraries. */ public static ClassLoader createClassLoader(String dexPath, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) { + // b/205164833: allow framework classes to have access to all public vendor libraries. + // This is because those classes are part of the platform and don't have an app manifest + // where required libraries can be specified using the <uses-native-library> tag. + // Note that this still does not give access to "private" vendor libraries. + List<String> nativeSharedLibraries = new ArrayList<>(); + nativeSharedLibraries.add("ALL"); + return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath, - parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null, null); + parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, + nativeSharedLibraries, null); } /** diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 92d5a47a2ed0..6d4b8c5ea1ad 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -724,9 +724,6 @@ public final class Zygote { DataOutputStream usapOutputStream = null; ZygoteArguments args = null; - // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool. - blockSigTerm(); - LocalSocket sessionSocket = null; if (argBuffer == null) { // Read arguments from usapPoolSocket instead. @@ -742,6 +739,10 @@ public final class Zygote { ZygoteCommandBuffer tmpArgBuffer = null; try { sessionSocket = usapPoolSocket.accept(); + // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool. + // This is safe from a race condition because the pool is only flushed after + // the SystemServer changes its internal state to stop using the USAP pool. + blockSigTerm(); usapOutputStream = new DataOutputStream(sessionSocket.getOutputStream()); @@ -759,9 +760,10 @@ public final class Zygote { unblockSigTerm(); IoUtils.closeQuietly(sessionSocket); IoUtils.closeQuietly(tmpArgBuffer); - blockSigTerm(); } } else { + // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool. + blockSigTerm(); try { args = ZygoteArguments.getInstance(argBuffer); } catch (Exception ex) { diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS index eb2478f6550a..7a590d0ed079 100644 --- a/core/java/com/android/internal/view/OWNERS +++ b/core/java/com/android/internal/view/OWNERS @@ -15,7 +15,7 @@ per-file *Surface* = file:/services/core/java/com/android/server/wm/OWNERS # WindowManager per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNERS -per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS +per-file BaseIWindow.java = file:/services/core/java/com/android/server/wm/OWNERS per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java index 2959667e046f..26ff192521da 100644 --- a/core/java/com/android/server/NetworkManagementSocketTagger.java +++ b/core/java/com/android/server/NetworkManagementSocketTagger.java @@ -17,7 +17,6 @@ package com.android.server; import android.os.StrictMode; -import android.os.SystemProperties; import android.util.Log; import android.util.Slog; @@ -33,13 +32,6 @@ public final class NetworkManagementSocketTagger extends SocketTagger { private static final String TAG = "NetworkManagementSocketTagger"; private static final boolean LOGD = false; - /** - * {@link SystemProperties} key that indicates if {@code qtaguid} bandwidth - * controls have been enabled. - */ - // TODO: remove when always enabled, or once socket tagging silently fails. - public static final String PROP_QTAGUID_ENABLED = "net.qtaguid_enabled"; - private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() { @Override protected SocketTags initialValue() { @@ -88,13 +80,11 @@ public final class NetworkManagementSocketTagger extends SocketTagger { private void tagSocketFd(FileDescriptor fd, int tag, int uid) { if (tag == -1 && uid == -1) return; - if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { - final int errno = native_tagSocketFd(fd, tag, uid); - if (errno < 0) { - Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " - + tag + ", " + - + uid + ") failed with errno" + errno); - } + final int errno = native_tagSocketFd(fd, tag, uid); + if (errno < 0) { + Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " + + tag + ", " + + uid + ") failed with errno" + errno); } } @@ -110,11 +100,9 @@ public final class NetworkManagementSocketTagger extends SocketTagger { final SocketTags options = threadSocketTags.get(); if (options.statsTag == -1 && options.statsUid == -1) return; - if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { - final int errno = native_untagSocketFd(fd); - if (errno < 0) { - Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); - } + final int errno = native_untagSocketFd(fd); + if (errno < 0) { + Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); } } @@ -124,21 +112,17 @@ public final class NetworkManagementSocketTagger extends SocketTagger { } public static void setKernelCounterSet(int uid, int counterSet) { - if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { - final int errno = native_setCounterSet(counterSet, uid); - if (errno < 0) { - Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " - + errno); - } + final int errno = native_setCounterSet(counterSet, uid); + if (errno < 0) { + Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " + + errno); } } public static void resetKernelUidStats(int uid) { - if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { - int errno = native_deleteTagData(0, uid); - if (errno < 0) { - Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno); - } + int errno = native_deleteTagData(0, uid); + if (errno < 0) { + Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno); } } diff --git a/core/java/com/android/server/net/OWNERS b/core/java/com/android/server/net/OWNERS index d3836d4c6c57..62c5737a2e8e 100644 --- a/core/java/com/android/server/net/OWNERS +++ b/core/java/com/android/server/net/OWNERS @@ -1,8 +1,2 @@ set noparent - -codewiz@google.com -jchalard@google.com -junyulai@google.com -lorenzo@google.com -reminv@google.com -satk@google.com +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 937fa8101def..2cfec4b17f63 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -391,7 +391,6 @@ cc_library_headers { name: "libandroid_runtime_vm_headers", host_supported: true, vendor_available: true, - recovery_available: true, // TODO(b/153609531): remove when libbinder is not native_bridge_supported native_bridge_supported: true, // Allow only modules from the following list to create threads that can be diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 97cac291630c..a13111143b0e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -746,8 +746,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p } const bool checkJni = GetBoolProperty("dalvik.vm.checkjni", false); - ALOGV("CheckJNI is %s\n", checkJni ? "ON" : "OFF"); if (checkJni) { + ALOGD("CheckJNI is ON"); + /* extended JNI checking */ addOption("-Xcheck:jni"); diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 78787e52dbaa..65d073fc1467 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -69,6 +69,9 @@ per-file AndroidRuntime.cpp = calin@google.com, ngeoffray@google.com, oth@google # Although marked "view" this is mostly graphics stuff per-file android_view_* = file:/graphics/java/android/graphics/OWNERS +# Verity +per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com + # VINTF per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS per-file android_os_VintfRuntimeInfo* = file:platform/system/libvintf:/OWNERS diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 4c2b114c724a..5e0d9b32380c 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -34,6 +34,7 @@ #include <vector> #include <android-base/logging.h> +#include <android-base/properties.h> #include <bionic/malloc.h> #include <debuggerd/client.h> #include <log/log.h> @@ -859,7 +860,22 @@ static jlong android_os_Debug_getDmabufHeapPoolsSizeKb(JNIEnv* env, jobject claz return poolsSizeKb; } +static bool halSupportsGpuPrivateMemory() { + int productApiLevel = + android::base::GetIntProperty("ro.product.first_api_level", + android::base::GetIntProperty("ro.build.version.sdk", + __ANDROID_API_FUTURE__)); + int boardApiLevel = + android::base::GetIntProperty("ro.board.api_level", + android::base::GetIntProperty("ro.board.first_api_level", + __ANDROID_API_FUTURE__)); + + return std::min(productApiLevel, boardApiLevel) >= __ANDROID_API_S__; +} + static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz) { + static bool gpuPrivateMemorySupported = halSupportsGpuPrivateMemory(); + struct memtrack_proc* p = memtrack_proc_new(); if (p == nullptr) { LOG(ERROR) << "getGpuPrivateMemoryKb: Failed to create memtrack_proc"; @@ -876,6 +892,12 @@ static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz) ssize_t gpuPrivateMem = memtrack_proc_gl_pss(p); memtrack_proc_destroy(p); + + // Old HAL implementations may return 0 for GPU private memory if not supported + if (gpuPrivateMem == 0 && !gpuPrivateMemorySupported) { + return -1; + } + return gpuPrivateMem / 1024; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 8d12df226ffe..e47718305b7c 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -889,6 +889,12 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { return array; } +static jlong nativeGetPrimaryPhysicalDisplayId(JNIEnv* env, jclass clazz) { + PhysicalDisplayId displayId; + SurfaceComposerClient::getPrimaryPhysicalDisplayId(&displayId); + return static_cast<jlong>(displayId.value); +} + static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) { sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId)); @@ -1879,6 +1885,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeReleaseFrameRateFlexibilityToken }, {"nativeGetPhysicalDisplayIds", "()[J", (void*)nativeGetPhysicalDisplayIds }, + {"nativeGetPrimaryPhysicalDisplayId", "()J", + (void*)nativeGetPrimaryPhysicalDisplayId }, {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;", (void*)nativeGetPhysicalDisplayToken }, {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;", diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index be82879c8411..ef6fd7dd6829 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -36,6 +36,7 @@ #include <inttypes.h> #include <sys/stat.h> #include <sys/types.h> +#include <linux/fs.h> #include <memory> @@ -253,6 +254,16 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr return INSTALL_FAILED_CONTAINER_ERROR; } + // If a filesystem like f2fs supports per-file compression, set the compression bit before data + // writes + unsigned int flags; + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) { + ALOGE("Failed to call FS_IOC_GETFLAGS on %s: %s\n", localTmpFileName, strerror(errno)); + } else if ((flags & FS_COMPR_FL) == 0) { + flags |= FS_COMPR_FL; + ioctl(fd, FS_IOC_SETFLAGS, &flags); + } + if (!zipFile->uncompressEntry(zipEntry, fd)) { ALOGE("Failed uncompressing %s to %s\n", fileName, localTmpFileName); close(fd); diff --git a/core/proto/android/app/OWNERS b/core/proto/android/app/OWNERS index cc479e61b855..4d76aee9d722 100644 --- a/core/proto/android/app/OWNERS +++ b/core/proto/android/app/OWNERS @@ -1 +1,2 @@ -per-file location_time_zone_manager.proto, time_zone_detector.proto = nfuller@google.com, mingaleev@google.com +per-file location_time_zone_manager.proto = file:platform/frameworks/base:/services/core/java/com/android/server/timezonedetector/OWNERS +per-file time_zone_detector.proto = file:platform/frameworks/base:/services/core/java/com/android/server/timezonedetector/OWNERS diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index c3d159659622..ed3968ae9b78 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -672,18 +672,31 @@ message GlobalSettingsProto { optional SettingProto new_contact_aggregator = 79 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto night_display_forced_auto_mode_available = 80 [ (android.privacy).dest = DEST_AUTOMATIC ]; - message NitzUpdate { - option (android.msg_privacy).dest = DEST_EXPLICIT; - - // If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment to - // SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been - // exceeded. - optional SettingProto diff = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; - // The length of time in milli-seconds that automatic small adjustments to - // SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded. - optional SettingProto spacing = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; - } - optional NitzUpdate nitz_update = 81; + message Nitz { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + // If UTC time between two NITZ signals is greater than this value then the second signal + // cannot be ignored. + // + // This value is in milliseconds. It is used for telephony-based time and time zone + // detection. + optional SettingProto update_diff = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + // If the elapsed realtime between two NITZ signals is greater than this value then the + // second signal cannot be ignored. + // + // This value is in milliseconds. It is used for telephony-based time and time zone + // detection. + optional SettingProto update_spacing = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + // If the device connects to a telephony network and was disconnected from a telephony + // network for less than this time, a previously received NITZ signal can be restored. + // + // This value is in milliseconds. It is used for telephony-based time and time zone + // detection. + optional SettingProto network_disconnect_retention = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional Nitz nitz = 81; message Notification { option (android.msg_privacy).dest = DEST_EXPLICIT; diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index fa1e9d4afcdf..a62ddd084aab 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -351,6 +351,7 @@ message ActivityRecordProto { optional bool translucent = 30; optional bool pip_auto_enter_enabled = 31; optional bool in_size_compat_mode = 32; + optional float min_aspect_ratio = 33; } /* represents WindowToken */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1418dbe0f849..1a38ea10a97d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -252,6 +252,11 @@ android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" /> <protected-broadcast android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONF_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED" /> <protected-broadcast android:name="android.bluetooth.action.TETHERING_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" /> @@ -1194,6 +1199,14 @@ android:description="@string/permdesc_readPhoneState" android:protectionLevel="dangerous" /> + <!-- Allows read only access to phone state with a non dangerous permission, + including the information like cellular network type, software version. --> + <permission android:name="android.permission.READ_BASIC_PHONE_STATE" + android:permissionGroup="android.permission-group.UNDEFINED" + android:label="@string/permlab_readBasicPhoneState" + android:description="@string/permdesc_readBasicPhoneState" + android:protectionLevel="normal" /> + <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities granted by {@link #READ_PHONE_STATE} but is exposed to instant applications. <p>Protection level: dangerous--> @@ -2394,7 +2407,7 @@ <permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION" android:protectionLevel="signature" /> - <!-- Allows listen permission to always reported signal strength. + <!-- Allows listen permission to always reported system signal strength. @hide Used internally. --> <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" android:protectionLevel="signature" /> @@ -6247,6 +6260,10 @@ android:permission="android.permission.BIND_JOB_SERVICE"> </service> + <service android:name="com.android.server.compos.IsolatedCompilationJobService" + android:permission="android.permission.BIND_JOB_SERVICE"> + </service> + <service android:name="com.android.server.PruneInstantAppsJobService" android:permission="android.permission.BIND_JOB_SERVICE" > </service> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a5f505176d5d..dc92e10abf0d 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1984,6 +1984,22 @@ <enum name="KEYCODE_THUMBS_UP" value="286" /> <enum name="KEYCODE_THUMBS_DOWN" value="287" /> <enum name="KEYCODE_PROFILE_SWITCH" value="288" /> + <enum name="KEYCODE_VIDEO_APP_1" value="289" /> + <enum name="KEYCODE_VIDEO_APP_2" value="290" /> + <enum name="KEYCODE_VIDEO_APP_3" value="291" /> + <enum name="KEYCODE_VIDEO_APP_4" value="292" /> + <enum name="KEYCODE_VIDEO_APP_5" value="293" /> + <enum name="KEYCODE_VIDEO_APP_6" value="294" /> + <enum name="KEYCODE_VIDEO_APP_7" value="295" /> + <enum name="KEYCODE_VIDEO_APP_8" value="296" /> + <enum name="KEYCODE_FEATURED_APP_1" value="297" /> + <enum name="KEYCODE_FEATURED_APP_2" value="298" /> + <enum name="KEYCODE_FEATURED_APP_3" value="299" /> + <enum name="KEYCODE_FEATURED_APP_4" value="300" /> + <enum name="KEYCODE_DEMO_APP_1" value="301" /> + <enum name="KEYCODE_DEMO_APP_2" value="302" /> + <enum name="KEYCODE_DEMO_APP_3" value="303" /> + <enum name="KEYCODE_DEMO_APP_4" value="304" /> </attr> <!-- ***************************************************************** --> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index d052d70442c4..f4acfaa1baca 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -547,7 +547,7 @@ <attr name="allowTaskReparenting" format="boolean" /> <!-- Declare that this application may use cleartext traffic, such as HTTP rather than HTTPS; - WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS. + WebSockets rather than WebSockets Secure; XMPP, IMAP, SMTP without STARTTLS or TLS. Defaults to true. If set to false {@code false}, the application declares that it does not intend to use cleartext network traffic, in which case platform components (e.g. HTTP stacks, {@code DownloadManager}, {@code MediaPlayer}) will refuse applications's requests @@ -1762,7 +1762,7 @@ <!-- @deprecated replaced by setting appCategory attribute to "game" --> <attr name="isGame" /> <!-- Declare that this application may use cleartext traffic, such as HTTP rather than - HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or + HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, SMTP without STARTTLS or TLS). Defaults to true. If set to false {@code false}, the application declares that it does not intend to use cleartext network traffic, in which case platform components (e.g. HTTP stacks, {@code DownloadManager}, {@code MediaPlayer}) will refuse diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7db2fe94d243..8496a478af97 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5054,4 +5054,7 @@ <!-- the number of the max cached processes in the system. --> <integer name="config_customizedMaxCachedProcesses">32</integer> + + <!-- Whether this device should support taking app snapshots on closure --> + <bool name="config_disableTaskSnapshots">false</bool> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a350d14d92ab..8eede56035c1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -64,16 +64,74 @@ <!-- Displayed when a carrier does not support call forwarding queries when roaming. --> <string name="mmiErrorWhileRoaming">Can not change call forwarding settings from your phone while you are roaming.</string> - <!-- Displayed when a phone feature such as call barring was activated. --> + <!-- Displayed when a phone feature such as call forwarding, call waiting, or call barring was + activated. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was enabled. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceEnabled} (this string). + --> <string name="serviceEnabled">Service was enabled.</string> <!-- Displayed in front of the list of a set of service classes - (voice, data, fax, etc.) that were enabled. --> + (voice, data, fax, etc.) that call waiting were enabled for. + Will be used with messages of the form: + <X> + <Y1> + ... + <Yn> + Where <X> is {@link #serviceEnabledFor} (this string) and <Y>..<Yn> can be: + {@link #serviceClassData}, {@link #serviceClassVoice}, {@link #serviceClassFAX}, + {@link #serviceClassSMS}, {@link #serviceClassDataAsync}, {@link #serviceClassDataSync}, + {@link #serviceClassPacket}, {@link #serviceClassPAD}. + --> <string name="serviceEnabledFor">Service was enabled for:</string> - <!-- Displayed when a phone feature such as call forwarding was deactivated. --> + <!-- Displayed when a phone feature such as call forwarding was deactivated. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was disabled. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceDisabled} (this string). + --> <string name="serviceDisabled">Service has been disabled.</string> - <!-- Displayed when a phone property such as a SIM password was registered. --> + <!-- Displayed when a phone property such as a SIM password was registered. Registration + entails setting up a service for use, where {@link #serviceEnabled} entails enabling a + previously registered service. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was registered. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceRegistered} (this string). --> <string name="serviceRegistered">Registration was successful.</string> - <!-- Displayed when a phone property such as a SIM password was erased. --> + <!-- Displayed when a phone property such as a SIM password was erased. + Erasure is the opposite of {@link #serviceRegistered} and entails removal of a service. + + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was registered. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceErased} (this string). + --> <string name="serviceErased">Erasure was successful.</string> <!-- Displayed when a SIM password was entered incorrectly. --> <string name="passwordIncorrect">Incorrect password.</string> @@ -107,23 +165,32 @@ [CHAR LIMIT=10] --> <string name="meid">MEID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ClipMmi">Incoming Caller ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ClirMmi">Hide Outgoing Caller ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ColpMmi">Connected Line ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. --> + <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ColrMmi">Connected Line ID Restriction</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call forwarding. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call forwarding. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="CfMmi">Call forwarding</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call waiting. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call waiting. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="CwMmi">Call waiting</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call barring. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call barring. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="BaMmi">Call barring</string> - <!-- Displayed as the title for a success/failure report changing the SIM password. --> + <!-- Displayed as the title for a success/failure report changing the SIM password. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="PwdMmi">Password change</string> - <!-- Displayed as the title for a success/failure report changing the SIM PIN. --> + <!-- Displayed as the title for a success/failure report changing the SIM PIN. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="PinMmi">PIN change</string> <string name="CnipMmi">Calling number present</string> <string name="CnirMmi">Calling number restricted</string> @@ -198,21 +265,29 @@ <string name="peerTtyModeOff">Peer requested TTY Mode OFF</string> <!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip /> - <!-- Example: Service was enabled for: Voice, Data --> + <!-- Example: Service was enabled for: Voice, Data + See {@link #serviceEnabledFor}.--> <string name="serviceClassVoice">Voice</string> - <!-- Example: Service was enabled for: Voice, Data --> + <!-- Example: Service was enabled for: Voice, Data. + See {@link #serviceEnabledFor}. --> <string name="serviceClassData">Data</string> - <!-- Example: Service was enabled for: Voice, FAX --> + <!-- Example: Service was enabled for: Voice, FAX + See {@link #serviceEnabledFor}. --> <string name="serviceClassFAX">FAX</string> - <!-- Example: Service was enabled for: Voice, SMS --> + <!-- Example: Service was enabled for: Voice, SMS + See {@link #serviceEnabledFor}. --> <string name="serviceClassSMS">SMS</string> - <!-- Meaning: asynchronous data. Example: Service was enabled for: Voice, Async --> + <!-- Meaning: asynchronous data. Example: Service was enabled for: Voice, Async + See {@link #serviceEnabledFor}. --> <string name="serviceClassDataAsync">Async</string> - <!-- Meaning: synchronous data. Example: Service was enabled for: Voice, Async --> + <!-- Meaning: synchronous data. Example: Service was enabled for: Voice, Async + See {@link #serviceEnabledFor}. --> <string name="serviceClassDataSync">Sync</string> - <!-- Meaning: packet data. Example: Service was enabled for: Voice, Packet --> + <!-- Meaning: packet data. Example: Service was enabled for: Voice, Packet + See {@link #serviceEnabledFor}. --> <string name="serviceClassPacket">Packet</string> - <!-- Meaning: unknown. Example: Service was enabled for: Voice, PAD --> + <!-- Meaning: unknown. Example: Service was enabled for: Voice, PAD + See {@link #serviceEnabledFor}. --> <string name="serviceClassPAD">PAD</string> <!-- CDMA Roaming Indicator Strings (non ERI)--> <skip /> @@ -1275,6 +1350,12 @@ phone number and device IDs, whether a call is active, and the remote number connected by a call.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=80]--> + <string name="permlab_readBasicPhoneState">read basic telephony status and identity </string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] --> + <string name="permdesc_readBasicPhoneState">Allows the app to access the basic telephony + features of the device.</string> + <!-- Title of an application permission. When granted the user is giving access to a third party app to route its calls through the system. --> <string name="permlab_manageOwnCalls">route calls through the system</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1c4452667541..d06dafe9b3e7 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4403,7 +4403,7 @@ <java-symbol type="string" name="view_and_control_notification_title" /> <java-symbol type="string" name="view_and_control_notification_content" /> <java-symbol type="array" name="config_accessibility_allowed_install_source" /> - + <!-- Translation --> <java-symbol type="string" name="ui_translation_accessibility_translated_text" /> <java-symbol type="string" name="ui_translation_accessibility_translation_finished" /> @@ -4430,4 +4430,7 @@ <java-symbol type="array" name="config_sharedLibrariesLoadedAfterApp" /> <java-symbol type="integer" name="config_customizedMaxCachedProcesses" /> + + <java-symbol type="bool" name="config_disableTaskSnapshots" /> + </resources> diff --git a/core/tests/PlatformCompatFramework/OWNERS b/core/tests/PlatformCompatFramework/OWNERS index cfd0a4b079ad..ee3086ab2fdb 100644 --- a/core/tests/PlatformCompatFramework/OWNERS +++ b/core/tests/PlatformCompatFramework/OWNERS @@ -1,6 +1 @@ -# Use this reviewer by default. -platform-compat-eng+reviews@google.com - -andreionea@google.com -mathewi@google.com -satayev@google.com +include tools/platform-compat:/OWNERS diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java index 59b46656dd52..bd55426601fc 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecConfigTest.java @@ -17,7 +17,6 @@ package android.bluetooth; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; import junit.framework.TestCase; @@ -34,7 +33,6 @@ public class BluetoothCodecConfigTest extends TestCase { BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, - BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX, BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID, }; private static final int[] kCodecPriorityArray = new int[] { @@ -168,20 +166,11 @@ public class BluetoothCodecConfigTest extends TestCase { long codec_specific3 = selectCodecSpecific3(config_id); long codec_specific4 = selectCodecSpecific4(config_id); - BluetoothCodecConfig bcc = new BluetoothCodecConfig(codec_type, codec_priority, + BluetoothCodecConfig bcc = buildBluetoothCodecConfig(codec_type, codec_priority, sample_rate, bits_per_sample, channel_mode, codec_specific1, codec_specific2, codec_specific3, codec_specific4); - if (sample_rate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { - assertFalse(bcc.isValid()); - } else if (bits_per_sample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { - assertFalse(bcc.isValid()); - } else if (channel_mode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { - assertFalse(bcc.isValid()); - } else { - assertTrue(bcc.isValid()); - } if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC) { assertTrue(bcc.isMandatoryCodec()); @@ -204,10 +193,6 @@ public class BluetoothCodecConfigTest extends TestCase { if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC) { assertEquals("LDAC", bcc.getCodecName()); } - if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX) { - assertEquals("UNKNOWN CODEC(" + BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX + ")", - bcc.getCodecName()); - } if (codec_type == BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) { assertEquals("INVALID CODEC", bcc.getCodecName()); } @@ -227,7 +212,7 @@ public class BluetoothCodecConfigTest extends TestCase { @SmallTest public void testBluetoothCodecConfig_equals() { BluetoothCodecConfig bcc1 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -235,7 +220,7 @@ public class BluetoothCodecConfigTest extends TestCase { 1000, 2000, 3000, 4000); BluetoothCodecConfig bcc2_same = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -244,7 +229,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertTrue(bcc1.equals(bcc2_same)); BluetoothCodecConfig bcc3_codec_type = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -253,7 +238,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc3_codec_type)); BluetoothCodecConfig bcc4_codec_priority = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -262,7 +247,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc4_codec_priority)); BluetoothCodecConfig bcc5_sample_rate = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_48000, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -271,7 +256,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc5_sample_rate)); BluetoothCodecConfig bcc6_bits_per_sample = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_24, @@ -280,7 +265,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc6_bits_per_sample)); BluetoothCodecConfig bcc7_channel_mode = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -289,7 +274,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc7_channel_mode)); BluetoothCodecConfig bcc8_codec_specific1 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -298,7 +283,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc8_codec_specific1)); BluetoothCodecConfig bcc9_codec_specific2 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -307,7 +292,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc9_codec_specific2)); BluetoothCodecConfig bcc10_codec_specific3 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -316,7 +301,7 @@ public class BluetoothCodecConfigTest extends TestCase { assertFalse(bcc1.equals(bcc10_codec_specific3)); BluetoothCodecConfig bcc11_codec_specific4 = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -324,4 +309,21 @@ public class BluetoothCodecConfigTest extends TestCase { 1000, 2000, 3000, 4004); assertFalse(bcc1.equals(bcc11_codec_specific4)); } + + private BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType, + int codecPriority, int sampleRate, int bitsPerSample, int channelMode, + long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { + return new BluetoothCodecConfig.Builder() + .setCodecType(sourceCodecType) + .setCodecPriority(codecPriority) + .setSampleRate(sampleRate) + .setBitsPerSample(bitsPerSample) + .setChannelMode(channelMode) + .setCodecSpecific1(codecSpecific1) + .setCodecSpecific2(codecSpecific2) + .setCodecSpecific3(codecSpecific3) + .setCodecSpecific4(codecSpecific4) + .build(); + + } } diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java index 83bf2ed19380..1cb2dcae865c 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothCodecStatusTest.java @@ -17,13 +17,13 @@ package android.bluetooth; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; - -import java.util.Arrays; -import java.util.Objects; import junit.framework.TestCase; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + /** * Unit test cases for {@link BluetoothCodecStatus}. * <p> @@ -34,7 +34,7 @@ public class BluetoothCodecStatusTest extends TestCase { // Codec configs: A and B are same; C is different private static final BluetoothCodecConfig config_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -42,7 +42,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig config_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -50,7 +50,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig config_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -59,7 +59,7 @@ public class BluetoothCodecStatusTest extends TestCase { // Local capabilities: A and B are same; C is different private static final BluetoothCodecConfig local_capability1_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -69,7 +69,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability1_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -79,7 +79,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability1_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -89,7 +89,7 @@ public class BluetoothCodecStatusTest extends TestCase { private static final BluetoothCodecConfig local_capability2_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -99,7 +99,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability2_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -109,7 +109,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability2_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -118,7 +118,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability3_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -128,7 +128,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability3_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -138,7 +138,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability3_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -147,7 +147,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability4_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -157,7 +157,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability4_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -167,7 +167,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability4_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000, @@ -176,7 +176,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability5_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -190,7 +190,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability5_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -204,7 +204,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig local_capability5_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -219,7 +219,7 @@ public class BluetoothCodecStatusTest extends TestCase { // Selectable capabilities: A and B are same; C is different private static final BluetoothCodecConfig selectable_capability1_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -228,7 +228,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability1_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -237,7 +237,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability1_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -245,7 +245,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability2_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -254,7 +254,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability2_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -263,7 +263,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability2_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -271,7 +271,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability3_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -280,7 +280,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability3_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -289,7 +289,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability3_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_16, @@ -297,7 +297,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability4_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_24, @@ -306,7 +306,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability4_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_24, @@ -315,7 +315,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability4_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100, BluetoothCodecConfig.BITS_PER_SAMPLE_24, @@ -323,7 +323,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability5_A = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -337,7 +337,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability5_B = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -351,7 +351,7 @@ public class BluetoothCodecStatusTest extends TestCase { 1000, 2000, 3000, 4000); private static final BluetoothCodecConfig selectable_capability5_C = - new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, + buildBluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT, BluetoothCodecConfig.SAMPLE_RATE_44100 | BluetoothCodecConfig.SAMPLE_RATE_48000 | @@ -363,79 +363,87 @@ public class BluetoothCodecStatusTest extends TestCase { BluetoothCodecConfig.CHANNEL_MODE_STEREO, 1000, 2000, 3000, 4000); - private static final BluetoothCodecConfig[] local_capability_A = { - local_capability1_A, - local_capability2_A, - local_capability3_A, - local_capability4_A, - local_capability5_A, - }; - - private static final BluetoothCodecConfig[] local_capability_B = { - local_capability1_B, - local_capability2_B, - local_capability3_B, - local_capability4_B, - local_capability5_B, - }; - - private static final BluetoothCodecConfig[] local_capability_B_reordered = { - local_capability5_B, - local_capability4_B, - local_capability2_B, - local_capability3_B, - local_capability1_B, - }; - - private static final BluetoothCodecConfig[] local_capability_C = { - local_capability1_C, - local_capability2_C, - local_capability3_C, - local_capability4_C, - local_capability5_C, - }; - - private static final BluetoothCodecConfig[] selectable_capability_A = { - selectable_capability1_A, - selectable_capability2_A, - selectable_capability3_A, - selectable_capability4_A, - selectable_capability5_A, - }; - - private static final BluetoothCodecConfig[] selectable_capability_B = { - selectable_capability1_B, - selectable_capability2_B, - selectable_capability3_B, - selectable_capability4_B, - selectable_capability5_B, - }; - - private static final BluetoothCodecConfig[] selectable_capability_B_reordered = { - selectable_capability5_B, - selectable_capability4_B, - selectable_capability2_B, - selectable_capability3_B, - selectable_capability1_B, - }; - - private static final BluetoothCodecConfig[] selectable_capability_C = { - selectable_capability1_C, - selectable_capability2_C, - selectable_capability3_C, - selectable_capability4_C, - selectable_capability5_C, - }; + private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_A = + new ArrayList() {{ + add(local_capability1_A); + add(local_capability2_A); + add(local_capability3_A); + add(local_capability4_A); + add(local_capability5_A); + }}; + + private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_B = + new ArrayList() {{ + add(local_capability1_B); + add(local_capability2_B); + add(local_capability3_B); + add(local_capability4_B); + add(local_capability5_B); + }}; + + private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_B_REORDERED = + new ArrayList() {{ + add(local_capability5_B); + add(local_capability4_B); + add(local_capability2_B); + add(local_capability3_B); + add(local_capability1_B); + }}; + + private static final List<BluetoothCodecConfig> LOCAL_CAPABILITY_C = + new ArrayList() {{ + add(local_capability1_C); + add(local_capability2_C); + add(local_capability3_C); + add(local_capability4_C); + add(local_capability5_C); + }}; + + private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_A = + new ArrayList() {{ + add(selectable_capability1_A); + add(selectable_capability2_A); + add(selectable_capability3_A); + add(selectable_capability4_A); + add(selectable_capability5_A); + }}; + + private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_B = + new ArrayList() {{ + add(selectable_capability1_B); + add(selectable_capability2_B); + add(selectable_capability3_B); + add(selectable_capability4_B); + add(selectable_capability5_B); + }}; + + private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_B_REORDERED = + new ArrayList() {{ + add(selectable_capability5_B); + add(selectable_capability4_B); + add(selectable_capability2_B); + add(selectable_capability3_B); + add(selectable_capability1_B); + }}; + + private static final List<BluetoothCodecConfig> SELECTABLE_CAPABILITY_C = + new ArrayList() {{ + add(selectable_capability1_C); + add(selectable_capability2_C); + add(selectable_capability3_C); + add(selectable_capability4_C); + add(selectable_capability5_C); + }}; private static final BluetoothCodecStatus bcs_A = - new BluetoothCodecStatus(config_A, local_capability_A, selectable_capability_A); + new BluetoothCodecStatus(config_A, LOCAL_CAPABILITY_A, SELECTABLE_CAPABILITY_A); private static final BluetoothCodecStatus bcs_B = - new BluetoothCodecStatus(config_B, local_capability_B, selectable_capability_B); + new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B, SELECTABLE_CAPABILITY_B); private static final BluetoothCodecStatus bcs_B_reordered = - new BluetoothCodecStatus(config_B, local_capability_B_reordered, - selectable_capability_B_reordered); + new BluetoothCodecStatus(config_B, LOCAL_CAPABILITY_B_REORDERED, + SELECTABLE_CAPABILITY_B_REORDERED); private static final BluetoothCodecStatus bcs_C = - new BluetoothCodecStatus(config_C, local_capability_C, selectable_capability_C); + new BluetoothCodecStatus(config_C, LOCAL_CAPABILITY_C, SELECTABLE_CAPABILITY_C); @SmallTest public void testBluetoothCodecStatus_get_methods() { @@ -444,16 +452,16 @@ public class BluetoothCodecStatusTest extends TestCase { assertTrue(Objects.equals(bcs_A.getCodecConfig(), config_B)); assertFalse(Objects.equals(bcs_A.getCodecConfig(), config_C)); - assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_A)); - assertTrue(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_B)); - assertFalse(Arrays.equals(bcs_A.getCodecsLocalCapabilities(), local_capability_C)); + assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_A)); + assertTrue(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_B)); + assertFalse(bcs_A.getCodecsLocalCapabilities().equals(LOCAL_CAPABILITY_C)); - assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), - selectable_capability_A)); - assertTrue(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), - selectable_capability_B)); - assertFalse(Arrays.equals(bcs_A.getCodecsSelectableCapabilities(), - selectable_capability_C)); + assertTrue(bcs_A.getCodecsSelectableCapabilities() + .equals(SELECTABLE_CAPABILITY_A)); + assertTrue(bcs_A.getCodecsSelectableCapabilities() + .equals(SELECTABLE_CAPABILITY_B)); + assertFalse(bcs_A.getCodecsSelectableCapabilities() + .equals(SELECTABLE_CAPABILITY_C)); } @SmallTest @@ -465,4 +473,21 @@ public class BluetoothCodecStatusTest extends TestCase { assertFalse(bcs_A.equals(bcs_C)); assertFalse(bcs_C.equals(bcs_A)); } + + private static BluetoothCodecConfig buildBluetoothCodecConfig(int sourceCodecType, + int codecPriority, int sampleRate, int bitsPerSample, int channelMode, + long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { + return new BluetoothCodecConfig.Builder() + .setCodecType(sourceCodecType) + .setCodecPriority(codecPriority) + .setSampleRate(sampleRate) + .setBitsPerSample(bitsPerSample) + .setChannelMode(channelMode) + .setCodecSpecific1(codecSpecific1) + .setCodecSpecific2(codecSpecific2) + .setCodecSpecific3(codecSpecific3) + .setCodecSpecific4(codecSpecific4) + .build(); + + } } diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/core/tests/coretests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index cf072b488efc..1c7659ec0bec 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -225,6 +225,8 @@ applications that come with the platform <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <permission name="android.permission.LOG_COMPAT_CHANGE" /> </privapp-permissions> <privapp-permissions package="com.android.providers.downloads"> @@ -501,6 +503,8 @@ applications that come with the platform <permission name="android.permission.UPDATE_DEVICE_STATS" /> <!-- Permission required for GTS test - PendingSystemUpdateTest --> <permission name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE" /> + <!-- Permission required for ATS test - CarDevicePolicyManagerTest --> + <permission name="android.permission.LOCK_DEVICE" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index b67988ee9646..c94b3d5ce6b2 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -499,6 +499,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-1556507536": { + "message": "Passing transform hint %d for window %s%s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "-1554521902": { "message": "showInsets(ime) was requested by different window: %s ", "level": "WARN", diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index c3b1cd74d29b..b61ae1259cf5 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -1075,6 +1076,53 @@ public class HardwareRenderer { ProcessInitializer.sInstance.setContext(context); } + /** + * Returns true if HardwareRender will produce output. + * + * This value is global to the process and affects all uses of HardwareRenderer, + * including + * those created by the system such as those used by the View tree when using hardware + * accelerated rendering. + * + * Default is true in all production environments, but may be false in testing-focused + * emulators or if {@link #setDrawingEnabled(boolean)} is used. + * + * Backported from android T. + * + * @hide + */ + @UnsupportedAppUsage + public static boolean isDrawingEnabled() { + return nIsDrawingEnabled(); + } + + /** + * Toggles whether or not HardwareRenderer will produce drawing output globally in the current + * process. + * + * This applies to all HardwareRenderer instances, including those created by the platform such + * as those used by the system for hardware accelerated View rendering. + * + * The capability to disable drawing output is intended for test environments, primarily + * headless ones. By setting this to false, tests that launch activities or interact with Views + * can be quicker with less RAM usage by skipping the final step of View drawing. All View + * lifecycle events will occur as normal, only the final step of rendering on the GPU to the + * display will be skipped. + * + * This can be toggled on and off at will, so screenshot tests can also run in this same + * environment by toggling drawing back on and forcing a frame to be drawn such as by calling + * view#invalidate(). Once drawn and the screenshot captured, this can then be turned back off. + * + * Backported from android T. + * + * @hide + */ + // TODO: Add link to androidx's Screenshot library for help with this + @UnsupportedAppUsage + public static void setDrawingEnabled(boolean drawingEnabled) { + nSetDrawingEnabled(drawingEnabled); + } + private static final class DestroyContextRunnable implements Runnable { private final long mNativeInstance; @@ -1393,4 +1441,8 @@ public class HardwareRenderer { private static native void nInitDisplayInfo(int width, int height, float refreshRate, int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos); + + private static native void nSetDrawingEnabled(boolean drawingEnabled); + + private static native boolean nIsDrawingEnabled(); } diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java index 850c55166edc..6fa1a694eb67 100644 --- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java +++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java @@ -89,7 +89,7 @@ abstract class KeyStoreCryptoOperationUtils { // specific sensor (the one that hasn't changed), and 2) currently the only // signal to developers is the UserNotAuthenticatedException, which doesn't // indicate a specific sensor. - boolean canUnlockViaBiometrics = true; + boolean canUnlockViaBiometrics = biometricSids.length > 0; for (long sid : biometricSids) { if (!keySids.contains(sid)) { canUnlockViaBiometrics = false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index e6d088e6537d..2adf8ce8525f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -272,8 +272,10 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; pw.println(prefix + this); - pw.println(innerPrefix + "Root taskId=" + getRootTaskId() - + " winMode=" + mRootTaskInfo.getWindowingMode()); + if (mRootTaskInfo != null) { + pw.println(innerPrefix + "Root taskId=" + mRootTaskInfo.taskId + + " winMode=" + mRootTaskInfo.getWindowingMode()); + } if (mTaskInfo1 != null) { pw.println(innerPrefix + "1 taskId=" + mTaskInfo1.taskId + " winMode=" + mTaskInfo1.getWindowingMode()); diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS index d80699de8a2d..f49e80ae16b1 100644 --- a/libs/WindowManager/Shell/tests/OWNERS +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -1,3 +1,4 @@ # Bug component: 909476 # includes OWNERS from parent directories natanieljr@google.com +pablogamito@google.com diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS index 610fd80fe73c..17f5164cf417 100644 --- a/libs/androidfw/OWNERS +++ b/libs/androidfw/OWNERS @@ -1,6 +1,6 @@ set noparent toddke@google.com -rtmitchell@google.com +zyy@google.com patb@google.com per-file CursorWindow.cpp=omakoto@google.com diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index b8fa55a18dac..c804418e8380 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -88,6 +88,8 @@ bool Properties::enableWebViewOverlays = false; StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI; +DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized; + bool Properties::load() { bool prevDebugLayersUpdates = debugLayersUpdates; bool prevDebugOverdraw = debugOverdraw; @@ -141,6 +143,9 @@ bool Properties::load() { enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, false); + // call isDrawingEnabled to force loading of the property + isDrawingEnabled(); + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } @@ -210,5 +215,19 @@ void Properties::overrideRenderPipelineType(RenderPipelineType type, bool inUnit sRenderPipelineType = type; } +void Properties::setDrawingEnabled(bool newDrawingEnabled) { + drawingEnabled = newDrawingEnabled ? DrawingEnabled::On : DrawingEnabled::Off; + enableRTAnimations = newDrawingEnabled; +} + +bool Properties::isDrawingEnabled() { + if (drawingEnabled == DrawingEnabled::NotInitialized) { + bool drawingEnabledProp = base::GetBoolProperty(PROPERTY_DRAWING_ENABLED, true); + drawingEnabled = drawingEnabledProp ? DrawingEnabled::On : DrawingEnabled::Off; + enableRTAnimations = drawingEnabledProp; + } + return drawingEnabled == DrawingEnabled::On; +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 7df6e2c92247..7f9782bf8d20 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -187,6 +187,12 @@ enum DebugLevel { */ #define PROPERTY_WEBVIEW_OVERLAYS_ENABLED "debug.hwui.webview_overlays_enabled" +/** + * Property for globally GL drawing state. Can be overridden per process with + * setDrawingEnabled. + */ +#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled" + /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// @@ -208,6 +214,8 @@ enum class StretchEffectBehavior { UniformScale // Uniform scale stretch everywhere }; +enum class DrawingEnabled { NotInitialized, On, Off }; + /** * Renderthread-only singleton which manages several static rendering properties. Most of these * are driven by system properties which are queried once at initialization, and again if init() @@ -301,6 +309,11 @@ public: stretchEffectBehavior = behavior; } + // Represents if drawing is enabled. Should only be Off in headless testing environments + static DrawingEnabled drawingEnabled; + static bool isDrawingEnabled(); + static void setDrawingEnabled(bool enable); + private: static StretchEffectBehavior stretchEffectBehavior; static ProfileType sProfileType; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 54367b8334cb..9e56584ed582 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -817,6 +817,14 @@ static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos); } +static void android_view_ThreadedRenderer_setDrawingEnabled(JNIEnv*, jclass, jboolean enabled) { + Properties::setDrawingEnabled(enabled); +} + +static jboolean android_view_ThreadedRenderer_isDrawingEnabled(JNIEnv*, jclass) { + return Properties::isDrawingEnabled(); +} + // ---------------------------------------------------------------------------- // HardwareRendererObserver // ---------------------------------------------------------------------------- @@ -953,6 +961,9 @@ static const JNINativeMethod gMethods[] = { {"preload", "()V", (void*)android_view_ThreadedRenderer_preload}, {"isWebViewOverlaysEnabled", "()Z", (void*)android_view_ThreadedRenderer_isWebViewOverlaysEnabled}, + {"nSetDrawingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDrawingEnabled}, + {"nIsDrawingEnabled", "()Z", (void*)android_view_ThreadedRenderer_isDrawingEnabled}, + }; static JavaVM* mJvm = nullptr; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 2f3a509831d1..bb0b1352c360 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -256,7 +256,7 @@ void CanvasContext::setStopped(bool stopped) { } void CanvasContext::allocateBuffers() { - if (mNativeSurface) { + if (mNativeSurface && Properties::isDrawingEnabled()) { ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow()); } } @@ -480,7 +480,8 @@ nsecs_t CanvasContext::draw() { SkRect dirty; mDamageAccumulator.finish(&dirty); - if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { + if (!Properties::isDrawingEnabled() || + (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); if (auto grContext = getGrContext()) { // Submit to ensure that any texture uploads complete and Skia can diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 6dbfcc349d50..2fed4686f16e 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -90,9 +90,17 @@ public: * and false otherwise (e.g. cache limits have been exceeded). */ bool pinImages(std::vector<SkImage*>& mutableImages) { + if (!Properties::isDrawingEnabled()) { + return true; + } return mRenderPipeline->pinImages(mutableImages); } - bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { return mRenderPipeline->pinImages(images); } + bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { + if (!Properties::isDrawingEnabled()) { + return true; + } + return mRenderPipeline->pinImages(images); + } /** * Unpin any image that had be previously pinned to the GPU cache diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index ed8cdb941792..c7f56961e498 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -32,7 +32,6 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -3255,6 +3254,54 @@ public class AudioManager { } /** + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) + public void setHfpEnabled(boolean enable) { + AudioSystem.setParameters("hfp_enable=" + enable); + } + + /** + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) + public void setHfpVolume(int volume) { + AudioSystem.setParameters("hfp_volume=" + volume); + } + + /** + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) + public void setHfpSamplingRate(int rate) { + AudioSystem.setParameters("hfp_set_sampling_rate=" + rate); + } + + /** + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) + public void setBluetoothHeadsetProperties(@NonNull String name, boolean hasNrecEnabled, + boolean hasWbsEnabled) { + AudioSystem.setParameters("bt_headset_name=" + name + + ";bt_headset_nrec=" + (hasNrecEnabled ? "on" : "off") + + ";bt_wbs=" + (hasWbsEnabled ? "on" : "off")); + } + + /** + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) + public void setA2dpSuspended(boolean enable) { + AudioSystem.setParameters("A2dpSuspended=" + enable); + } + + /** * Gets a variable number of parameter values from audio hardware. * * @param keys list of parameters @@ -5748,112 +5795,25 @@ public class AudioManager { } } - /** - * Indicate Hearing Aid connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - * * BluetoothDevice, int, int, boolean, int)} and - * and {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * @param musicDevice Default get system volume for the connecting device. - * (either {@link android.bluetooth.BluetoothProfile.hearingaid} or - * {@link android.bluetooth.BluetoothProfile.HEARING_AID}) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothHearingAidDeviceConnectionState( - BluetoothDevice device, int state, boolean suppressNoisyIntent, - int musicDevice) { - final IAudioService service = getService(); - try { - service.setBluetoothHearingAidDeviceConnectionState(device, - state, suppressNoisyIntent, musicDevice); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate Le Audio output device connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothLeAudioOutDeviceConnectionState(BluetoothDevice device, int state, - boolean suppressNoisyIntent) { - final IAudioService service = getService(); - try { - service.setBluetoothLeAudioOutDeviceConnectionState(device, state, suppressNoisyIntent); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** - * Indicate Le Audio input connection state change. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * {@hide} - */ - public void setBluetoothLeAudioInDeviceConnectionState(BluetoothDevice device, int state) { - final IAudioService service = getService(); - try { - service.setBluetoothLeAudioInDeviceConnectionState(device, state); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate A2DP source or sink connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, - * int, boolean, int)} and - * {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}. - * @param device Bluetooth device connected/disconnected - * @param state new connection state, {@link BluetoothProfile#STATE_CONNECTED} - * or {@link BluetoothProfile#STATE_DISCONNECTED} - * @param profile profile for the A2DP device - * @param a2dpVolume New volume for the connecting device. Does nothing if disconnecting. - * (either {@link android.bluetooth.BluetoothProfile.A2DP} or - * {@link android.bluetooth.BluetoothProfile.A2DP_SINK}) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - BluetoothDevice device, int state, - int profile, boolean suppressNoisyIntent, int a2dpVolume) { - final IAudioService service = getService(); - try { - service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, - state, profile, suppressNoisyIntent, a2dpVolume); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate A2DP device configuration has changed. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to - * {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice, int, int, - * boolean, int)} and - * {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, int, boolean, int)} - * @param device Bluetooth device whose configuration has changed. + * Indicate Bluetooth profile connection state change. + * Configuration changes for A2DP are indicated by having the same <code>newDevice</code> and + * <code>previousDevice</code> + * This operation is asynchronous. + * + * @param newDevice Bluetooth device connected or null if there is no new devices + * @param previousDevice Bluetooth device disconnected or null if there is no disconnected + * devices + * @param info contain all info related to the device. {@link BtProfileConnectionInfo} * {@hide} */ - public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) + public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice, + @Nullable BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { final IAudioService service = getService(); try { - service.handleBluetoothA2dpDeviceConfigChange(device); + service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/BtProfileConnectionInfo.aidl b/media/java/android/media/BtProfileConnectionInfo.aidl new file mode 100644 index 000000000000..047f06be0964 --- /dev/null +++ b/media/java/android/media/BtProfileConnectionInfo.aidl @@ -0,0 +1,20 @@ +/* + * 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.media; + +parcelable BtProfileConnectionInfo; + diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java new file mode 100644 index 000000000000..19ea2de6a434 --- /dev/null +++ b/media/java/android/media/BtProfileConnectionInfo.java @@ -0,0 +1,163 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.bluetooth.BluetoothProfile; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Contains information about Bluetooth profile connection state changed + * {@hide} + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class BtProfileConnectionInfo implements Parcelable { + /** @hide */ + @IntDef({ + BluetoothProfile.A2DP, + BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper + BluetoothProfile.HEADSET, // Can only be set by BtHelper + BluetoothProfile.HEARING_AID, + BluetoothProfile.LE_AUDIO, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BtProfile {} + + private final @BtProfile int mProfile; + private final boolean mSupprNoisy; + private final int mVolume; + private final boolean mIsLeOutput; + + private BtProfileConnectionInfo(@BtProfile int profile, boolean suppressNoisyIntent, int volume, + boolean isLeOutput) { + mProfile = profile; + mSupprNoisy = suppressNoisyIntent; + mVolume = volume; + mIsLeOutput = isLeOutput; + } + + /** + * Constructor used by BtHelper when a profile is connected + * {@hide} + */ + public BtProfileConnectionInfo(@BtProfile int profile) { + this(profile, false, -1, false); + } + + public static final @NonNull Parcelable.Creator<BtProfileConnectionInfo> CREATOR = + new Parcelable.Creator<BtProfileConnectionInfo>() { + @Override + public BtProfileConnectionInfo createFromParcel(Parcel source) { + return new BtProfileConnectionInfo(source.readInt(), source.readBoolean(), + source.readInt(), source.readBoolean()); + } + + @Override + public BtProfileConnectionInfo[] newArray(int size) { + return new BtProfileConnectionInfo[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) { + dest.writeInt(mProfile); + dest.writeBoolean(mSupprNoisy); + dest.writeInt(mVolume); + dest.writeBoolean(mIsLeOutput); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Constructor for A2dp info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + * + * @param volume of device -1 to ignore value + */ + public static @NonNull BtProfileConnectionInfo a2dpInfo(boolean suppressNoisyIntent, + int volume) { + return new BtProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, volume, + false); + } + + /** + * Constructor for hearing aid info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + */ + public static @NonNull BtProfileConnectionInfo hearingAidInfo(boolean suppressNoisyIntent) { + return new BtProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, -1, + false); + } + + /** + * constructor for le audio info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + * + * @param isLeOutput if true mean the device is an output device, if false it's an input device + */ + public static @NonNull BtProfileConnectionInfo leAudio(boolean suppressNoisyIntent, + boolean isLeOutput) { + return new BtProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, -1, + isLeOutput); + } + + /** + * @return The profile connection + */ + public @BtProfile int getProfile() { + return mProfile; + } + + /** + * @return {@code true} if {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be + * sent + */ + public boolean getSuppressNoisyIntent() { + return mSupprNoisy; + } + + /** + * Only for {@link BluetoothProfile.A2DP} profile + * @return the volume of the connection or -1 if the value is ignored + */ + public int getVolume() { + return mVolume; + } + + /** + * Only for {@link BluetoothProfile.LE_AUDIO} profile + * @return {@code true} is the LE device is an output device, {@code false} if it's an input + * device + */ + public boolean getIsLeOutput() { + return mIsLeOutput; + } +} diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index dd44fdf1e8c4..5ff56f9c680d 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -24,6 +24,7 @@ import android.media.AudioFocusInfo; import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; +import android.media.BtProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -207,8 +208,6 @@ interface IAudioService { void setWiredDeviceConnectionState(int type, int state, String address, String name, String caller); - void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device); - @UnsupportedAppUsage AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); @@ -268,16 +267,8 @@ interface IAudioService { oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio); - void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device, - int state, boolean suppressNoisyIntent, int musicDevice); - - void setBluetoothLeAudioOutDeviceConnectionState(in BluetoothDevice device, int state, - boolean suppressNoisyIntent); - - void setBluetoothLeAudioInDeviceConnectionState(in BluetoothDevice device, int state); - - void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device, - int state, int profile, boolean suppressNoisyIntent, int a2dpVolume); + void handleBluetoothActiveDeviceChanged(in BluetoothDevice newDevice, + in BluetoothDevice previousDevice, in BtProfileConnectionInfo info); oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult, in IAudioPolicyCallback pcb); diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 1616c03112a8..54e19db8e7e9 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -163,6 +163,14 @@ public abstract class Image implements AutoCloseable { * {@link android.graphics.BitmapFactory#decodeByteArray BitmapFactory#decodeByteArray}. * </td> * </tr> + * <tr> + * <td>{@link android.graphics.ImageFormat#YCBCR_P010 YCBCR_P010}</td> + * <td>1</td> + * <td>P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane + * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. + * </td> + * </tr> * </table> * * @see android.graphics.ImageFormat diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 8b9153621165..72dd2bd43eb6 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -60,10 +60,10 @@ import java.util.concurrent.locks.ReentrantLock; with {@link MediaExtractor}, {@link MediaSync}, {@link MediaMuxer}, {@link MediaCrypto}, {@link MediaDrm}, {@link Image}, {@link Surface}, and {@link AudioTrack}.) <p> - <center><object style="width: 540px; height: 205px;" type="image/svg+xml" - data="../../../images/media/mediacodec_buffers.svg"><img - src="../../../images/media/mediacodec_buffers.png" style="width: 540px; height: 205px" - alt="MediaCodec buffer flow diagram"></object></center> + <center> + <img src="../../../images/media/mediacodec_buffers.svg" style="width: 540px; height: 205px" + alt="MediaCodec buffer flow diagram"> + </center> <p> In broad terms, a codec processes input data to generate output data. It processes data asynchronously and uses a set of input and output buffers. At a simplistic level, you request @@ -268,10 +268,10 @@ import java.util.concurrent.locks.ReentrantLock; Uninitialized, Configured and Error, whereas the Executing state conceptually progresses through three sub-states: Flushed, Running and End-of-Stream. <p> - <center><object style="width: 516px; height: 353px;" type="image/svg+xml" - data="../../../images/media/mediacodec_states.svg"><img - src="../../../images/media/mediacodec_states.png" style="width: 519px; height: 356px" - alt="MediaCodec state diagram"></object></center> + <center> + <img src="../../../images/media/mediacodec_states.svg" style="width: 519px; height: 356px" + alt="MediaCodec state diagram"> + </center> <p> When you create a codec using one of the factory methods, the codec is in the Uninitialized state. First, you need to configure it via {@link #configure configure(…)}, which brings @@ -513,10 +513,10 @@ import java.util.concurrent.locks.ReentrantLock; Similarly, upon an initial call to {@code start} the codec will move directly to the Running sub-state and start passing available input buffers via the callback. <p> - <center><object style="width: 516px; height: 353px;" type="image/svg+xml" - data="../../../images/media/mediacodec_async_states.svg"><img - src="../../../images/media/mediacodec_async_states.png" style="width: 516px; height: 353px" - alt="MediaCodec state diagram for asynchronous operation"></object></center> + <center> + <img src="../../../images/media/mediacodec_async_states.svg" style="width: 516px; height: 353px" + alt="MediaCodec state diagram for asynchronous operation"> + </center> <p> MediaCodec is typically used like this in asynchronous mode: <pre class=prettyprint> @@ -5107,7 +5107,6 @@ final public class MediaCodec { public MediaImage( @NonNull ByteBuffer buffer, @NonNull ByteBuffer info, boolean readOnly, long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) { - mFormat = ImageFormat.YUV_420_888; mTimestamp = timestamp; mIsImageValid = true; mIsReadOnly = buffer.isReadOnly(); @@ -5120,6 +5119,11 @@ final public class MediaCodec { mBufferContext = 0; + int cbPlaneOffset = -1; + int crPlaneOffset = -1; + int planeOffsetInc = -1; + int pixelStride = -1; + // read media-info. See MediaImage2 if (info.remaining() == 104) { int type = info.getInt(); @@ -5137,14 +5141,27 @@ final public class MediaCodec { "unsupported size: " + mWidth + "x" + mHeight); } int bitDepth = info.getInt(); - if (bitDepth != 8) { + if (bitDepth != 8 && bitDepth != 10) { throw new UnsupportedOperationException("unsupported bit depth: " + bitDepth); } int bitDepthAllocated = info.getInt(); - if (bitDepthAllocated != 8) { + if (bitDepthAllocated != 8 && bitDepthAllocated != 16) { throw new UnsupportedOperationException( "unsupported allocated bit depth: " + bitDepthAllocated); } + if (bitDepth == 8 && bitDepthAllocated == 8) { + mFormat = ImageFormat.YUV_420_888; + planeOffsetInc = 1; + pixelStride = 2; + } else if (bitDepth == 10 && bitDepthAllocated == 16) { + mFormat = ImageFormat.YCBCR_P010; + planeOffsetInc = 2; + pixelStride = 4; + } else { + throw new UnsupportedOperationException("couldn't infer ImageFormat" + + " bitDepth: " + bitDepth + " bitDepthAllocated: " + bitDepthAllocated); + } + mPlanes = new MediaPlane[numPlanes]; for (int ix = 0; ix < numPlanes; ix++) { int planeOffset = info.getInt(); @@ -5166,12 +5183,31 @@ final public class MediaCodec { buffer.limit(buffer.position() + Utils.divUp(bitDepth, 8) + (mHeight / vert - 1) * rowInc + (mWidth / horiz - 1) * colInc); mPlanes[ix] = new MediaPlane(buffer.slice(), rowInc, colInc); + if ((mFormat == ImageFormat.YUV_420_888 || mFormat == ImageFormat.YCBCR_P010) + && ix == 1) { + cbPlaneOffset = planeOffset; + } else if ((mFormat == ImageFormat.YUV_420_888 + || mFormat == ImageFormat.YCBCR_P010) && ix == 2) { + crPlaneOffset = planeOffset; + } } } else { throw new UnsupportedOperationException( "unsupported info length: " + info.remaining()); } + // Validate chroma semiplanerness. + if (mFormat == ImageFormat.YCBCR_P010) { + if (crPlaneOffset != cbPlaneOffset + planeOffsetInc) { + throw new UnsupportedOperationException("Invalid plane offsets" + + " cbPlaneOffset: " + cbPlaneOffset + " crPlaneOffset: " + crPlaneOffset); + } + if (mPlanes[1].getPixelStride() != pixelStride + || mPlanes[2].getPixelStride() != pixelStride) { + throw new UnsupportedOperationException("Invalid pixelStride"); + } + } + if (cropRect == null) { cropRect = new Rect(0, 0, mWidth, mHeight); } diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 374cc7592faf..3c152fb68c0a 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -426,6 +426,12 @@ public final class MediaCodecInfo { /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */ public static final int COLOR_Format24BitABGR6666 = 43; + /** @hide + * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane + * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. */ + public static final int COLOR_FormatYUVP010 = 54; + /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */ public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100; // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference. diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index 86ed50bacb63..72ee00f03774 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -102,6 +102,13 @@ public abstract class PlayerBase { mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE; }; + /** @hide */ + public int getPlayerIId() { + synchronized (mLock) { + return mPlayerIId; + } + } + /** * Call from derived class when instantiation / initialization is successful */ diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 2636ab227646..8dcdc989ec8f 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -953,7 +953,7 @@ android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_t Parcel* parcel = parcelForJavaObject(env, jAttributionSource); android::content::AttributionSourceState attributionSource; attributionSource.readFromParcel(parcel); - sp<MediaPlayer> mp = new MediaPlayer(attributionSource); + sp<MediaPlayer> mp = sp<MediaPlayer>::make(attributionSource); if (mp == NULL) { jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index ecd9cc1cd098..39b560b393a9 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -65,6 +65,7 @@ bool isPossiblyYUV(PixelFormat format) { case HAL_PIXEL_FORMAT_Y8: case HAL_PIXEL_FORMAT_Y16: case HAL_PIXEL_FORMAT_RAW16: + case HAL_PIXEL_FORMAT_RAW12: case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW_OPAQUE: case HAL_PIXEL_FORMAT_BLOB: diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp index 923377c8180e..f90796e415c0 100644 --- a/media/native/midi/amidi.cpp +++ b/media/native/midi/amidi.cpp @@ -325,8 +325,8 @@ public: } uint8_t readBuffer[AMIDI_PACKET_SIZE]; - ssize_t readCount = read(mPort->ufd, readBuffer, sizeof(readBuffer)); - if (readCount == EINTR || readCount < 1) { + ssize_t readCount = TEMP_FAILURE_RETRY(read(mPort->ufd, readBuffer, sizeof(readBuffer))); + if (readCount < 1) { return AMEDIA_ERROR_UNKNOWN; } @@ -407,7 +407,8 @@ ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPo ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data + numSent, blockSize, timestamp); - ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes); + ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, + numTransferBytes)); if (numWritten < 0) { break; // error so bail out. } @@ -430,7 +431,8 @@ media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPor uint8_t opCode = AMIDI_OPCODE_FLUSH; ssize_t numTransferBytes = 1; - ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes); + ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, &opCode, + numTransferBytes)); if (numWritten < numTransferBytes) { ALOGE("AMidiInputPort_flush Couldn't write MIDI flush. requested:%zd, written:%zd", diff --git a/mms/OWNERS b/mms/OWNERS index 7f05a2a24d6e..f56845ed80c7 100644 --- a/mms/OWNERS +++ b/mms/OWNERS @@ -3,16 +3,15 @@ set noparent tgunn@google.com breadley@google.com rgreenwalt@google.com -amitmahajan@google.com fionaxu@google.com jackyu@google.com jminjie@google.com satk@google.com shuoq@google.com -nazaninb@google.com sarahchin@google.com xiaotonj@google.com huiwang@google.com jayachandranc@google.com chinmayd@google.com amruthr@google.com +sasindran@google.com diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp new file mode 100644 index 000000000000..2b81200f0079 --- /dev/null +++ b/omapi/aidl/Android.bp @@ -0,0 +1,35 @@ +// Copyright 2020, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +aidl_interface { + name: "android.se.omapi", + vendor_available: true, + srcs: ["android/se/omapi/*.aidl"], + stability: "vintf", + backend: { + java: { + sdk_version: "module_current", + }, + rust: { + enabled: true, + }, + ndk: { + separate_platform_variant: false, + }, + }, +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl new file mode 100644 index 000000000000..725013a35cde --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl @@ -0,0 +1,46 @@ +/* + * 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. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementChannel { + void close(); + boolean isClosed(); + boolean isBasicChannel(); + byte[] getSelectResponse(); + byte[] transmit(in byte[] command); + boolean selectNext(); +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl new file mode 100644 index 000000000000..77e1c53f47ac --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementListener { +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl new file mode 100644 index 000000000000..2b10c473c902 --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementReader { + boolean isSecureElementPresent(); + android.se.omapi.ISecureElementSession openSession(); + void closeSessions(); + boolean reset(); +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl new file mode 100644 index 000000000000..0c8e431d18a1 --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *//* + * Copyright (c) 2015-2017, The Linux Foundation. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementService { + String[] getReaders(); + android.se.omapi.ISecureElementReader getReader(in String reader); + boolean[] isNfcEventAllowed(in String reader, in byte[] aid, in String[] packageNames, in int userId); +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl new file mode 100644 index 000000000000..06287c551f5c --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *//* + * Copyright (c) 2015-2017, The Linux Foundation. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementSession { + byte[] getAtr(); + void close(); + void closeChannels(); + boolean isClosed(); + android.se.omapi.ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener); + android.se.omapi.ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener); +} diff --git a/core/java/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl index 4ae57ab829cb..bbd3c148caaf 100644 --- a/core/java/android/se/omapi/ISecureElementChannel.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl @@ -22,6 +22,7 @@ package android.se.omapi; import android.se.omapi.ISecureElementSession; /** @hide */ +@VintfStability interface ISecureElementChannel { /** @@ -58,6 +59,9 @@ interface ISecureElementChannel { * Transmits the specified command APDU and returns the response APDU. * MANAGE channel commands are not supported. * Selection of applets is not supported in logical channels. + * + * @param command Command APDU, its structure is defined in ISO/IEC 7816-4 + * in Standard byte format */ byte[] transmit(in byte[] command); diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl index e9dd18181c56..479dcd7d5acf 100644 --- a/core/java/android/se/omapi/ISecureElementListener.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl @@ -23,5 +23,6 @@ package android.se.omapi; * Interface to receive call-backs when the service is connected. * @hide */ +@VintfStability interface ISecureElementListener { } diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl index 41244ab058e0..a6979face61f 100644 --- a/core/java/android/se/omapi/ISecureElementReader.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl @@ -22,6 +22,7 @@ package android.se.omapi; import android.se.omapi.ISecureElementSession; /** @hide */ +@VintfStability interface ISecureElementReader { /** @@ -34,7 +35,7 @@ interface ISecureElementReader { * Connects to a secure element in this reader. <br> * This method prepares (initialises) the Secure Element for communication * before the Session object is returned (e.g. powers the Secure Element by - * ICC ON if its not already on). There might be multiple sessions opened at + * ICC ON if it is not already on). There might be multiple sessions opened at * the same time on the same reader. The system ensures the interleaving of * APDUs between the respective sessions. * diff --git a/core/java/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/android/se/omapi/ISecureElementService.aidl index 4fa799e78757..13707ec22b5c 100644 --- a/core/java/android/se/omapi/ISecureElementService.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementService.aidl @@ -28,23 +28,31 @@ import android.se.omapi.ISecureElementReader; * SecureElement service interface. * @hide */ +@VintfStability interface ISecureElementService { /** * Returns the friendly names of available Secure Element readers. + * <ul> + * <li>If the reader is a SIM reader, then its name must be "SIM[Slot]".</li> + * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li> + * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li> + * </ul> + * Slot is a decimal number without leading zeros. The Numbering must start with 1 + * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...). */ String[] getReaders(); /** * Returns SecureElement Service reader object to the given name. */ - ISecureElementReader getReader(String reader); + ISecureElementReader getReader(in String reader); /** * Checks if the application defined by the package name is allowed to * receive NFC transaction events for the defined AID. */ - boolean[] isNFCEventAllowed(String reader, in byte[] aid, - in String[] packageNames); + boolean[] isNfcEventAllowed(in String reader, in byte[] aid, + in String[] packageNames, in int userId); } diff --git a/core/java/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl index 8ea599f2e866..129ecc4ddaa3 100644 --- a/core/java/android/se/omapi/ISecureElementSession.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl @@ -27,6 +27,7 @@ import android.se.omapi.ISecureElementReader; import android.se.omapi.ISecureElementListener; /** @hide */ +@VintfStability interface ISecureElementSession { /** @@ -45,7 +46,6 @@ interface ISecureElementSession { */ void closeChannels(); - /** * Tells if this session is closed. * @@ -59,15 +59,19 @@ interface ISecureElementSession { * applet if aid != null. * Logical channels cannot be opened with this connection. * Use interface method openLogicalChannel() to open a logical channel. + * Listener is passed to secure element service and used to monitor whether + * the client application that uses OMAPI is still alive or not. */ ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, - ISecureElementListener listener); + in ISecureElementListener listener); /** * Opens a connection using the next free logical channel of the card in the * specified reader. Selects the specified applet. * Selection of other applets with this connection is not supported. + * Listener is passed to secure element service and used to monitor whether + * the client application that uses OMAPI is still alive or not. */ ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, - ISecureElementListener listener); + in ISecureElementListener listener); } diff --git a/omapi/aidl/vts/functional/AccessControlApp/Android.bp b/omapi/aidl/vts/functional/AccessControlApp/Android.bp new file mode 100644 index 000000000000..f03c3f6eb647 --- /dev/null +++ b/omapi/aidl/vts/functional/AccessControlApp/Android.bp @@ -0,0 +1,54 @@ +// +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "VtsHalOmapiSeAccessControlTestCases", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsHalOmapiSeAccessControlTestCases.cpp", + ], + shared_libs: [ + "libbase", + "liblog", + "libcutils", + "libhidlbase", + "libnativehelper", + "libutils", + "libbinder_ndk", + ], + static_libs: [ + "VtsHalHidlTargetTestBase", + "android.se.omapi-V1-ndk", + ], + cflags: [ + "-O0", + "-g", + "-Wall", + "-Werror", + ], + require_root: true, + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp new file mode 100644 index 000000000000..9ea65431417a --- /dev/null +++ b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp @@ -0,0 +1,428 @@ +/* + * 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. + */ + +#include <aidl/android/se/omapi/BnSecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementChannel.h> +#include <aidl/android/se/omapi/ISecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementReader.h> +#include <aidl/android/se/omapi/ISecureElementService.h> +#include <aidl/android/se/omapi/ISecureElementSession.h> + +#include <VtsCoreUtil.h> +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <binder/IServiceManager.h> +#include <cutils/properties.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> +#include <utils/String16.h> + +using namespace std; +using namespace ::testing; +using namespace android; + +int main(int argc, char** argv) { + InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + return status; +} + +namespace { + +class OMAPISEAccessControlTest : public TestWithParam<std::string> { + protected: + + class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {}; + + /** + * Verifies TLV data + * + * @return true if the data is tlv formatted, false otherwise + */ + bool verifyBerTlvData(std::vector<uint8_t> tlv) { + if (tlv.size() == 0) { + LOG(ERROR) << "Invalid tlv, null"; + return false; + } + int i = 0; + if ((tlv[i++] & 0x1F) == 0x1F) { + // extra byte for TAG field + i++; + } + + int len = tlv[i++] & 0xFF; + if (len > 127) { + // more than 1 byte for length + int bytesLength = len - 128; + len = 0; + for (int j = bytesLength; j > 0; j--) { + len += (len << 8) + (tlv[i++] & 0xFF); + } + } + // Additional 2 bytes for the SW + return (tlv.size() == (i + len + 2)); + } + + void testSelectableAid( + std::vector<std::vector<uint8_t>> authorizedAids) { + for (auto aid : authorizedAids) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::vector<uint8_t> selectResponse = {}; + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + ASSERT_TRUE( + verifyBerTlvData(selectResponse)) << "Select Response is not complete"; + } + } + } + } + + void testUnauthorisedAid( + std::vector<std::vector<uint8_t>> unAuthorizedAids) { + for (auto aid : unAuthorizedAids) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + if (!res.isOk()) { + ASSERT_EQ(res.getExceptionCode(), EX_SECURITY); + ASSERT_FALSE(res.isOk()) << "expected failed status for this test"; + } + } + } + } + } + + void testTransmitAPDU( + std::vector<uint8_t> aid, + std::vector<std::vector<uint8_t>> apdus) { + for (auto apdu : apdus) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + ASSERT_NE(reader, nullptr) << "reader is null"; + bool status = false; + std::vector<uint8_t> selectResponse = {}; + std::vector<uint8_t> transmitResponse = {}; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + ASSERT_TRUE( + verifyBerTlvData(selectResponse)) << "Select Response is not complete"; + + res = channel->transmit(apdu, &transmitResponse); + LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() + << " Message: " << res.getMessage(); + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + ASSERT_TRUE(res.isOk()) << "failed to transmit"; + } + } + } + } + + void testUnauthorisedAPDU( + std::vector<uint8_t> aid, + std::vector<std::vector<uint8_t>> apdus) { + for (auto apdu : apdus) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + ASSERT_NE(reader, nullptr) << "reader is null"; + bool status = false; + std::vector<uint8_t> selectResponse = {}; + std::vector<uint8_t> transmitResponse = {}; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + ASSERT_TRUE( + verifyBerTlvData(selectResponse)) << "Select Response is not complete"; + + res = channel->transmit(apdu, &transmitResponse); + LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() + << " Message: " << res.getMessage(); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + if (!res.isOk()) { + ASSERT_EQ(res.getExceptionCode(), EX_SECURITY); + ASSERT_FALSE(res.isOk()) << "expected failed status for this test"; + } + } + } + } + } + + bool supportOMAPIReaders() { + return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())); + } + + void getFirstApiLevel(int32_t* outApiLevel) { + int32_t firstApiLevel = property_get_int32(FEATURE_SE_API_LEVEL.c_str(), -1); + if (firstApiLevel < 0) { + firstApiLevel = property_get_int32(FEATURE_SE_SDK_VERSION.c_str(), -1); + } + ASSERT_GT(firstApiLevel, 0); // first_api_level must exist + *outApiLevel = firstApiLevel; + return; + } + + bool supportsHardware() { + bool lowRamDevice = property_get_bool(FEATURE_SE_LOW_RAM.c_str(), true); + return !lowRamDevice || deviceSupportsFeature(FEATURE_SE_HARDWARE_WATCH.c_str()) || + deviceSupportsFeature(FEATURE_SE_OMAPI_SERVICE.c_str()); // android.se.omapi + } + + void SetUp() override { + ASSERT_TRUE(supportsHardware()); + int32_t apiLevel; + getFirstApiLevel(&apiLevel); + ASSERT_TRUE(apiLevel > 27); + ASSERT_TRUE(supportOMAPIReaders()); + LOG(INFO) << "get OMAPI service with name:" << GetParam(); + ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str())); + mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder); + ASSERT_TRUE(mOmapiSeService); + + std::vector<std::string> readers = {}; + + if (mOmapiSeService != NULL) { + auto status = mOmapiSeService->getReaders(&readers); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + for (auto readerName : readers) { + // Filter eSE readers only + if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) { + std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader; + status = mOmapiSeService->getReader(readerName, &reader); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + mVSReaders[readerName] = reader; + } + } + } + } + + void TearDown() override { + if (mOmapiSeService != nullptr) { + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + reader->closeSessions(); + } + } + } + } + + static inline std::string const ESE_READER_PREFIX = "eSE"; + static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese"; + static inline std::string const FEATURE_SE_LOW_RAM = "ro.config.low_ram"; + static inline std::string const FEATURE_SE_HARDWARE_WATCH = "android.hardware.type.watch"; + static inline std::string const FEATURE_SE_OMAPI_SERVICE = "com.android.se"; + static inline std::string const FEATURE_SE_SDK_VERSION = "ro.build.version.sdk"; + static inline std::string const FEATURE_SE_API_LEVEL = "ro.product.first_api_level"; + + std::vector<uint8_t> AID_40 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x40}; + std::vector<uint8_t> AID_41 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x41}; + std::vector<uint8_t> AID_42 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x42}; + std::vector<uint8_t> AID_43 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x43}; + std::vector<uint8_t> AID_44 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x44}; + std::vector<uint8_t> AID_45 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x45}; + std::vector<uint8_t> AID_46 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x46}; + std::vector<uint8_t> AID_47 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x47}; + std::vector<uint8_t> AID_48 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x48}; + std::vector<uint8_t> AID_49 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x49}; + std::vector<uint8_t> AID_4A = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4A}; + std::vector<uint8_t> AID_4B = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4B}; + std::vector<uint8_t> AID_4C = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4C}; + std::vector<uint8_t> AID_4D = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4D}; + std::vector<uint8_t> AID_4E = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4E}; + std::vector<uint8_t> AID_4F = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4F}; + + std::vector<std::vector<uint8_t>> AUTHORIZED_AID = {AID_40, AID_41, AID_42, AID_44, AID_45, + AID_47, AID_48, AID_49, AID_4A, AID_4B, + AID_4C, AID_4D, AID_4E, AID_4F}; + std::vector<std::vector<uint8_t>> UNAUTHORIZED_AID = {AID_43, AID_46}; + + /* Authorized APDU for AID_40 */ + std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_40 = { + {0x00, 0x06, 0x00, 0x00}, + {0xA0, 0x06, 0x00, 0x00}, + }; + /* Unauthorized APDU for AID_40 */ + std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_40 = { + {0x00, 0x08, 0x00, 0x00, 0x00}, + {0x80, 0x06, 0x00, 0x00}, + {0xA0, 0x08, 0x00, 0x00, 0x00}, + {0x94, 0x06, 0x00, 0x00, 0x00}, + }; + + /* Authorized APDU for AID_41 */ + std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_41 = { + {0x94, 0x06, 0x00, 0x00}, + {0x94, 0x08, 0x00, 0x00, 0x00}, + {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}}; + /* Unauthorized APDU for AID_41 */ + std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_41 = { + {0x00, 0x06, 0x00, 0x00}, + {0x80, 0x06, 0x00, 0x00}, + {0xA0, 0x06, 0x00, 0x00}, + {0x00, 0x08, 0x00, 0x00, 0x00}, + {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0x80, 0x08, 0x00, 0x00, 0x00}, + {0xA0, 0x08, 0x00, 0x00, 0x00}, + {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + }; + + std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService; + + std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> + mVSReaders = {}; +}; + +TEST_P(OMAPISEAccessControlTest, TestAuthorizedAID) { + testSelectableAid(AUTHORIZED_AID); +} + +TEST_P(OMAPISEAccessControlTest, TestUnauthorizedAID) { + testUnauthorisedAid(UNAUTHORIZED_AID); +} + +TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID40) { + testTransmitAPDU(AID_40, AUTHORIZED_APDU_AID_40); +} + +TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID40) { + testUnauthorisedAPDU(AID_40, UNAUTHORIZED_APDU_AID_40); +} + +TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID41) { + testTransmitAPDU(AID_41, AUTHORIZED_APDU_AID_41); +} + +TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID41) { + testUnauthorisedAPDU(AID_41, UNAUTHORIZED_APDU_AID_41); +} + +INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEAccessControlTest, + testing::ValuesIn(::android::getAidlHalInstanceNames( + aidl::android::se::omapi::ISecureElementService::descriptor)), + android::hardware::PrintInstanceNameToString); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEAccessControlTest); + +} // namespace diff --git a/omapi/aidl/vts/functional/omapi/Android.bp b/omapi/aidl/vts/functional/omapi/Android.bp new file mode 100644 index 000000000000..c3ab8d13920c --- /dev/null +++ b/omapi/aidl/vts/functional/omapi/Android.bp @@ -0,0 +1,54 @@ +// +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "VtsHalOmapiSeServiceV1_TargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsHalOmapiSeServiceV1_TargetTest.cpp", + ], + shared_libs: [ + "libbase", + "liblog", + "libcutils", + "libhidlbase", + "libnativehelper", + "libutils", + "libbinder_ndk", + ], + static_libs: [ + "VtsHalHidlTargetTestBase", + "android.se.omapi-V1-ndk", + ], + cflags: [ + "-O0", + "-g", + "-Wall", + "-Werror", + ], + require_root: true, + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp new file mode 100644 index 000000000000..319cb7e70884 --- /dev/null +++ b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp @@ -0,0 +1,609 @@ +/* + * 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. + */ + +#include <aidl/android/se/omapi/BnSecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementChannel.h> +#include <aidl/android/se/omapi/ISecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementReader.h> +#include <aidl/android/se/omapi/ISecureElementService.h> +#include <aidl/android/se/omapi/ISecureElementSession.h> + +#include <VtsCoreUtil.h> +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <binder/IServiceManager.h> +#include <cutils/properties.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> +#include <utils/String16.h> + +using namespace std; +using namespace ::testing; +using namespace android; + +int main(int argc, char** argv) { + InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + return status; +} + +namespace { + +class OMAPISEServiceHalTest : public TestWithParam<std::string> { + protected: + class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {}; + + void testSelectableAid( + std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader, + std::vector<uint8_t> aid, std::vector<uint8_t>& selectResponse) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + } + + void testNonSelectableAid( + std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader, + std::vector<uint8_t> aid) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + LOG(ERROR) << res.getMessage(); + ASSERT_FALSE(res.isOk()) << "expected to fail to open channel for this test"; + } + + /** + * Verifies TLV data + * + * @return true if the data is tlv formatted, false otherwise + */ + bool verifyBerTlvData(std::vector<uint8_t> tlv) { + if (tlv.size() == 0) { + LOG(ERROR) << "Invalid tlv, null"; + return false; + } + int i = 0; + if ((tlv[i++] & 0x1F) == 0x1F) { + // extra byte for TAG field + i++; + } + + int len = tlv[i++] & 0xFF; + if (len > 127) { + // more than 1 byte for length + int bytesLength = len - 128; + len = 0; + for (int j = bytesLength; j > 0; j--) { + len += (len << 8) + (tlv[i++] & 0xFF); + } + } + // Additional 2 bytes for the SW + return (tlv.size() == (i + len + 2)); + } + + void internalTransmitApdu( + std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader, + std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + std::vector<uint8_t> selectResponse = {}; + + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + + res = channel->transmit(apdu, &transmitResponse); + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() + << " Message: " << res.getMessage(); + ASSERT_TRUE(res.isOk()) << "failed to transmit"; + } + + bool supportOMAPIReaders() { + return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())); + } + + void SetUp() override { + LOG(INFO) << "get OMAPI service with name:" << GetParam(); + ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str())); + mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder); + ASSERT_TRUE(mOmapiSeService); + + std::vector<std::string> readers = {}; + + if (omapiSecureService() != NULL) { + auto status = omapiSecureService()->getReaders(&readers); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + for (auto readerName : readers) { + // Filter eSE readers only + if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) { + std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader; + status = omapiSecureService()->getReader(readerName, &reader); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + mVSReaders[readerName] = reader; + } + } + } + } + + void TearDown() override { + if (mOmapiSeService != nullptr) { + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + reader->closeSessions(); + } + } + } + } + + bool isDebuggableBuild() { + char value[PROPERTY_VALUE_MAX] = {0}; + property_get("ro.system.build.type", value, ""); + if (strcmp(value, "userdebug") == 0) { + return true; + } + if (strcmp(value, "eng") == 0) { + return true; + } + return false; + } + + std::shared_ptr<aidl::android::se::omapi::ISecureElementService> omapiSecureService() { + return mOmapiSeService; + } + + static inline std::string const ESE_READER_PREFIX = "eSE"; + static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese"; + + std::vector<uint8_t> SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31}; + std::vector<uint8_t> LONG_SELECT_RESPONSE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, + 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, + 0x43, 0x54, 0x53, 0x32}; + std::vector<uint8_t> NON_SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0xFF}; + + std::vector<std::vector<uint8_t>> ILLEGAL_COMMANDS_TRANSMIT = { + {0x00, 0x70, 0x00, 0x00}, + {0x00, 0x70, 0x80, 0x00}, + {0x00, 0xA4, 0x04, 0x04, 0x10, 0x4A, 0x53, 0x52, 0x31, 0x37, 0x37, + 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x30}}; + + /* OMAPI APDU Test case 1 and 3 */ + std::vector<std::vector<uint8_t>> NO_DATA_APDU = {{0x00, 0x06, 0x00, 0x00}, + {0x80, 0x06, 0x00, 0x00}, + {0xA0, 0x06, 0x00, 0x00}, + {0x94, 0x06, 0x00, 0x00}, + {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}}; + + /* OMAPI APDU Test case 2 and 4 */ + std::vector<std::vector<uint8_t>> DATA_APDU = {{0x00, 0x08, 0x00, 0x00, 0x00}, + {0x80, 0x08, 0x00, 0x00, 0x00}, + {0xA0, 0x08, 0x00, 0x00, 0x00}, + {0x94, 0x08, 0x00, 0x00, 0x00}, + {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}}; + + /* Case 2 APDU command expects the P2 received in the SELECT command as 1-byte outgoing data */ + std::vector<uint8_t> CHECK_SELECT_P2_APDU = {0x00, 0xF4, 0x00, 0x00, 0x00}; + + /* OMAPI APDU Test case 1 and 3 */ + std::vector<std::vector<uint8_t>> SW_62xx_NO_DATA_APDU = {{0x00, 0xF3, 0x00, 0x06}, + {0x00, 0xF3, 0x00, 0x0A, 0x01, 0xAA}}; + + /* OMAPI APDU Test case 2 and 4 */ + std::vector<uint8_t> SW_62xx_DATA_APDU = {0x00, 0xF3, 0x00, 0x08, 0x00}; + std::vector<uint8_t> SW_62xx_VALIDATE_DATA_APDU = {0x00, 0xF3, 0x00, 0x0C, 0x01, 0xAA, 0x00}; + std::vector<std::vector<uint8_t>> SW_62xx = { + {0x62, 0x00}, {0x62, 0x81}, {0x62, 0x82}, {0x62, 0x83}, {0x62, 0x85}, {0x62, 0xF1}, + {0x62, 0xF2}, {0x63, 0xF1}, {0x63, 0xF2}, {0x63, 0xC2}, {0x62, 0x02}, {0x62, 0x80}, + {0x62, 0x84}, {0x62, 0x86}, {0x63, 0x00}, {0x63, 0x81}}; + + std::vector<std::vector<uint8_t>> SEGMENTED_RESP_APDU = { + // Get response Case2 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xC2, 0x08, 0x00, 0x00}, + // Get response Case4 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xC4, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00}, + // Get response Case2 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xC6, 0x08, 0x00, 0x00}, + // Get response Case4 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xC8, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00}, + // Test device buffer capacity 7FFF data + {0x00, 0xC2, 0x7F, 0xFF, 0x00}, + // Get response 6CFF+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xCF, 0x08, 0x00, 0x00}, + // Get response with another CLA with answer length (P1P2) of 0x0800, 2048 bytes + {0x94, 0xC2, 0x08, 0x00, 0x00}}; + long SERVICE_CONNECTION_TIME_OUT = 3000; + + std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService; + + std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> + mVSReaders = {}; +}; + +/** Tests getReaders API */ +TEST_P(OMAPISEServiceHalTest, TestGetReaders) { + std::vector<std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> eseReaders = + {}; + + for (const auto& [name, reader] : mVSReaders) { + bool status = false; + LOG(INFO) << "Name of the reader: " << name; + + if (reader) { + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + } + ASSERT_TRUE(status); + + if (name.find(ESE_READER_PREFIX) == std::string::npos) { + LOG(ERROR) << "Incorrect Reader name"; + FAIL(); + } + + if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) { + eseReaders.push_back(reader); + } else { + LOG(INFO) << "Reader not supported: " << name; + FAIL(); + } + } + + if (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())) { + ASSERT_GE(eseReaders.size(), 1); + } else { + ASSERT_TRUE(eseReaders.size() == 0); + } +} + +/** Tests OpenBasicChannel API when aid is null */ +TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNullAid) { + ASSERT_TRUE(supportOMAPIReaders() == true); + std::vector<uint8_t> aid = {}; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + bool result = false; + + auto status = reader->openSession(&session); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + if (!session) { + LOG(ERROR) << "Could not open session"; + FAIL(); + } + + status = session->openBasicChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + if (channel != nullptr) { + status = channel->isBasicChannel(&result); + ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened"; + } + } + } +} + +/** Tests OpenBasicChannel API when aid is provided */ +TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNonNullAid) { + ASSERT_TRUE(supportOMAPIReaders() == true); + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + bool result = false; + + auto status = reader->openSession(&session); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + if (!session) { + LOG(ERROR) << "Could not open session"; + FAIL(); + } + + status = session->openBasicChannel(SELECTABLE_AID, 0x00, seListener, &channel); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + if (channel != nullptr) { + status = channel->isBasicChannel(&result); + ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened"; + } + } + } +} + +/** Tests Select API */ +TEST_P(OMAPISEServiceHalTest, TestSelectableAid) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::vector<uint8_t> selectResponse = {}; + testSelectableAid(reader, SELECTABLE_AID, selectResponse); + } + } +} + +/** Tests Select API */ +TEST_P(OMAPISEServiceHalTest, TestLongSelectResponse) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::vector<uint8_t> selectResponse = {}; + testSelectableAid(reader, LONG_SELECT_RESPONSE_AID, selectResponse); + ASSERT_TRUE(verifyBerTlvData(selectResponse)) << "Select Response is not complete"; + } + } +} + +/** Test to fail open channel with wrong aid */ +TEST_P(OMAPISEServiceHalTest, TestWrongAid) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + testNonSelectableAid(reader, NON_SELECTABLE_AID); + } + } +} + +/** Tests with invalid cmds in Transmit */ +TEST_P(OMAPISEServiceHalTest, TestSecurityExceptionInTransmit) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + std::vector<uint8_t> selectResponse = {}; + + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + + for (auto cmd : ILLEGAL_COMMANDS_TRANSMIT) { + std::vector<uint8_t> response = {}; + res = channel->transmit(cmd, &response); + ASSERT_EQ(res.getExceptionCode(), EX_SECURITY); + ASSERT_FALSE(res.isOk()) << "expected failed status for this test"; + } + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + } + } +} + +/** + * Tests Transmit API for all readers. + * + * Checks the return status and verifies the size of the + * response. + */ +TEST_P(OMAPISEServiceHalTest, TestTransmitApdu) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + for (auto apdu : NO_DATA_APDU) { + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + ASSERT_GE(response.size(), 2); + ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90)); + } + + for (auto apdu : DATA_APDU) { + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + /* 256 byte data and 2 bytes of status word */ + ASSERT_GE(response.size(), 258); + ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90)); + } + } + } +} + +/** + * Tests if underlying implementations returns the correct Status Word + * + * TO verify that : + * - the device does not modify the APDU sent to the Secure Element + * - the warning code is properly received by the application layer as SW answer + * - the verify that the application layer can fetch the additionnal data (when present) + */ +TEST_P(OMAPISEServiceHalTest, testStatusWordTransmit) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + for (auto apdu : SW_62xx_NO_DATA_APDU) { + for (uint8_t i = 0x00; i < SW_62xx.size(); i++) { + apdu[2] = i + 1; + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + std::vector<uint8_t> SW = SW_62xx[i]; + ASSERT_GE(response.size(), 2); + ASSERT_EQ(response[response.size() - 1], SW[1]); + ASSERT_EQ(response[response.size() - 2], SW[0]); + } + } + + for (uint8_t i = 0x00; i < SW_62xx.size(); i++) { + std::vector<uint8_t> apdu = SW_62xx_DATA_APDU; + apdu[2] = i + 1; + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + std::vector<uint8_t> SW = SW_62xx[i]; + ASSERT_GE(response.size(), 3); + ASSERT_EQ(response[response.size() - 1], SW[1]); + ASSERT_EQ(response[response.size() - 2], SW[0]); + } + + for (uint8_t i = 0x00; i < SW_62xx.size(); i++) { + std::vector<uint8_t> apdu = SW_62xx_VALIDATE_DATA_APDU; + apdu[2] = i + 1; + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + ASSERT_GE(response.size(), apdu.size() + 2); + std::vector<uint8_t> responseSubstring((response.begin() + 0), + (response.begin() + apdu.size())); + // We should not care about which channel number is actually assigned. + responseSubstring[0] = apdu[0]; + ASSERT_TRUE((responseSubstring == apdu)); + std::vector<uint8_t> SW = SW_62xx[i]; + ASSERT_EQ(response[response.size() - 1], SW[1]); + ASSERT_EQ(response[response.size() - 2], SW[0]); + } + } + } +} + +/** Test if the responses are segmented by the underlying implementation */ +TEST_P(OMAPISEServiceHalTest, TestSegmentedResponseTransmit) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + for (auto apdu : SEGMENTED_RESP_APDU) { + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + int expectedLength = (0x00 << 24) | (0x00 << 16) | (apdu[2] << 8) | apdu[3]; + ASSERT_EQ(response.size(), (expectedLength + 2)); + ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90)); + ASSERT_EQ((response[response.size() - 3] & 0xFF), (0xFF)); + } + } + } +} + +/** + * Tests the P2 value of the select command. + * + * Verifies that the default P2 value (0x00) is not modified by the underlying implementation. + */ +TEST_P(OMAPISEServiceHalTest, TestP2Value) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, CHECK_SELECT_P2_APDU, response); + ASSERT_GE(response.size(), 3); + ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90)); + ASSERT_EQ((response[response.size() - 3] & 0xFF), (0x00)); + } + } +} + +INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEServiceHalTest, + testing::ValuesIn(::android::getAidlHalInstanceNames( + aidl::android::se::omapi::ISecureElementService::descriptor)), + android::hardware::PrintInstanceNameToString); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEServiceHalTest); + +} // namespace diff --git a/omapi/java/Android.bp b/omapi/java/Android.bp new file mode 100644 index 000000000000..8d38da048d9b --- /dev/null +++ b/omapi/java/Android.bp @@ -0,0 +1,17 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "framework-omapi-sources", + srcs: [ + "**/*.java", + "**/*.aidl", + ], + visibility: ["//frameworks/base"], +} diff --git a/core/java/android/se/OWNERS b/omapi/java/android/se/OWNERS index 5682fd3281f4..5682fd3281f4 100644 --- a/core/java/android/se/OWNERS +++ b/omapi/java/android/se/OWNERS diff --git a/core/java/android/se/omapi/Channel.java b/omapi/java/android/se/omapi/Channel.java index 90ce11ae0313..90ce11ae0313 100644 --- a/core/java/android/se/omapi/Channel.java +++ b/omapi/java/android/se/omapi/Channel.java diff --git a/core/java/android/se/omapi/OWNERS b/omapi/java/android/se/omapi/OWNERS index 5682fd3281f4..5682fd3281f4 100644 --- a/core/java/android/se/omapi/OWNERS +++ b/omapi/java/android/se/omapi/OWNERS diff --git a/core/java/android/se/omapi/Reader.java b/omapi/java/android/se/omapi/Reader.java index 90c934d189fa..3c2135d9bc9d 100644 --- a/core/java/android/se/omapi/Reader.java +++ b/omapi/java/android/se/omapi/Reader.java @@ -170,7 +170,9 @@ public final class Reader { try { closeSessions(); return mReader.reset(); - } catch (RemoteException ignore) {return false;} + } catch (RemoteException ignore) { + return false; + } } } } diff --git a/core/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java index 333af91ac872..f42ca364b6d9 100644 --- a/core/java/android/se/omapi/SEService.java +++ b/omapi/java/android/se/omapi/SEService.java @@ -230,20 +230,20 @@ public final class SEService { * is not exist. * @return A Reader object for this uicc slot. */ - public @NonNull Reader getUiccReader(int slotNumber) { - if (slotNumber < 1) { - throw new IllegalArgumentException("slotNumber should be larger than 0"); - } - loadReaders(); + public @NonNull Reader getUiccReader(int slotNumber) { + if (slotNumber < 1) { + throw new IllegalArgumentException("slotNumber should be larger than 0"); + } + loadReaders(); - String readerName = UICC_TERMINAL + slotNumber; - Reader reader = mReaders.get(readerName); + String readerName = UICC_TERMINAL + slotNumber; + Reader reader = mReaders.get(readerName); - if (reader == null) { + if (reader == null) { throw new IllegalArgumentException("Reader:" + readerName + " doesn't exist"); - } + } - return reader; + return reader; } /** diff --git a/core/java/android/se/omapi/Session.java b/omapi/java/android/se/omapi/Session.java index d5f8c82bf47e..d5f8c82bf47e 100644 --- a/core/java/android/se/omapi/Session.java +++ b/omapi/java/android/se/omapi/Session.java diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS index 0d23f053499c..b9de5a3919f5 100644 --- a/packages/CarrierDefaultApp/OWNERS +++ b/packages/CarrierDefaultApp/OWNERS @@ -2,17 +2,16 @@ set noparent tgunn@google.com breadley@google.com rgreenwalt@google.com -amitmahajan@google.com fionaxu@google.com jackyu@google.com jminjie@google.com satk@google.com shuoq@google.com -nazaninb@google.com sarahchin@google.com xiaotonj@google.com huiwang@google.com jayachandranc@google.com chinmayd@google.com amruthr@google.com +sasindran@google.com diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index c1a0a9a92cc2..b4cafd8548f4 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -93,9 +93,9 @@ public class CompanionDeviceActivity extends Activity { final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0); setTitle(Html.fromHtml(getString( R.string.confirmation_title, - getCallingAppName(), - profileName, - selectedDevice.getDisplayName()), 0)); + Html.escapeHtml(getCallingAppName()), + Html.escapeHtml(selectedDevice.getDisplayName())), 0)); + mPairButton = findViewById(R.id.button_pair); mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice)); getService().mSelectedDevice = selectedDevice; @@ -108,8 +108,8 @@ public class CompanionDeviceActivity extends Activity { mPairButton = findViewById(R.id.button_pair); mPairButton.setVisibility(View.GONE); setTitle(Html.fromHtml(getString(R.string.chooser_title, - profileName, - getCallingAppName()), 0)); + Html.escapeHtml(profileName), + Html.escapeHtml(getCallingAppName())), 0)); mDeviceListView = findViewById(R.id.device_list); mDevicesAdapter = new DevicesAdapter(); mDeviceListView.setAdapter(mDevicesAdapter); diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 801b490406cc..a3eb0eccad9d 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -251,7 +251,7 @@ public class ExternalStorageProvider extends FileSystemProvider { if (volume.getType() == VolumeInfo.TYPE_PUBLIC) { root.flags |= Root.FLAG_HAS_SETTINGS; } - if (volume.isVisibleForRead(userId)) { + if (volume.isVisibleForUser(userId)) { root.visiblePath = volume.getPathForUser(userId); } else { root.visiblePath = null; diff --git a/packages/Nsd/OWNERS b/packages/Nsd/OWNERS new file mode 100644 index 000000000000..4862377852ac --- /dev/null +++ b/packages/Nsd/OWNERS @@ -0,0 +1 @@ +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
\ No newline at end of file diff --git a/packages/Nsd/framework/Android.bp b/packages/Nsd/framework/Android.bp new file mode 100644 index 000000000000..2363a9f8d4a3 --- /dev/null +++ b/packages/Nsd/framework/Android.bp @@ -0,0 +1,54 @@ +// +// 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 { + // See: http://go/android-license-faq + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "framework-connectivity-nsd-internal-sources", + srcs: [ + "src/**/*.java", + "src/**/*.aidl", + ], + path: "src", + visibility: [ + "//visibility:private", + ], +} + +filegroup { + name: "framework-connectivity-nsd-aidl-export-sources", + srcs: [ + "aidl-export/**/*.aidl", + ], + path: "aidl-export", + visibility: [ + "//visibility:private", + ], +} + +filegroup { + name: "framework-connectivity-nsd-sources", + srcs: [ + ":framework-connectivity-nsd-internal-sources", + ":framework-connectivity-nsd-aidl-export-sources", + ], + visibility: [ + "//frameworks/base", + ], +} diff --git a/packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl b/packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl new file mode 100644 index 000000000000..657bdd1e8706 --- /dev/null +++ b/packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.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.net.nsd; + +@JavaOnlyStableParcelable parcelable NsdServiceInfo;
\ No newline at end of file diff --git a/core/java/android/net/nsd/INsdManager.aidl b/packages/Nsd/framework/src/android/net/nsd/INsdManager.aidl index e9e8935a19b2..89e9cdbd4445 100644 --- a/core/java/android/net/nsd/INsdManager.aidl +++ b/packages/Nsd/framework/src/android/net/nsd/INsdManager.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2012, The Android Open Source Project + * 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. @@ -16,16 +16,15 @@ package android.net.nsd; +import android.net.nsd.INsdManagerCallback; +import android.net.nsd.INsdServiceConnector; import android.os.Messenger; /** - * Interface that NsdService implements + * Interface that NsdService implements to connect NsdManager clients. * * {@hide} */ -interface INsdManager -{ - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - Messenger getMessenger(); - void setEnabled(boolean enable); +interface INsdManager { + INsdServiceConnector connect(INsdManagerCallback cb); } diff --git a/packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl b/packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl new file mode 100644 index 000000000000..1a262ec0e9dd --- /dev/null +++ b/packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.nsd; + +import android.os.Messenger; +import android.net.nsd.NsdServiceInfo; + +/** + * Callbacks from NsdService to NsdManager + * @hide + */ +oneway interface INsdManagerCallback { + void onDiscoverServicesStarted(int listenerKey, in NsdServiceInfo info); + void onDiscoverServicesFailed(int listenerKey, int error); + void onServiceFound(int listenerKey, in NsdServiceInfo info); + void onServiceLost(int listenerKey, in NsdServiceInfo info); + void onStopDiscoveryFailed(int listenerKey, int error); + void onStopDiscoverySucceeded(int listenerKey); + void onRegisterServiceFailed(int listenerKey, int error); + void onRegisterServiceSucceeded(int listenerKey, in NsdServiceInfo info); + void onUnregisterServiceFailed(int listenerKey, int error); + void onUnregisterServiceSucceeded(int listenerKey); + void onResolveServiceFailed(int listenerKey, int error); + void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info); +} diff --git a/packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl b/packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl new file mode 100644 index 000000000000..b06ae55b150e --- /dev/null +++ b/packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.nsd; + +import android.net.nsd.INsdManagerCallback; +import android.net.nsd.NsdServiceInfo; +import android.os.Messenger; + +/** + * Interface that NsdService implements for each NsdManager client. + * + * {@hide} + */ +interface INsdServiceConnector { + void registerService(int listenerKey, in NsdServiceInfo serviceInfo); + void unregisterService(int listenerKey); + void discoverServices(int listenerKey, in NsdServiceInfo serviceInfo); + void stopDiscovery(int listenerKey); + void resolveService(int listenerKey, in NsdServiceInfo serviceInfo); + void startDaemon(); +}
\ No newline at end of file diff --git a/core/java/android/net/nsd/NsdManager.java b/packages/Nsd/framework/src/android/net/nsd/NsdManager.java index ae8d0101947d..6c597e26e042 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/packages/Nsd/framework/src/android/net/nsd/NsdManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * 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. @@ -31,17 +31,13 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; -import android.os.Messenger; import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; -import java.util.concurrent.CountDownLatch; - /** * The Network Service Discovery Manager class provides the API to discover services * on a network. As an example, if device A and device B are connected over a Wi-Fi @@ -234,6 +230,11 @@ public final class NsdManager { /** @hide */ public static final int NATIVE_DAEMON_EVENT = BASE + 26; + /** @hide */ + public static final int REGISTER_CLIENT = BASE + 27; + /** @hide */ + public static final int UNREGISTER_CLIENT = BASE + 28; + /** Dns based service discovery protocol */ public static final int PROTOCOL_DNS_SD = 0x0001; @@ -274,7 +275,7 @@ public final class NsdManager { private static final int FIRST_LISTENER_KEY = 1; - private final INsdManager mService; + private final INsdServiceConnector mService; private final Context mContext; private int mListenerKey = FIRST_LISTENER_KEY; @@ -282,9 +283,7 @@ public final class NsdManager { private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); private final Object mMapLock = new Object(); - private final AsyncChannel mAsyncChannel = new AsyncChannel(); - private ServiceHandler mHandler; - private final CountDownLatch mConnected = new CountDownLatch(1); + private final ServiceHandler mHandler; /** * Create a new Nsd instance. Applications use @@ -295,18 +294,108 @@ public final class NsdManager { * is a system private class. */ public NsdManager(Context context, INsdManager service) { - mService = service; mContext = context; - init(); + + HandlerThread t = new HandlerThread("NsdManager"); + t.start(); + mHandler = new ServiceHandler(t.getLooper()); + + try { + mService = service.connect(new NsdCallbackImpl(mHandler)); + } catch (RemoteException e) { + throw new RuntimeException("Failed to connect to NsdService"); + } + + // Only proactively start the daemon if the target SDK < S, otherwise the internal service + // would automatically start/stop the native daemon as needed. + if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) { + try { + mService.startDaemon(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to proactively start daemon"); + // Continue: the daemon can still be started on-demand later + } + } } - /** - * @hide - */ - @VisibleForTesting - public void disconnect() { - mAsyncChannel.disconnect(); - mHandler.getLooper().quitSafely(); + private static class NsdCallbackImpl extends INsdManagerCallback.Stub { + private final Handler mServHandler; + + NsdCallbackImpl(Handler serviceHandler) { + mServHandler = serviceHandler; + } + + private void sendInfo(int message, int listenerKey, NsdServiceInfo info) { + mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey, info)); + } + + private void sendError(int message, int listenerKey, int error) { + mServHandler.sendMessage(mServHandler.obtainMessage(message, error, listenerKey)); + } + + private void sendNoArg(int message, int listenerKey) { + mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey)); + } + + @Override + public void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) { + sendInfo(DISCOVER_SERVICES_STARTED, listenerKey, info); + } + + @Override + public void onDiscoverServicesFailed(int listenerKey, int error) { + sendError(DISCOVER_SERVICES_FAILED, listenerKey, error); + } + + @Override + public void onServiceFound(int listenerKey, NsdServiceInfo info) { + sendInfo(SERVICE_FOUND, listenerKey, info); + } + + @Override + public void onServiceLost(int listenerKey, NsdServiceInfo info) { + sendInfo(SERVICE_LOST, listenerKey, info); + } + + @Override + public void onStopDiscoveryFailed(int listenerKey, int error) { + sendError(STOP_DISCOVERY_FAILED, listenerKey, error); + } + + @Override + public void onStopDiscoverySucceeded(int listenerKey) { + sendNoArg(STOP_DISCOVERY_SUCCEEDED, listenerKey); + } + + @Override + public void onRegisterServiceFailed(int listenerKey, int error) { + sendError(REGISTER_SERVICE_FAILED, listenerKey, error); + } + + @Override + public void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { + sendInfo(REGISTER_SERVICE_SUCCEEDED, listenerKey, info); + } + + @Override + public void onUnregisterServiceFailed(int listenerKey, int error) { + sendError(UNREGISTER_SERVICE_FAILED, listenerKey, error); + } + + @Override + public void onUnregisterServiceSucceeded(int listenerKey) { + sendNoArg(UNREGISTER_SERVICE_SUCCEEDED, listenerKey); + } + + @Override + public void onResolveServiceFailed(int listenerKey, int error) { + sendError(RESOLVE_SERVICE_FAILED, listenerKey, error); + } + + @Override + public void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) { + sendInfo(RESOLVE_SERVICE_SUCCEEDED, listenerKey, info); + } } /** @@ -376,19 +465,6 @@ public final class NsdManager { public void handleMessage(Message message) { final int what = message.what; final int key = message.arg2; - switch (what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); - return; - case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: - mConnected.countDown(); - return; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - Log.e(TAG, "Channel lost"); - return; - default: - break; - } final Object listener; final NsdServiceInfo ns; synchronized (mMapLock) { @@ -504,36 +580,6 @@ public final class NsdManager { } /** - * Initialize AsyncChannel - */ - private void init() { - final Messenger messenger = getMessenger(); - if (messenger == null) { - fatal("Failed to obtain service Messenger"); - } - HandlerThread t = new HandlerThread("NsdManager"); - t.start(); - mHandler = new ServiceHandler(t.getLooper()); - mAsyncChannel.connect(mContext, mHandler, messenger); - try { - mConnected.await(); - } catch (InterruptedException e) { - fatal("Interrupted wait at init"); - } - if (CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) { - return; - } - // Only proactively start the daemon if the target SDK < S, otherwise the internal service - // would automatically start/stop the native daemon as needed. - mAsyncChannel.sendMessage(DAEMON_STARTUP); - } - - private static void fatal(String msg) { - Log.e(TAG, msg); - throw new RuntimeException(msg); - } - - /** * Register a service to be discovered by other services. * * <p> The function call immediately returns after sending a request to register service @@ -556,7 +602,11 @@ public final class NsdManager { checkServiceInfo(serviceInfo); checkProtocol(protocolType); int key = putListener(listener, serviceInfo); - mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo); + try { + mService.registerService(key, serviceInfo); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -574,7 +624,11 @@ public final class NsdManager { */ public void unregisterService(RegistrationListener listener) { int id = getListenerKey(listener); - mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id); + try { + mService.unregisterService(id); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -613,7 +667,11 @@ public final class NsdManager { s.setServiceType(serviceType); int key = putListener(listener, s); - mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s); + try { + mService.discoverServices(key, s); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -634,7 +692,11 @@ public final class NsdManager { */ public void stopServiceDiscovery(DiscoveryListener listener) { int id = getListenerKey(listener); - mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id); + try { + mService.stopDiscovery(id); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -649,29 +711,10 @@ public final class NsdManager { public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { checkServiceInfo(serviceInfo); int key = putListener(listener, serviceInfo); - mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo); - } - - /** Internal use only @hide */ - public void setEnabled(boolean enabled) { - try { - mService.setEnabled(enabled); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get a reference to NsdService handler. This is used to establish - * an AsyncChannel communication with the service - * - * @return Messenger pointing to the NsdService handler - */ - private Messenger getMessenger() { try { - return mService.getMessenger(); + mService.resolveService(key, serviceInfo); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + e.rethrowFromSystemServer(); } } diff --git a/core/java/android/net/nsd/NsdServiceInfo.java b/packages/Nsd/framework/src/android/net/nsd/NsdServiceInfo.java index 0946499f164f..0946499f164f 100644 --- a/core/java/android/net/nsd/NsdServiceInfo.java +++ b/packages/Nsd/framework/src/android/net/nsd/NsdServiceInfo.java diff --git a/packages/Nsd/service/Android.bp b/packages/Nsd/service/Android.bp new file mode 100644 index 000000000000..529f58d130ed --- /dev/null +++ b/packages/Nsd/service/Android.bp @@ -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 { + // See: http://go/android-license-faq + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "services.connectivity-nsd-sources", + srcs: [ + "src/**/*.java", + ], + path: "src", + visibility: [ + "//frameworks/base/services/core", + ], +} diff --git a/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java b/packages/Nsd/service/src/com/android/server/INativeDaemonConnectorCallbacks.java index 0cf9dcde012d..0cf9dcde012d 100644 --- a/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java +++ b/packages/Nsd/service/src/com/android/server/INativeDaemonConnectorCallbacks.java diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java index eac767f7355c..ec8d7798e17e 100644 --- a/services/core/java/com/android/server/NativeDaemonConnector.java +++ b/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java @@ -20,18 +20,15 @@ import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.Build; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.util.LocalLog; -import android.util.Slog; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; -import com.android.server.power.ShutdownThread; -import com.google.android.collect.Lists; import java.io.FileDescriptor; import java.io.IOException; @@ -40,19 +37,19 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.LinkedList; +import java.util.Objects; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.LinkedList; -import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; /** * Generic connector class for interfacing with a native daemon which uses the * {@code libsysutils} FrameworkListener protocol. */ -final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor { +final class NativeDaemonConnector implements Runnable, Handler.Callback { private final static boolean VDBG = false; private final String TAG; @@ -85,13 +82,6 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) { - this(callbacks, socket, responseQueueSize, logTag, maxLogSize, wl, - FgThread.get().getLooper()); - } - - NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket, - int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl, - Looper looper) { mCallbacks = callbacks; mSocket = socket; mResponseQueue = new ResponseQueue(responseQueueSize); @@ -99,15 +89,17 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo if (mWakeLock != null) { mWakeLock.setReferenceCounted(true); } - mLooper = looper; mSequenceNumber = new AtomicInteger(0); TAG = logTag != null ? logTag : "NativeDaemonConnector"; mLocalLog = new LocalLog(maxLogSize); + final HandlerThread thread = new HandlerThread(TAG); + thread.start(); + mLooper = thread.getLooper(); } /** * Enable Set debugging mode, which causes messages to also be written to both - * {@link Slog} in addition to internal log. + * {@link Log} in addition to internal log. */ public void setDebug(boolean debug) { mDebug = debug; @@ -126,7 +118,9 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo * calls while holding a lock on the given object. */ public void setWarnIfHeld(Object warnIfHeld) { - Preconditions.checkState(mWarnIfHeld == null); + if (mWarnIfHeld != null) { + throw new IllegalStateException("warnIfHeld is already set."); + } mWarnIfHeld = Objects.requireNonNull(warnIfHeld); } @@ -135,23 +129,15 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo mCallbackHandler = new Handler(mLooper, this); while (true) { - if (isShuttingDown()) break; try { listenToSocket(); } catch (Exception e) { loge("Error in NativeDaemonConnector: " + e); - if (isShuttingDown()) break; SystemClock.sleep(5000); } } } - private static boolean isShuttingDown() { - String shutdownAct = SystemProperties.get( - ShutdownThread.SHUTDOWN_ACTION_PROPERTY, ""); - return shutdownAct != null && shutdownAct.length() > 0; - } - @Override public boolean handleMessage(Message msg) { final String event = (String) msg.obj; @@ -183,7 +169,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo // In order to ensure that unprivileged apps aren't able to impersonate native daemons on // production devices, even if said native daemons ill-advisedly pick a socket name that // starts with __test__, only allow this on debug builds. - if (mSocket.startsWith("__test__") && Build.IS_DEBUGGABLE) { + if (mSocket.startsWith("__test__") && Build.isDebuggable()) { return new LocalSocketAddress(mSocket); } else { return new LocalSocketAddress(mSocket, LocalSocketAddress.Namespace.RESERVED); @@ -375,7 +361,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo try { latch.await(); } catch (InterruptedException e) { - Slog.wtf(TAG, "Interrupted while waiting for unsolicited response handling", e); + Log.wtf(TAG, "Interrupted while waiting for unsolicited response handling", e); } } @@ -462,13 +448,13 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args) throws NativeDaemonConnectorException { if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) { - Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x" + Log.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x" + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable()); } final long startTime = SystemClock.elapsedRealtime(); - final ArrayList<NativeDaemonEvent> events = Lists.newArrayList(); + final ArrayList<NativeDaemonEvent> events = new ArrayList<>(); final StringBuilder rawBuilder = new StringBuilder(); final StringBuilder logBuilder = new StringBuilder(); @@ -571,7 +557,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo */ public static class Command { private String mCmd; - private ArrayList<Object> mArguments = Lists.newArrayList(); + private ArrayList<Object> mArguments = new ArrayList<>(); public Command(String cmd, Object... args) { mCmd = cmd; @@ -586,11 +572,6 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo } } - /** {@inheritDoc} */ - public void monitor() { - synchronized (mDaemonLock) { } - } - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mLocalLog.dump(fd, pw, args); pw.println(); @@ -598,12 +579,12 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo } private void log(String logstring) { - if (mDebug) Slog.d(TAG, logstring); + if (mDebug) Log.d(TAG, logstring); mLocalLog.log(logstring); } private void loge(String logstring) { - Slog.e(TAG, logstring); + Log.e(TAG, logstring); mLocalLog.log(logstring); } @@ -659,12 +640,12 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo if (found == null) { // didn't find it - make sure our queue isn't too big before adding while (mPendingCmds.size() >= mMaxCount) { - Slog.e("NativeDaemonConnector.ResponseQueue", + Log.e("NativeDaemonConnector.ResponseQueue", "more buffered than allowed: " + mPendingCmds.size() + " >= " + mMaxCount); // let any waiter timeout waiting for this PendingCmd pendingCmd = mPendingCmds.remove(); - Slog.e("NativeDaemonConnector.ResponseQueue", + Log.e("NativeDaemonConnector.ResponseQueue", "Removing request: " + pendingCmd.logCmd + " (" + pendingCmd.cmdNum + ")"); } @@ -706,7 +687,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS); } catch (InterruptedException e) {} if (result == null) { - Slog.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response"); + Log.e("NativeDaemonConnector.ResponseQueue", "Timeout waiting for response"); } return result; } diff --git a/services/core/java/com/android/server/NativeDaemonConnectorException.java b/packages/Nsd/service/src/com/android/server/NativeDaemonConnectorException.java index 4d8881c68324..4d8881c68324 100644 --- a/services/core/java/com/android/server/NativeDaemonConnectorException.java +++ b/packages/Nsd/service/src/com/android/server/NativeDaemonConnectorException.java diff --git a/services/core/java/com/android/server/NativeDaemonEvent.java b/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java index e6feda3dad22..568369434629 100644 --- a/services/core/java/com/android/server/NativeDaemonEvent.java +++ b/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java @@ -16,8 +16,7 @@ package com.android.server; -import android.util.Slog; -import com.google.android.collect.Lists; +import android.util.Log; import java.io.FileDescriptor; import java.util.ArrayList; @@ -179,7 +178,7 @@ public class NativeDaemonEvent { * {@link #getMessage()} for any events matching the requested code. */ public static String[] filterMessageList(NativeDaemonEvent[] events, int matchCode) { - final ArrayList<String> result = Lists.newArrayList(); + final ArrayList<String> result = new ArrayList<>(); for (NativeDaemonEvent event : events) { if (event.getCode() == matchCode) { result.add(event.getMessage()); @@ -212,7 +211,7 @@ public class NativeDaemonEvent { int wordEnd = -1; boolean quoted = false; - if (DEBUG_ROUTINE) Slog.e(LOGTAG, "parsing '" + rawEvent + "'"); + if (DEBUG_ROUTINE) Log.e(LOGTAG, "parsing '" + rawEvent + "'"); if (rawEvent.charAt(current) == '\"') { quoted = true; current++; @@ -240,14 +239,14 @@ public class NativeDaemonEvent { word = word.replace("\\\\", "\\"); word = word.replace("\\\"", "\""); - if (DEBUG_ROUTINE) Slog.e(LOGTAG, "found '" + word + "'"); + if (DEBUG_ROUTINE) Log.e(LOGTAG, "found '" + word + "'"); parsed.add(word); // find the beginning of the next word - either of these options int nextSpace = rawEvent.indexOf(' ', current); int nextQuote = rawEvent.indexOf(" \"", current); if (DEBUG_ROUTINE) { - Slog.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote); + Log.e(LOGTAG, "nextSpace=" + nextSpace + ", nextQuote=" + nextQuote); } if (nextQuote > -1 && nextQuote <= nextSpace) { quoted = true; @@ -259,8 +258,8 @@ public class NativeDaemonEvent { } } // else we just start the next word after the current and read til the end if (DEBUG_ROUTINE) { - Slog.e(LOGTAG, "next loop - current=" + current + - ", length=" + length + ", quoted=" + quoted); + Log.e(LOGTAG, "next loop - current=" + current + + ", length=" + length + ", quoted=" + quoted); } } return parsed.toArray(new String[parsed.size()]); diff --git a/services/core/java/com/android/server/NativeDaemonTimeoutException.java b/packages/Nsd/service/src/com/android/server/NativeDaemonTimeoutException.java index 658f7d6264eb..658f7d6264eb 100644 --- a/services/core/java/com/android/server/NativeDaemonTimeoutException.java +++ b/packages/Nsd/service/src/com/android/server/NativeDaemonTimeoutException.java diff --git a/services/core/java/com/android/server/NsdService.java b/packages/Nsd/service/src/com/android/server/NsdService.java index c9608a55170e..497107dbf989 100644 --- a/services/core/java/com/android/server/NsdService.java +++ b/packages/Nsd/service/src/com/android/server/NsdService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * 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. @@ -16,29 +16,27 @@ package com.android.server; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.database.ContentObserver; -import android.net.NetworkStack; -import android.net.Uri; +import android.content.pm.PackageManager; import android.net.nsd.INsdManager; +import android.net.nsd.INsdManagerCallback; +import android.net.nsd.INsdServiceConnector; import android.net.nsd.NsdManager; import android.net.nsd.NsdServiceInfo; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.Message; -import android.os.Messenger; +import android.os.RemoteException; import android.os.UserHandle; -import android.provider.Settings; import android.util.Base64; -import android.util.Slog; +import android.util.Log; +import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.AsyncChannel; -import com.android.internal.util.DumpUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.net.module.util.DnsSdTxtRecord; @@ -64,7 +62,6 @@ public class NsdService extends INsdManager.Stub { private static final long CLEANUP_DELAY_MS = 10000; private final Context mContext; - private final NsdSettings mNsdSettings; private final NsdStateMachine mNsdStateMachine; private final DaemonConnection mDaemon; private final NativeCallbackReceiver mDaemonCallback; @@ -72,12 +69,11 @@ public class NsdService extends INsdManager.Stub { /** * Clients receiving asynchronous messages */ - private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>(); + private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>(); /* A map from unique id to client info */ private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>(); - private final AsyncChannel mReplyChannel = new AsyncChannel(); private final long mCleanupDelayMs; private static final int INVALID_ID = 0; @@ -120,94 +116,79 @@ public class NsdService extends INsdManager.Stub { this.removeMessages(NsdManager.DAEMON_CLEANUP); } - /** - * Observes the NSD on/off setting, and takes action when changed. - */ - private void registerForNsdSetting() { - final ContentObserver contentObserver = new ContentObserver(this.getHandler()) { - @Override - public void onChange(boolean selfChange) { - notifyEnabled(isNsdEnabled()); - } - }; - - final Uri uri = Settings.Global.getUriFor(Settings.Global.NSD_ON); - mNsdSettings.registerContentObserver(uri, contentObserver); - } - NsdStateMachine(String name, Handler handler) { super(name, handler); addState(mDefaultState); addState(mDisabledState, mDefaultState); addState(mEnabledState, mDefaultState); - State initialState = isNsdEnabled() ? mEnabledState : mDisabledState; + State initialState = mEnabledState; setInitialState(initialState); setLogRecSize(25); - registerForNsdSetting(); } class DefaultState extends State { @Override public boolean processMessage(Message msg) { - ClientInfo cInfo = null; + final ClientInfo cInfo; + final int clientId = msg.arg2; switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - AsyncChannel c = (AsyncChannel) msg.obj; - if (DBG) Slog.d(TAG, "New client listening to asynchronous messages"); - c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); - cInfo = new ClientInfo(c, msg.replyTo); - mClients.put(msg.replyTo, cInfo); - } else { - Slog.e(TAG, "Client connection failure, error=" + msg.arg1); + case NsdManager.REGISTER_CLIENT: + final Pair<NsdServiceConnector, INsdManagerCallback> arg = + (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj; + final INsdManagerCallback cb = arg.second; + try { + cb.asBinder().linkToDeath(arg.first, 0); + cInfo = new ClientInfo(cb); + mClients.put(arg.first, cInfo); + } catch (RemoteException e) { + Log.w(TAG, "Client " + clientId + " has already died"); } break; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - switch (msg.arg1) { - case AsyncChannel.STATUS_SEND_UNSUCCESSFUL: - Slog.e(TAG, "Send failed, client connection lost"); - break; - case AsyncChannel.STATUS_REMOTE_DISCONNECTION: - if (DBG) Slog.d(TAG, "Client disconnected"); - break; - default: - if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); - break; - } - - cInfo = mClients.get(msg.replyTo); + case NsdManager.UNREGISTER_CLIENT: + final NsdServiceConnector connector = (NsdServiceConnector) msg.obj; + cInfo = mClients.remove(connector); if (cInfo != null) { cInfo.expungeAllRequests(); - mClients.remove(msg.replyTo); if (cInfo.isLegacy()) { mLegacyClientCount -= 1; } } maybeScheduleStop(); break; - case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: - AsyncChannel ac = new AsyncChannel(); - ac.connect(mContext, getHandler(), msg.replyTo); - break; case NsdManager.DISCOVER_SERVICES: - replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onDiscoverServicesFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.STOP_DISCOVERY: - replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onStopDiscoveryFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.REGISTER_SERVICE: - replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onRegisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.UNREGISTER_SERVICE: - replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onUnregisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.RESOLVE_SERVICE: - replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.DAEMON_CLEANUP: mDaemon.maybeStop(); @@ -215,7 +196,7 @@ public class NsdService extends INsdManager.Stub { // This event should be only sent by the legacy (target SDK < S) clients. // Mark the sending client as legacy. case NsdManager.DAEMON_STARTUP: - cInfo = mClients.get(msg.replyTo); + cInfo = getClientInfoForReply(msg); if (cInfo != null) { cancelStop(); cInfo.setLegacy(); @@ -225,11 +206,16 @@ public class NsdService extends INsdManager.Stub { break; case NsdManager.NATIVE_DAEMON_EVENT: default: - Slog.e(TAG, "Unhandled " + msg); + Log.e(TAG, "Unhandled " + msg); return NOT_HANDLED; } return HANDLED; } + + private ClientInfo getClientInfoForReply(Message msg) { + final ListenerArgs args = (ListenerArgs) msg.obj; + return mClients.get(args.connector); + } } class DisabledState extends State { @@ -266,7 +252,7 @@ public class NsdService extends INsdManager.Stub { private boolean requestLimitReached(ClientInfo clientInfo) { if (clientInfo.mClientIds.size() >= ClientInfo.MAX_LIMIT) { - if (DBG) Slog.d(TAG, "Exceeded max outstanding requests " + clientInfo); + if (DBG) Log.d(TAG, "Exceeded max outstanding requests " + clientInfo); return true; } return false; @@ -289,122 +275,119 @@ public class NsdService extends INsdManager.Stub { @Override public boolean processMessage(Message msg) { - ClientInfo clientInfo; - NsdServiceInfo servInfo; - int id; + final ClientInfo clientInfo; + final int id; + final int clientId = msg.arg2; + final ListenerArgs args; switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - return NOT_HANDLED; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - return NOT_HANDLED; case NsdManager.DISABLE: //TODO: cleanup clients transitionTo(mDisabledState); break; case NsdManager.DISCOVER_SERVICES: - if (DBG) Slog.d(TAG, "Discover services"); - servInfo = (NsdServiceInfo) msg.obj; - clientInfo = mClients.get(msg.replyTo); + if (DBG) Log.d(TAG, "Discover services"); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); if (requestLimitReached(clientInfo)) { - replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.FAILURE_MAX_LIMIT); + clientInfo.onDiscoverServicesFailed( + clientId, NsdManager.FAILURE_MAX_LIMIT); break; } maybeStartDaemon(); id = getUniqueId(); - if (discoverServices(id, servInfo.getServiceType())) { + if (discoverServices(id, args.serviceInfo.getServiceType())) { if (DBG) { - Slog.d(TAG, "Discover " + msg.arg2 + " " + id + - servInfo.getServiceType()); + Log.d(TAG, "Discover " + msg.arg2 + " " + id + + args.serviceInfo.getServiceType()); } - storeRequestMap(msg.arg2, id, clientInfo, msg.what); - replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo); + storeRequestMap(clientId, id, clientInfo, msg.what); + clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo); } else { stopServiceDiscovery(id); - replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, + clientInfo.onDiscoverServicesFailed(clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.STOP_DISCOVERY: - if (DBG) Slog.d(TAG, "Stop service discovery"); - clientInfo = mClients.get(msg.replyTo); + if (DBG) Log.d(TAG, "Stop service discovery"); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); try { - id = clientInfo.mClientIds.get(msg.arg2); + id = clientInfo.mClientIds.get(clientId); } catch (NullPointerException e) { - replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onStopDiscoveryFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; } - removeRequestMap(msg.arg2, id, clientInfo); + removeRequestMap(clientId, id, clientInfo); if (stopServiceDiscovery(id)) { - replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED); + clientInfo.onStopDiscoverySucceeded(clientId); } else { - replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onStopDiscoveryFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.REGISTER_SERVICE: - if (DBG) Slog.d(TAG, "Register service"); - clientInfo = mClients.get(msg.replyTo); + if (DBG) Log.d(TAG, "Register service"); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); if (requestLimitReached(clientInfo)) { - replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.FAILURE_MAX_LIMIT); + clientInfo.onRegisterServiceFailed( + clientId, NsdManager.FAILURE_MAX_LIMIT); break; } maybeStartDaemon(); id = getUniqueId(); - if (registerService(id, (NsdServiceInfo) msg.obj)) { - if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id); - storeRequestMap(msg.arg2, id, clientInfo, msg.what); + if (registerService(id, args.serviceInfo)) { + if (DBG) Log.d(TAG, "Register " + clientId + " " + id); + storeRequestMap(clientId, id, clientInfo, msg.what); // Return success after mDns reports success } else { unregisterService(id); - replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onRegisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.UNREGISTER_SERVICE: - if (DBG) Slog.d(TAG, "unregister service"); - clientInfo = mClients.get(msg.replyTo); - try { - id = clientInfo.mClientIds.get(msg.arg2); - } catch (NullPointerException e) { - replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + if (DBG) Log.d(TAG, "unregister service"); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); + if (clientInfo == null) { + Log.e(TAG, "Unknown connector in unregistration"); break; } - removeRequestMap(msg.arg2, id, clientInfo); + id = clientInfo.mClientIds.get(clientId); + removeRequestMap(clientId, id, clientInfo); if (unregisterService(id)) { - replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED); + clientInfo.onUnregisterServiceSucceeded(clientId); } else { - replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onUnregisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.RESOLVE_SERVICE: - if (DBG) Slog.d(TAG, "Resolve service"); - servInfo = (NsdServiceInfo) msg.obj; - clientInfo = mClients.get(msg.replyTo); - + if (DBG) Log.d(TAG, "Resolve service"); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); if (clientInfo.mResolvedService != null) { - replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_ALREADY_ACTIVE); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_ALREADY_ACTIVE); break; } maybeStartDaemon(); id = getUniqueId(); - if (resolveService(id, servInfo)) { + if (resolveService(id, args.serviceInfo)) { clientInfo.mResolvedService = new NsdServiceInfo(); - storeRequestMap(msg.arg2, id, clientInfo, msg.what); + storeRequestMap(clientId, id, clientInfo, msg.what); } else { - replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.NATIVE_DAEMON_EVENT: @@ -425,7 +408,7 @@ public class NsdService extends INsdManager.Stub { ClientInfo clientInfo = mIdToClientInfoMap.get(id); if (clientInfo == null) { String name = NativeResponseCode.nameOf(code); - Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name)); + Log.e(TAG, String.format("id %d for %s has no client mapping", id, name)); return false; } @@ -436,43 +419,40 @@ public class NsdService extends INsdManager.Stub { // SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY, // and we may get in this situation. String name = NativeResponseCode.nameOf(code); - Slog.d(TAG, String.format( + Log.d(TAG, String.format( "Notification %s for listener id %d that is no longer active", name, id)); return false; } if (DBG) { String name = NativeResponseCode.nameOf(code); - Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw)); + Log.d(TAG, String.format("Native daemon message %s: %s", name, raw)); } switch (code) { case NativeResponseCode.SERVICE_FOUND: /* NNN uniqueId serviceName regType domain */ servInfo = new NsdServiceInfo(cooked[2], cooked[3]); - clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0, - clientId, servInfo); + clientInfo.onServiceFound(clientId, servInfo); break; case NativeResponseCode.SERVICE_LOST: /* NNN uniqueId serviceName regType domain */ servInfo = new NsdServiceInfo(cooked[2], cooked[3]); - clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0, - clientId, servInfo); + clientInfo.onServiceLost(clientId, servInfo); break; case NativeResponseCode.SERVICE_DISCOVERY_FAILED: /* NNN uniqueId errorCode */ - clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onDiscoverServicesFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_REGISTERED: /* NNN regId serviceName regType */ servInfo = new NsdServiceInfo(cooked[2], null); - clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED, - id, clientId, servInfo); + clientInfo.onRegisterServiceSucceeded(clientId, servInfo); break; case NativeResponseCode.SERVICE_REGISTRATION_FAILED: /* NNN regId errorCode */ - clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onRegisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_UPDATED: /* NNN regId */ @@ -490,7 +470,7 @@ public class NsdService extends INsdManager.Stub { ++index; } if (index >= cooked[2].length()) { - Slog.e(TAG, "Invalid service found " + raw); + Log.e(TAG, "Invalid service found " + raw); break; } String name = cooked[2].substring(0, index); @@ -511,8 +491,8 @@ public class NsdService extends INsdManager.Stub { if (getAddrInfo(id2, cooked[3])) { storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE); } else { - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); clientInfo.mResolvedService = null; } break; @@ -521,26 +501,26 @@ public class NsdService extends INsdManager.Stub { stopResolveService(id); removeRequestMap(clientId, id, clientInfo); clientInfo.mResolvedService = null; - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_GET_ADDR_FAILED: /* NNN resolveId errorCode */ stopGetAddrInfo(id); removeRequestMap(clientId, id, clientInfo); clientInfo.mResolvedService = null; - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: /* NNN resolveId hostname ttl addr */ try { clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED, - 0, clientId, clientInfo.mResolvedService); + clientInfo.onResolveServiceSucceeded( + clientId, clientInfo.mResolvedService); } catch (java.net.UnknownHostException e) { - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } stopGetAddrInfo(id); removeRequestMap(clientId, id, clientInfo); @@ -560,13 +540,13 @@ public class NsdService extends INsdManager.Stub { char c = s.charAt(i); if (c == '\\') { if (++i >= s.length()) { - Slog.e(TAG, "Unexpected end of escape sequence in: " + s); + Log.e(TAG, "Unexpected end of escape sequence in: " + s); break; } c = s.charAt(i); if (c != '.' && c != '\\') { if (i + 2 >= s.length()) { - Slog.e(TAG, "Unexpected end of escape sequence in: " + s); + Log.e(TAG, "Unexpected end of escape sequence in: " + s); break; } c = (char) ((c-'0') * 100 + (s.charAt(i+1)-'0') * 10 + (s.charAt(i+2)-'0')); @@ -579,11 +559,9 @@ public class NsdService extends INsdManager.Stub { } @VisibleForTesting - NsdService(Context ctx, NsdSettings settings, Handler handler, - DaemonConnectionSupplier fn, long cleanupDelayMs) { + NsdService(Context ctx, Handler handler, DaemonConnectionSupplier fn, long cleanupDelayMs) { mCleanupDelayMs = cleanupDelayMs; mContext = ctx; - mNsdSettings = settings; mNsdStateMachine = new NsdStateMachine(TAG, handler); mNsdStateMachine.start(); mDaemonCallback = new NativeCallbackReceiver(); @@ -591,29 +569,80 @@ public class NsdService extends INsdManager.Stub { } public static NsdService create(Context context) throws InterruptedException { - NsdSettings settings = NsdSettings.makeDefault(context); HandlerThread thread = new HandlerThread(TAG); thread.start(); Handler handler = new Handler(thread.getLooper()); - NsdService service = new NsdService(context, settings, handler, - DaemonConnection::new, CLEANUP_DELAY_MS); + NsdService service = + new NsdService(context, handler, DaemonConnection::new, CLEANUP_DELAY_MS); service.mDaemonCallback.awaitConnection(); return service; } - public Messenger getMessenger() { + @Override + public INsdServiceConnector connect(INsdManagerCallback cb) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService"); - return new Messenger(mNsdStateMachine.getHandler()); + final INsdServiceConnector connector = new NsdServiceConnector(); + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb))); + return connector; } - public void setEnabled(boolean isEnabled) { - NetworkStack.checkNetworkStackPermission(mContext); - mNsdSettings.putEnabledStatus(isEnabled); - notifyEnabled(isEnabled); + private static class ListenerArgs { + public final NsdServiceConnector connector; + public final NsdServiceInfo serviceInfo; + ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) { + this.connector = connector; + this.serviceInfo = serviceInfo; + } } - private void notifyEnabled(boolean isEnabled) { - mNsdStateMachine.sendMessage(isEnabled ? NsdManager.ENABLE : NsdManager.DISABLE); + private class NsdServiceConnector extends INsdServiceConnector.Stub + implements IBinder.DeathRecipient { + @Override + public void registerService(int listenerKey, NsdServiceInfo serviceInfo) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.REGISTER_SERVICE, 0, listenerKey, + new ListenerArgs(this, serviceInfo))); + } + + @Override + public void unregisterService(int listenerKey) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.UNREGISTER_SERVICE, 0, listenerKey, + new ListenerArgs(this, null))); + } + + @Override + public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.DISCOVER_SERVICES, 0, listenerKey, + new ListenerArgs(this, serviceInfo))); + } + + @Override + public void stopDiscovery(int listenerKey) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null))); + } + + @Override + public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.RESOLVE_SERVICE, 0, listenerKey, + new ListenerArgs(this, serviceInfo))); + } + + @Override + public void startDaemon() { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null))); + } + + @Override + public void binderDied() { + mNsdStateMachine.sendMessage( + mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this)); + } } private void sendNsdStateChangeBroadcast(boolean isEnabled) { @@ -624,14 +653,6 @@ public class NsdService extends INsdManager.Stub { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - private boolean isNsdEnabled() { - boolean ret = mNsdSettings.isEnabled(); - if (DBG) { - Slog.d(TAG, "Network service discovery is " + (ret ? "enabled" : "disabled")); - } - return ret; - } - private int getUniqueId() { if (++mUniqueId == INVALID_ID) return ++mUniqueId; return mUniqueId; @@ -737,12 +758,12 @@ public class NsdService extends INsdManager.Stub { */ public boolean execute(Object... args) { if (DBG) { - Slog.d(TAG, "mdnssd " + Arrays.toString(args)); + Log.d(TAG, "mdnssd " + Arrays.toString(args)); } try { mNativeConnector.execute("mdnssd", args); } catch (NativeDaemonConnectorException e) { - Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e); + Log.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e); return false; } return true; @@ -773,7 +794,7 @@ public class NsdService extends INsdManager.Stub { private boolean registerService(int regId, NsdServiceInfo service) { if (DBG) { - Slog.d(TAG, "registerService: " + regId + " " + service); + Log.d(TAG, "registerService: " + regId + " " + service); } String name = service.getServiceName(); String type = service.getServiceType(); @@ -822,7 +843,12 @@ public class NsdService extends INsdManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump " + TAG + + " due to missing android.permission.DUMP permission"); + return; + } for (ClientInfo client : mClients.values()) { pw.println("Client Info"); @@ -832,43 +858,11 @@ public class NsdService extends INsdManager.Stub { mNsdStateMachine.dump(fd, pw, args); } - /* arg2 on the source message has an id that needs to be retained in replies - * see NsdManager for details */ - private Message obtainMessage(Message srcMsg) { - Message msg = Message.obtain(); - msg.arg2 = srcMsg.arg2; - return msg; - } - - private void replyToMessage(Message msg, int what) { - if (msg.replyTo == null) return; - Message dstMsg = obtainMessage(msg); - dstMsg.what = what; - mReplyChannel.replyToMessage(msg, dstMsg); - } - - private void replyToMessage(Message msg, int what, int arg1) { - if (msg.replyTo == null) return; - Message dstMsg = obtainMessage(msg); - dstMsg.what = what; - dstMsg.arg1 = arg1; - mReplyChannel.replyToMessage(msg, dstMsg); - } - - private void replyToMessage(Message msg, int what, Object obj) { - if (msg.replyTo == null) return; - Message dstMsg = obtainMessage(msg); - dstMsg.what = what; - dstMsg.obj = obj; - mReplyChannel.replyToMessage(msg, dstMsg); - } - /* Information tracked per client */ private class ClientInfo { private static final int MAX_LIMIT = 10; - private final AsyncChannel mChannel; - private final Messenger mMessenger; + private final INsdManagerCallback mCb; /* Remembers a resolved service until getaddrinfo completes */ private NsdServiceInfo mResolvedService; @@ -881,17 +875,14 @@ public class NsdService extends INsdManager.Stub { // The target SDK of this client < Build.VERSION_CODES.S private boolean mIsLegacy = false; - private ClientInfo(AsyncChannel c, Messenger m) { - mChannel = c; - mMessenger = m; - if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m); + private ClientInfo(INsdManagerCallback cb) { + mCb = cb; + if (DBG) Log.d(TAG, "New client"); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("mChannel ").append(mChannel).append("\n"); - sb.append("mMessenger ").append(mMessenger).append("\n"); sb.append("mResolvedService ").append(mResolvedService).append("\n"); sb.append("mIsLegacy ").append(mIsLegacy).append("\n"); for(int i = 0; i< mClientIds.size(); i++) { @@ -920,8 +911,10 @@ public class NsdService extends INsdManager.Stub { clientId = mClientIds.keyAt(i); globalId = mClientIds.valueAt(i); mIdToClientInfoMap.remove(globalId); - if (DBG) Slog.d(TAG, "Terminating client-ID " + clientId + - " global-ID " + globalId + " type " + mClientRequests.get(clientId)); + if (DBG) { + Log.d(TAG, "Terminating client-ID " + clientId + + " global-ID " + globalId + " type " + mClientRequests.get(clientId)); + } switch (mClientRequests.get(clientId)) { case NsdManager.DISCOVER_SERVICES: stopServiceDiscovery(globalId); @@ -949,36 +942,101 @@ public class NsdService extends INsdManager.Stub { } return mClientIds.keyAt(idx); } - } - /** - * Interface which encapsulates dependencies of NsdService that are hard to mock, hard to - * override, or have side effects on global state in unit tests. - */ - @VisibleForTesting - public interface NsdSettings { - boolean isEnabled(); - void putEnabledStatus(boolean isEnabled); - void registerContentObserver(Uri uri, ContentObserver observer); - - static NsdSettings makeDefault(Context context) { - final ContentResolver resolver = context.getContentResolver(); - return new NsdSettings() { - @Override - public boolean isEnabled() { - return Settings.Global.getInt(resolver, Settings.Global.NSD_ON, 1) == 1; - } + void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) { + try { + mCb.onDiscoverServicesStarted(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onDiscoverServicesStarted", e); + } + } - @Override - public void putEnabledStatus(boolean isEnabled) { - Settings.Global.putInt(resolver, Settings.Global.NSD_ON, isEnabled ? 1 : 0); - } + void onDiscoverServicesFailed(int listenerKey, int error) { + try { + mCb.onDiscoverServicesFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onDiscoverServicesFailed", e); + } + } - @Override - public void registerContentObserver(Uri uri, ContentObserver observer) { - resolver.registerContentObserver(uri, false, observer); - } - }; + void onServiceFound(int listenerKey, NsdServiceInfo info) { + try { + mCb.onServiceFound(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onServiceFound(", e); + } + } + + void onServiceLost(int listenerKey, NsdServiceInfo info) { + try { + mCb.onServiceLost(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onServiceLost(", e); + } + } + + void onStopDiscoveryFailed(int listenerKey, int error) { + try { + mCb.onStopDiscoveryFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onStopDiscoveryFailed", e); + } + } + + void onStopDiscoverySucceeded(int listenerKey) { + try { + mCb.onStopDiscoverySucceeded(listenerKey); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onStopDiscoverySucceeded", e); + } + } + + void onRegisterServiceFailed(int listenerKey, int error) { + try { + mCb.onRegisterServiceFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onRegisterServiceFailed", e); + } + } + + void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { + try { + mCb.onRegisterServiceSucceeded(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onRegisterServiceSucceeded", e); + } + } + + void onUnregisterServiceFailed(int listenerKey, int error) { + try { + mCb.onUnregisterServiceFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onUnregisterServiceFailed", e); + } + } + + void onUnregisterServiceSucceeded(int listenerKey) { + try { + mCb.onUnregisterServiceSucceeded(listenerKey); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e); + } + } + + void onResolveServiceFailed(int listenerKey, int error) { + try { + mCb.onResolveServiceFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onResolveServiceFailed", e); + } + } + + void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) { + try { + mCb.onResolveServiceSucceeded(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onResolveServiceSucceeded", e); + } } } } diff --git a/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java b/packages/Nsd/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java index e2253a2151b0..e2253a2151b0 100644 --- a/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java +++ b/packages/Nsd/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java diff --git a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp index 7ff9cedab5f7..b2c82c6fb846 100644 --- a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp +++ b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp @@ -30,11 +30,8 @@ static bool writeAllBytes(const int fd, void* buffer, const size_t byteCount) { char* writeBuffer = static_cast<char*>(buffer); size_t remainingBytes = byteCount; while (remainingBytes > 0) { - ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes); + ssize_t writtenByteCount = TEMP_FAILURE_RETRY(write(fd, writeBuffer, remainingBytes)); if (writtenByteCount == -1) { - if (errno == EINTR) { - continue; - } __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Error writing to buffer: %d", errno); return false; @@ -49,19 +46,17 @@ static bool readAllBytes(const int fd, void* buffer, const size_t byteCount) { char* readBuffer = static_cast<char*>(buffer); size_t remainingBytes = byteCount; while (remainingBytes > 0) { - ssize_t readByteCount = read(fd, readBuffer, remainingBytes); - - remainingBytes -= readByteCount; - readBuffer += readByteCount; - + ssize_t readByteCount = TEMP_FAILURE_RETRY(read(fd, readBuffer, remainingBytes)); if (readByteCount == -1) { - if (errno == EINTR) { - continue; - } __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Error reading from buffer: %d", errno); return false; - } else if (readByteCount == 0 && remainingBytes > 0) { + } + + remainingBytes -= readByteCount; + readBuffer += readByteCount; + + if (readByteCount == 0 && remainingBytes > 0) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "File closed before all bytes were read. %zu/%zu remaining", remainingBytes, byteCount); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 3c7856048860..99e3160fcbe3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -28,13 +28,16 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; +import android.os.Build; import android.os.ParcelUuid; import android.util.Log; +import androidx.annotation.RequiresApi; + import com.android.settingslib.R; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; public class A2dpProfile implements LocalBluetoothProfile { @@ -226,6 +229,10 @@ public class A2dpProfile implements LocalBluetoothProfile { return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED; } + /** + * @return whether high quality audio is enabled or not + */ + @RequiresApi(Build.VERSION_CODES.TIRAMISU) public boolean isHighQualityAudioEnabled(BluetoothDevice device) { BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); if (bluetoothDevice == null) { @@ -271,6 +278,13 @@ public class A2dpProfile implements LocalBluetoothProfile { } } + /** + * Gets the label associated with the codec of a Bluetooth device. + * + * @param device to get codec label from + * @return the label associated with the device codec + */ + @RequiresApi(Build.VERSION_CODES.TIRAMISU) public String getHighQualityAudioOptionLabel(BluetoothDevice device) { BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec; @@ -280,18 +294,18 @@ public class A2dpProfile implements LocalBluetoothProfile { } // We want to get the highest priority codec, since that's the one that will be used with // this device, and see if it is high-quality (ie non-mandatory). - BluetoothCodecConfig[] selectable = null; + List<BluetoothCodecConfig> selectable = null; if (mService.getCodecStatus(device) != null) { selectable = mService.getCodecStatus(device).getCodecsSelectableCapabilities(); // To get the highest priority, we sort in reverse. - Arrays.sort(selectable, + Collections.sort(selectable, (a, b) -> { return b.getCodecPriority() - a.getCodecPriority(); }); } - final BluetoothCodecConfig codecConfig = (selectable == null || selectable.length < 1) - ? null : selectable[0]; + final BluetoothCodecConfig codecConfig = (selectable == null || selectable.size() < 1) + ? null : selectable.get(0); final int codecType = (codecConfig == null || codecConfig.isMandatoryCodec()) ? BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID : codecConfig.getCodecType(); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 58d2185ea9e9..389892ed15e4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -160,10 +160,12 @@ public class BluetoothEventManager { private void registerIntentReceiver(BroadcastReceiver receiver, IntentFilter filter) { if (mUserHandle == null) { // If userHandle has not been provided, simply call registerReceiver. - mContext.registerReceiver(receiver, filter, null, mReceiverHandler); + mContext.registerReceiver(receiver, filter, null, mReceiverHandler, + Context.RECEIVER_EXPORTED); } else { // userHandle was explicitly specified, so need to call multi-user aware API. - mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler); + mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler, + Context.RECEIVER_EXPORTED); } } @@ -305,7 +307,8 @@ public class BluetoothEventManager { CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); if (cachedDevice == null) { cachedDevice = mDeviceManager.addDevice(device); - Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice"); + Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice " + + cachedDevice.getDevice().getAnonymizedAddress()); } else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED && !cachedDevice.getDevice().isConnected()) { // Dispatch device add callback to show bonded but diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 021ba2247e67..ddee433d3997 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -21,7 +21,6 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; @@ -191,7 +190,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) { if (BluetoothUtils.D) { Log.d(TAG, "onProfileStateChanged: profile " + profile + ", device " - + mDevice.getAlias() + ", newProfileState " + newProfileState); + + mDevice.getAnonymizedAddress() + ", newProfileState " + newProfileState); } if (mLocalAdapter.getState() == BluetoothAdapter.STATE_TURNING_OFF) { @@ -741,7 +740,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } if (BluetoothUtils.D) { - Log.d(TAG, "updating profiles for " + mDevice.getAlias()); + Log.d(TAG, "updating profiles for " + mDevice.getAnonymizedAddress()); BluetoothClass bluetoothClass = mDevice.getBluetoothClass(); if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString()); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java index 9afdd43ce73c..f167721f94bf 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java @@ -43,6 +43,9 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import java.util.Arrays; +import java.util.List; + @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowBluetoothAdapter.class}) public class A2dpProfileTest { @@ -179,7 +182,7 @@ public class A2dpProfileTest { BluetoothProfile.STATE_CONNECTED); BluetoothCodecStatus status = mock(BluetoothCodecStatus.class); BluetoothCodecConfig config = mock(BluetoothCodecConfig.class); - BluetoothCodecConfig[] configs = {config}; + List<BluetoothCodecConfig> configs = Arrays.asList(config); when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status); when(status.getCodecsSelectableCapabilities()).thenReturn(configs); @@ -194,7 +197,7 @@ public class A2dpProfileTest { BluetoothProfile.STATE_CONNECTED); BluetoothCodecStatus status = mock(BluetoothCodecStatus.class); BluetoothCodecConfig config = mock(BluetoothCodecConfig.class); - BluetoothCodecConfig[] configs = {config}; + List<BluetoothCodecConfig> configs = Arrays.asList(config); when(mBluetoothA2dp.getCodecStatus(mDevice)).thenReturn(status); when(status.getCodecsSelectableCapabilities()).thenReturn(configs); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java index 5252c6c82754..52d243a14e2f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java @@ -20,9 +20,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.robolectric.shadow.api.Shadow.extract; -import android.net.ConnectivityManager; import android.os.UserManager; -import android.util.SparseBooleanArray; +import android.telephony.TelephonyManager; import org.junit.Before; import org.junit.Test; @@ -35,7 +34,7 @@ import org.robolectric.annotation.Implements; @RunWith(RobolectricTestRunner.class) @Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class, - SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class}) + SimStatusImeiInfoPreferenceControllerTest.ShadowTelephonyManager.class}) public class SimStatusImeiInfoPreferenceControllerTest { private AbstractSimStatusImeiInfoPreferenceController mController; @@ -56,9 +55,9 @@ public class SimStatusImeiInfoPreferenceControllerTest { ShadowUserManager userManager = extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); userManager.setIsAdminUser(true); - ShadowConnectivityManager connectivityManager = - extract(RuntimeEnvironment.application.getSystemService(ConnectivityManager.class)); - connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true); + ShadowTelephonyManager telephonyManager = + extract(RuntimeEnvironment.application.getSystemService(TelephonyManager.class)); + telephonyManager.setDataCapable(true); assertThat(mController.isAvailable()).isTrue(); } @@ -68,9 +67,9 @@ public class SimStatusImeiInfoPreferenceControllerTest { ShadowUserManager userManager = extract(RuntimeEnvironment.application.getSystemService(UserManager.class)); userManager.setIsAdminUser(true); - ShadowConnectivityManager connectivityManager = - extract(RuntimeEnvironment.application.getSystemService(ConnectivityManager.class)); - connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false); + ShadowTelephonyManager telephonyManager = + extract(RuntimeEnvironment.application.getSystemService(TelephonyManager.class)); + telephonyManager.setDataCapable(false); assertThat(mController.isAvailable()).isFalse(); } @@ -99,19 +98,17 @@ public class SimStatusImeiInfoPreferenceControllerTest { } } - @Implements(ConnectivityManager.class) - public static class ShadowConnectivityManager - extends org.robolectric.shadows.ShadowConnectivityManager { - - private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray(); - - private void setNetworkSupported(int networkType, boolean supported) { - mSupportedNetworkTypes.put(networkType, supported); + @Implements(TelephonyManager.class) + public static class ShadowTelephonyManager + extends org.robolectric.shadows.ShadowTelephonyManager { + private boolean mDataCapable = false; + private void setDataCapable(boolean capable) { + mDataCapable = capable; } @Implementation - public boolean isNetworkSupported(int networkType) { - return mSupportedNetworkTypes.get(networkType); + public boolean isDataCapable() { + return mDataCapable; } } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 9cd7083a2a11..c30c742fd9d9 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -98,6 +98,10 @@ public class SettingsBackupAgent extends BackupAgentHelper { private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config"; private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config"; private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings"; + // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a + // fatal crash. Creating a backup with a different key will prevent Android 12 versions from + // restoring this data. + private static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2"; // Versioning of the state file. Increment this version // number any time the set of state items is altered. @@ -253,7 +257,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { deviceSpecificInformation, data); stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] = writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS], - KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data); + KEY_SIM_SPECIFIC_SETTINGS_2, simSpecificSettingsData, data); writeNewChecksums(stateChecksums, newState); } @@ -395,6 +399,9 @@ public class SettingsBackupAgent extends BackupAgentHelper { break; case KEY_SIM_SPECIFIC_SETTINGS: + // Intentional fall through so that sim-specific backups from Android 12 will + // also be restored on newer Android versions. + case KEY_SIM_SPECIFIC_SETTINGS_2: byte[] restoredSimSpecificSettings = new byte[size]; data.readEntityData(restoredSimSpecificSettings, 0, size); restoreSimSpecificSettings(restoredSimSpecificSettings); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 073b4d00653d..6c6b0e8a5076 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1085,14 +1085,17 @@ class SettingsProtoDumpUtil { Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE, GlobalSettingsProto.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE); - final long nitzUpdateToken = p.start(GlobalSettingsProto.NITZ_UPDATE); + final long nitzToken = p.start(GlobalSettingsProto.NITZ); dumpSetting(s, p, Settings.Global.NITZ_UPDATE_DIFF, - GlobalSettingsProto.NitzUpdate.DIFF); + GlobalSettingsProto.Nitz.UPDATE_DIFF); dumpSetting(s, p, Settings.Global.NITZ_UPDATE_SPACING, - GlobalSettingsProto.NitzUpdate.SPACING); - p.end(nitzUpdateToken); + GlobalSettingsProto.Nitz.UPDATE_SPACING); + dumpSetting(s, p, + Settings.Global.NITZ_NETWORK_DISCONNECT_RETENTION, + GlobalSettingsProto.Nitz.NETWORK_DISCONNECT_RETENTION); + p.end(nitzToken); final long notificationToken = p.start(GlobalSettingsProto.NOTIFICATION); dumpSetting(s, p, diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 3297937e3e75..773b0682233f 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -388,6 +388,7 @@ public class SettingsBackupTest { Settings.Global.NETWORK_WATCHLIST_ENABLED, Settings.Global.NEW_CONTACT_AGGREGATOR, Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE, + Settings.Global.NITZ_NETWORK_DISCONNECT_RETENTION, Settings.Global.NITZ_UPDATE_DIFF, Settings.Global.NITZ_UPDATE_SPACING, Settings.Global.NOTIFICATION_SNOOZE_OPTIONS, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 54fb6475197a..87d50f29061f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -575,6 +575,12 @@ <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" /> <uses-permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE" /> + <!-- Permission required for CTS test - SettingsMultiPaneDeepLinkTest --> + <uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" /> + + <!-- Permission required for ATS test - CarDevicePolicyManagerTest --> + <uses-permission android:name="android.permission.LOCK_DEVICE" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" @@ -641,6 +647,15 @@ </intent-filter> </receiver> + <receiver + android:name=".ProfcollectUploadReceiver" + android:exported="true" + android:permission="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD" > + <intent-filter> + <action android:name="com.android.shell.action.PROFCOLLECT_UPLOAD" /> + </intent-filter> + </receiver> + <service android:name=".BugreportProgressService" android:exported="false"/> diff --git a/packages/Shell/res/xml/file_provider_paths.xml b/packages/Shell/res/xml/file_provider_paths.xml index 225c7571e7e2..85d7dd372294 100644 --- a/packages/Shell/res/xml/file_provider_paths.xml +++ b/packages/Shell/res/xml/file_provider_paths.xml @@ -1,3 +1,4 @@ <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="bugreports" path="bugreports/" /> + <root-path name="profcollect" path="/data/misc/profcollectd/report/" /> </paths> diff --git a/packages/Shell/src/com/android/shell/ProfcollectUploadReceiver.java b/packages/Shell/src/com/android/shell/ProfcollectUploadReceiver.java new file mode 100644 index 000000000000..d2da724796cb --- /dev/null +++ b/packages/Shell/src/com/android/shell/ProfcollectUploadReceiver.java @@ -0,0 +1,98 @@ +/* + * 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.shell; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.util.Log; + +import androidx.core.content.FileProvider; + +import com.android.internal.R; + +import java.io.File; +import java.util.List; + +/** + * A proxy service that relays report upload requests to the uploader app, while translating + * the path to the report to a content URI owned by this service. + */ +public final class ProfcollectUploadReceiver extends BroadcastReceiver { + private static final String AUTHORITY = "com.android.shell"; + private static final String PROFCOLLECT_DATA_ROOT = "/data/misc/profcollectd/report/"; + + private static final String LOG_TAG = "ProfcollectUploadReceiver"; + + @Override + public void onReceive(Context context, Intent intent) { + Log.i(LOG_TAG, "Received upload intent"); + + String uploaderPkg = getUploaderPackageName(context); + String uploaderAction = getUploaderActionName(context); + + try { + ApplicationInfo info = context.getPackageManager().getApplicationInfo(uploaderPkg, + 0); + if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + Log.e(LOG_TAG, "The profcollect uploader app " + uploaderPkg + + " must be a system application"); + return; + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(LOG_TAG, "Cannot find profcollect uploader app " + uploaderPkg); + return; + } + + String filename = intent.getStringExtra("filename"); + File reportFile = new File(PROFCOLLECT_DATA_ROOT + filename); + Uri reportUri = FileProvider.getUriForFile(context, AUTHORITY, reportFile); + Intent uploadIntent = + new Intent(uploaderAction) + .setPackage(uploaderPkg) + .putExtra("EXTRA_DESTINATION", "PROFCOLLECT") + .putExtra("EXTRA_PACKAGE_NAME", context.getPackageName()) + .setData(reportUri) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + + List<ResolveInfo> receivers = + context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0); + if (receivers == null || receivers.isEmpty()) { + Log.e(LOG_TAG, "No one to receive upload intent, abort upload."); + return; + } + + context.grantUriPermission(uploaderPkg, reportUri, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + context.sendBroadcast(uploadIntent); + } + + private String getUploaderPackageName(Context context) { + return context.getResources().getString( + R.string.config_defaultProfcollectReportUploaderApp); + } + + private String getUploaderActionName(Context context) { + return context.getResources().getString( + R.string.config_defaultProfcollectReportUploaderAction); + } +} diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index 1cf14f2362de..e1da74466b55 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -11,6 +11,7 @@ asc@google.com awickham@google.com beverlyt@google.com brockman@google.com +brycelee@google.com ccassidy@google.com cinek@google.com cwren@google.com @@ -22,6 +23,7 @@ hwwang@google.com hyunyoungs@google.com jaggies@google.com jamesoleary@google.com +jbolinger@google.com jdemeulenaere@google.com jeffdq@google.com jjaggi@google.com @@ -70,4 +72,4 @@ zakcohen@google.com hseog@google.com #Android TV -rgl@google.com
\ No newline at end of file +rgl@google.com diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java index 376368fbf9d4..d80d9cc9d62d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java @@ -21,12 +21,19 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.util.AttributeSet; +import android.util.Log; +import android.widget.FrameLayout; +import android.widget.TextView; + +import com.android.systemui.R; /** * Manages the layout for under-display fingerprint sensors (UDFPS). Ensures that UI elements * do not overlap with */ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView { + private static final String TAG = "AuthBiometricUdfpsView"; + @Nullable private UdfpsDialogMeasureAdapter mMeasureAdapter; public AuthBiometricUdfpsView(Context context) { @@ -51,4 +58,23 @@ public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView { ? mMeasureAdapter.onMeasureInternal(width, height, layoutParams) : layoutParams; } + + @Override + void onLayoutInternal() { + super.onLayoutInternal(); + + // Move the UDFPS icon and indicator text if necessary. This probably only needs to happen + // for devices where the UDFPS sensor is too low. + // TODO(b/201510778): Update this logic to support cases where the sensor or text overlap + // the button bar area. + final int bottomSpacerHeight = mMeasureAdapter.getBottomSpacerHeight(); + Log.w(TAG, "bottomSpacerHeight: " + bottomSpacerHeight); + if (bottomSpacerHeight < 0) { + FrameLayout iconFrame = findViewById(R.id.biometric_icon_frame); + iconFrame.setTranslationY(-bottomSpacerHeight); + + TextView indicator = findViewById(R.id.indicator); + indicator.setTranslationY(-bottomSpacerHeight); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java index 7ccfb865cd5a..6185e59b17d8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java @@ -45,6 +45,7 @@ public class UdfpsDialogMeasureAdapter { @NonNull private final FingerprintSensorPropertiesInternal mSensorProps; @Nullable private WindowManager mWindowManager; + private int mBottomSpacerHeight; public UdfpsDialogMeasureAdapter( @NonNull ViewGroup view, @NonNull FingerprintSensorPropertiesInternal sensorProps) { @@ -74,6 +75,16 @@ public class UdfpsDialogMeasureAdapter { } } + /** + * @return the actual (and possibly negative) bottom spacer height. If negative, this indicates + * that the UDFPS sensor is too low. Our current xml and custom measurement logic is very hard + * too cleanly support this case. So, let's have the onLayout code translate the sensor location + * instead. + */ + int getBottomSpacerHeight() { + return mBottomSpacerHeight; + } + @NonNull private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height) { // Get the height of the everything below the icon. Currently, that's the indicator and @@ -86,7 +97,7 @@ public class UdfpsDialogMeasureAdapter { final int dialogMargin = getDialogMarginPx(); final int displayHeight = getWindowBounds().height(); final Insets navbarInsets = getNavbarInsets(); - final int bottomSpacerHeight = calculateBottomSpacerHeightForPortrait( + mBottomSpacerHeight = calculateBottomSpacerHeightForPortrait( mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight, dialogMargin, navbarInsets.bottom); @@ -122,9 +133,10 @@ public class UdfpsDialogMeasureAdapter { MeasureSpec.EXACTLY)); } else if (child.getId() == R.id.space_below_icon) { // Set the spacer height so the fingerprint icon is on the physical sensor area + final int clampedSpacerHeight = Math.max(mBottomSpacerHeight, 0); child.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(bottomSpacerHeight, MeasureSpec.EXACTLY)); + MeasureSpec.makeMeasureSpec(clampedSpacerHeight, MeasureSpec.EXACTLY)); } else { child.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 15f77ffc08fd..8a213ec502e4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -239,18 +239,20 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin if (mGhbmView != null && surface == null) { Log.e(TAG, "doIlluminate | surface must be non-null for GHBM"); } - mHbmProvider.enableHbm(mHbmType, surface, () -> { - if (mGhbmView != null) { - mGhbmView.drawIlluminationDot(mSensorRect); - } - if (onIlluminatedRunnable != null) { - // No framework API can reliably tell when a frame reaches the panel. A timeout - // is the safest solution. - postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs); - } else { - Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null"); - } - }); + if (mHbmProvider != null) { + mHbmProvider.enableHbm(mHbmType, surface, () -> { + if (mGhbmView != null) { + mGhbmView.drawIlluminationDot(mSensorRect); + } + if (onIlluminatedRunnable != null) { + // No framework API can reliably tell when a frame reaches the panel. A timeout + // is the safest solution. + postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs); + } else { + Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null"); + } + }); + } } @Override @@ -263,6 +265,8 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin mGhbmView.setGhbmIlluminationListener(null); mGhbmView.setVisibility(View.INVISIBLE); } - mHbmProvider.disableHbm(null /* onHbmDisabled */); + if (mHbmProvider != null) { + mHbmProvider.disableHbm(null /* onHbmDisabled */); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java index 89786ee880ad..a617850ef0ae 100644 --- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java @@ -139,7 +139,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener + " with ducking", e); } player.start(); - if (DEBUG) { Log.d(mTag, "player.start"); } + if (DEBUG) { Log.d(mTag, "player.start piid:" + player.getPlayerIId()); } } catch (Exception e) { if (player != null) { player.release(); @@ -155,7 +155,13 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener mPlayer = player; } if (mp != null) { - if (DEBUG) { Log.d(mTag, "mPlayer.release"); } + if (DEBUG) { + Log.d(mTag, "mPlayer.pause+release piid:" + player.getPlayerIId()); + } + mp.pause(); + try { + Thread.sleep(100); + } catch (InterruptedException ie) { } mp.release(); } this.notify(); @@ -244,6 +250,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener try { mp.stop(); } catch (Exception e) { } + if (DEBUG) { + Log.i(mTag, "About to release MediaPlayer piid:" + + mp.getPlayerIId() + " due to notif cancelled"); + } mp.release(); synchronized(mQueueAudioFocusLock) { if (mAudioManagerWithAudioFocus != null) { @@ -284,7 +294,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener public void onCompletion(MediaPlayer mp) { synchronized(mQueueAudioFocusLock) { if (mAudioManagerWithAudioFocus != null) { - if (DEBUG) Log.d(mTag, "onCompletion() abandonning AudioFocus"); + if (DEBUG) Log.d(mTag, "onCompletion() abandoning AudioFocus"); mAudioManagerWithAudioFocus.abandonAudioFocus(null); mAudioManagerWithAudioFocus = null; } else { @@ -310,6 +320,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener } } if (mp != null) { + if (DEBUG) { + Log.i("NotificationPlayer", "About to release MediaPlayer piid:" + + mp.getPlayerIId() + " due to onCompletion"); + } mp.release(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 541ee2c4fe8f..4a75810f86db 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -51,6 +51,7 @@ import com.android.systemui.qs.external.TileServices; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -95,6 +96,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D private final UiEventLogger mUiEventLogger; private final InstanceIdSequence mInstanceIdSequence; private final CustomTileStatePersister mCustomTileStatePersister; + private final FeatureFlags mFeatureFlags; private final List<Callback> mCallbacks = new ArrayList<>(); private AutoTileManager mAutoTiles; @@ -122,7 +124,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D UiEventLogger uiEventLogger, UserTracker userTracker, SecureSettings secureSettings, - CustomTileStatePersister customTileStatePersister + CustomTileStatePersister customTileStatePersister, + FeatureFlags featureFlags ) { mIconController = iconController; mContext = context; @@ -144,6 +147,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mUserTracker = userTracker; mSecureSettings = secureSettings; mCustomTileStatePersister = customTileStatePersister; + mFeatureFlags = featureFlags; mainHandler.post(() -> { // This is technically a hack to avoid circular dependency of @@ -265,7 +269,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) { newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode); } - final List<String> tileSpecs = loadTileSpecs(mContext, newValue); + final List<String> tileSpecs = loadTileSpecs(mContext, newValue, mFeatureFlags); int currentUser = mUserTracker.getUserId(); if (currentUser != mCurrentUser) { mUserContext = mUserTracker.getUserContext(); @@ -334,7 +338,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D if (newTiles.isEmpty() && !tileSpecs.isEmpty()) { // If we didn't manage to create any tiles, set it to empty (default) Log.d(TAG, "No valid tiles on tuning changed. Setting to default."); - changeTiles(currentSpecs, loadTileSpecs(mContext, "")); + changeTiles(currentSpecs, loadTileSpecs(mContext, "", mFeatureFlags)); } else { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onTilesChanged(); @@ -389,7 +393,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D private void changeTileSpecs(Predicate<List<String>> changeFunction) { final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser); - final List<String> tileSpecs = loadTileSpecs(mContext, setting); + final List<String> tileSpecs = loadTileSpecs(mContext, setting, mFeatureFlags); if (changeFunction.test(tileSpecs)) { saveTilesToSettings(tileSpecs); } @@ -478,7 +482,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec()); } - protected static List<String> loadTileSpecs(Context context, String tileList) { + protected static List<String> loadTileSpecs( + Context context, String tileList, FeatureFlags featureFlags) { final Resources res = context.getResources(); if (TextUtils.isEmpty(tileList)) { @@ -511,6 +516,21 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D } } } + if (featureFlags.isProviderModelSettingEnabled()) { + if (!tiles.contains("internet")) { + if (tiles.contains("wifi")) { + // Replace the WiFi with Internet, and remove the Cell + tiles.set(tiles.indexOf("wifi"), "internet"); + tiles.remove("cell"); + } else if (tiles.contains("cell")) { + // Replace the Cell with Internet + tiles.set(tiles.indexOf("cell"), "internet"); + } + } else { + tiles.remove("wifi"); + tiles.remove("cell"); + } + } return tiles; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index 3c2f35b954ea..f2832b3d45ff 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -41,6 +41,7 @@ import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.util.leak.GarbageMonitor; import java.util.ArrayList; @@ -62,6 +63,7 @@ public class TileQueryHelper { private final Executor mBgExecutor; private final Context mContext; private final UserTracker mUserTracker; + private final FeatureFlags mFeatureFlags; private TileStateListener mListener; private boolean mFinished; @@ -71,12 +73,14 @@ public class TileQueryHelper { Context context, UserTracker userTracker, @Main Executor mainExecutor, - @Background Executor bgExecutor + @Background Executor bgExecutor, + FeatureFlags featureFlags ) { mContext = context; mMainExecutor = mainExecutor; mBgExecutor = bgExecutor; mUserTracker = userTracker; + mFeatureFlags = featureFlags; } public void setListener(TileStateListener listener) { @@ -117,6 +121,10 @@ public class TileQueryHelper { } final ArrayList<QSTile> tilesToAdd = new ArrayList<>(); + if (mFeatureFlags.isProviderModelSettingEnabled()) { + possibleTiles.remove("cell"); + possibleTiles.remove("wifi"); + } for (String spec : possibleTiles) { // Only add current and stock tiles that can be created from QSFactoryImpl. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index 86c90c7bcb2e..9eb95c409009 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.row; -import android.annotation.ColorInt; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -28,15 +27,12 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.ViewState; public class FooterView extends StackScrollerDecorView { - private final int mClearAllTopPadding; private FooterViewButton mDismissButton; private FooterViewButton mManageButton; private boolean mShowHistory; public FooterView(Context context, AttributeSet attrs) { super(context, attrs); - mClearAllTopPadding = context.getResources().getDimensionPixelSize( - R.dimen.clear_all_padding_top); } @Override @@ -55,11 +51,6 @@ public class FooterView extends StackScrollerDecorView { mManageButton = findViewById(R.id.manage_text); } - public void setTextColor(@ColorInt int color) { - mManageButton.setTextColor(color); - mDismissButton.setTextColor(color); - } - public void setManageButtonClickListener(OnClickListener listener) { mManageButton.setOnClickListener(listener); } @@ -95,21 +86,25 @@ public class FooterView extends StackScrollerDecorView { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - int textColor = getResources().getColor(R.color.notif_pill_text); - Resources.Theme theme = getContext().getTheme(); - mDismissButton.setBackground( - getResources().getDrawable(R.drawable.notif_footer_btn_background, theme)); - mDismissButton.setTextColor(textColor); - mManageButton.setBackground( - getResources().getDrawable(R.drawable.notif_footer_btn_background, theme)); - mManageButton = findViewById(R.id.manage_text); + updateColors(); mDismissButton.setText(R.string.clear_all_notifications_text); - mManageButton.setTextColor(textColor); mDismissButton.setContentDescription( mContext.getString(R.string.accessibility_clear_all)); showHistory(mShowHistory); } + /** + * Update the text and background colors for the current color palette and night mode setting. + */ + public void updateColors() { + Resources.Theme theme = mContext.getTheme(); + int textColor = getResources().getColor(R.color.notif_pill_text, theme); + mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); + mDismissButton.setTextColor(textColor); + mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); + mManageButton.setTextColor(textColor); + } + @Override public ExpandableViewState createExpandableViewState() { return new FooterViewState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 289c32f17b31..0660daab3720 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -4231,7 +4231,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable final @ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); mSectionsManager.setHeaderForegroundColor(textColor); - mFooterView.setTextColor(textColor); + mFooterView.updateColors(); mEmptyShadeView.setTextColor(textColor); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 3ee3e55c749a..7f89b2629a6a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -139,7 +139,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { () -> mock(AutoTileManager.class), mock(DumpManager.class), mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)), mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class), - mock(SecureSettings.class), mock(CustomTileStatePersister.class)); + mock(SecureSettings.class), mock(CustomTileStatePersister.class), mFeatureFlags); qs.setHost(host); qs.setListening(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 9e97f801be3e..4cbad5f15c5f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -63,6 +63,7 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -124,6 +125,8 @@ public class QSTileHostTest extends SysuiTestCase { private SecureSettings mSecureSettings; @Mock private CustomTileStatePersister mCustomTileStatePersister; + @Mock + private FeatureFlags mFeatureFlags; private Handler mHandler; private TestableLooper mLooper; @@ -137,9 +140,9 @@ public class QSTileHostTest extends SysuiTestCase { mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager, mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker, - mSecureSettings, mCustomTileStatePersister); + mSecureSettings, mCustomTileStatePersister, mFeatureFlags); setUpTileFactory(); - + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false); when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt())) .thenReturn(""); } @@ -169,13 +172,13 @@ public class QSTileHostTest extends SysuiTestCase { @Test public void testLoadTileSpecs_emptySetting() { - List<String> tiles = QSTileHost.loadTileSpecs(mContext, ""); + List<String> tiles = QSTileHost.loadTileSpecs(mContext, "", mFeatureFlags); assertFalse(tiles.isEmpty()); } @Test public void testLoadTileSpecs_nullSetting() { - List<String> tiles = QSTileHost.loadTileSpecs(mContext, null); + List<String> tiles = QSTileHost.loadTileSpecs(mContext, null, mFeatureFlags); assertFalse(tiles.isEmpty()); } @@ -189,6 +192,55 @@ public class QSTileHostTest extends SysuiTestCase { } @Test + public void testRemoveWifiAndCellularWithoutInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2"); + + assertEquals("internet", mQSTileHost.mTileSpecs.get(0)); + assertEquals("spec1", mQSTileHost.mTileSpecs.get(1)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(2)); + } + + @Test + public void testRemoveWifiAndCellularWithInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet"); + + assertEquals("spec1", mQSTileHost.mTileSpecs.get(0)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(1)); + assertEquals("internet", mQSTileHost.mTileSpecs.get(2)); + } + + @Test + public void testRemoveWifiWithoutInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2"); + + assertEquals("spec1", mQSTileHost.mTileSpecs.get(0)); + assertEquals("internet", mQSTileHost.mTileSpecs.get(1)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(2)); + } + + @Test + public void testRemoveCellWithInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet"); + + assertEquals("spec1", mQSTileHost.mTileSpecs.get(0)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(1)); + assertEquals("internet", mQSTileHost.mTileSpecs.get(2)); + } + + @Test + public void testNoWifiNoCellularNoInternet() { + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true); + mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2"); + + assertEquals("spec1", mQSTileHost.mTileSpecs.get(0)); + assertEquals("spec2", mQSTileHost.mTileSpecs.get(1)); + } + + @Test public void testSpecWithInvalidDoesNotUseDefault() { mContext.getOrCreateTestableResources() .addOverride(R.string.quick_settings_tiles, "spec1,spec2"); @@ -321,7 +373,7 @@ public class QSTileHostTest extends SysuiTestCase { @Test public void testLoadTileSpec_repeated() { - List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2"); + List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2", mFeatureFlags); assertEquals(2, specs.size()); assertEquals("spec1", specs.get(0)); @@ -332,7 +384,7 @@ public class QSTileHostTest extends SysuiTestCase { public void testLoadTileSpec_repeatedInDefault() { mContext.getOrCreateTestableResources() .addOverride(R.string.quick_settings_tiles_default, "spec1,spec1"); - List<String> specs = QSTileHost.loadTileSpecs(mContext, "default"); + List<String> specs = QSTileHost.loadTileSpecs(mContext, "default", mFeatureFlags); // Remove spurious tiles, like dbg:mem specs.removeIf(spec -> !"spec1".equals(spec)); @@ -343,7 +395,7 @@ public class QSTileHostTest extends SysuiTestCase { public void testLoadTileSpec_repeatedDefaultAndSetting() { mContext.getOrCreateTestableResources() .addOverride(R.string.quick_settings_tiles_default, "spec1"); - List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1"); + List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1", mFeatureFlags); // Remove spurious tiles, like dbg:mem specs.removeIf(spec -> !"spec1".equals(spec)); @@ -371,11 +423,12 @@ public class QSTileHostTest extends SysuiTestCase { Provider<AutoTileManager> autoTiles, DumpManager dumpManager, BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger, UiEventLogger uiEventLogger, UserTracker userTracker, - SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) { + SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, + FeatureFlags featureFlags) { super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager, tunerService, autoTiles, dumpManager, broadcastDispatcher, Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings, - customTileStatePersister); + customTileStatePersister, featureFlags); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java index 4efcc5c3fc73..c5b67091d197 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java @@ -58,6 +58,7 @@ import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -108,6 +109,8 @@ public class TileQueryHelperTest extends SysuiTestCase { private PackageManager mPackageManager; @Mock private UserTracker mUserTracker; + @Mock + private FeatureFlags mFeatureFlags; @Captor private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor; @@ -133,12 +136,12 @@ public class TileQueryHelperTest extends SysuiTestCase { } } ).when(mQSTileHost).createTile(anyString()); - + when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false); FakeSystemClock clock = new FakeSystemClock(); mMainExecutor = new FakeExecutor(clock); mBgExecutor = new FakeExecutor(clock); mTileQueryHelper = new TileQueryHelper( - mContext, mUserTracker, mMainExecutor, mBgExecutor); + mContext, mUserTracker, mMainExecutor, mBgExecutor, mFeatureFlags); mTileQueryHelper.setListener(mListener); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java index 2b1840462291..01fa222896d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java @@ -48,6 +48,7 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.phone.AutoTileManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -98,6 +99,8 @@ public class TileServicesTest extends SysuiTestCase { private UserTracker mUserTracker; @Mock private SecureSettings mSecureSettings; + @Mock + private FeatureFlags mFeatureFlags; @Before public void setUp() throws Exception { @@ -119,7 +122,8 @@ public class TileServicesTest extends SysuiTestCase { mUiEventLogger, mUserTracker, mSecureSettings, - mock(CustomTileStatePersister.class)); + mock(CustomTileStatePersister.class), + mFeatureFlags); mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher, mUserTracker); } diff --git a/rs/jni/Android.bp b/rs/jni/Android.bp new file mode 100644 index 000000000000..9a6fa8e8e423 --- /dev/null +++ b/rs/jni/Android.bp @@ -0,0 +1,54 @@ +// +// 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 { + // See: http://go/android-license-faq + default_applicable_licenses: ["frameworks_base_license"], +} + +cc_library_shared { + name: "librs_jni", + + srcs: ["android_renderscript_RenderScript.cpp"], + + shared_libs: [ + "libandroid", + "libandroid_runtime", + "libandroidfw", + "libRS", + "libcutils", + "libhwui", + "liblog", + "libutils", + "libui", + "libgui", + "libjnigraphics", + ], + + header_libs: [ + "jni_headers", + "libbase_headers", + ], + + include_dirs: ["frameworks/rs"], + + cflags: [ + "-Wno-unused-parameter", + "-Wunused", + "-Wunreachable-code", + "-Wno-deprecated-declarations", + ], +} diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk deleted file mode 100644 index 0caba421dca8..000000000000 --- a/rs/jni/Android.mk +++ /dev/null @@ -1,37 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - android_renderscript_RenderScript.cpp - -LOCAL_SHARED_LIBRARIES := \ - libandroid \ - libandroid_runtime \ - libandroidfw \ - libRS \ - libcutils \ - libhwui \ - liblog \ - libutils \ - libui \ - libgui \ - libjnigraphics - -LOCAL_HEADER_LIBRARIES := \ - jni_headers \ - libbase_headers - -LOCAL_C_INCLUDES += \ - frameworks/rs - -LOCAL_CFLAGS += -Wno-unused-parameter -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -Wno-deprecated-declarations - -LOCAL_MODULE:= librs_jni -LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0 -LOCAL_LICENSE_CONDITIONS:= notice -LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../NOTICE -LOCAL_MODULE_TAGS := optional -LOCAL_REQUIRED_MODULES := libRS - -include $(BUILD_SHARED_LIBRARY) diff --git a/services/Android.bp b/services/Android.bp index cc0fd98c7060..e11b0442377e 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -23,6 +23,36 @@ java_defaults { }, } +// Opt-in config for optimizing and shrinking the services target using R8. +// Enabled via `export SYSTEM_OPTIMIZE_JAVA=true`, or explicitly in Make via the +// `SOONG_CONFIG_ANDROID_SYSTEM_OPTIMIZE_JAVA` variable. +// TODO(b/196084106): Enable optimizations by default after stabilizing and +// building out retrace infrastructure. +soong_config_module_type { + name: "system_optimized_java_defaults", + module_type: "java_defaults", + config_namespace: "ANDROID", + bool_variables: ["SYSTEM_OPTIMIZE_JAVA"], + properties: ["optimize"], +} + +system_optimized_java_defaults { + name: "services_java_defaults", + soong_config_variables: { + SYSTEM_OPTIMIZE_JAVA: { + optimize: { + enabled: true, + optimize: true, + shrink: true, + proguard_flags_files: ["proguard.flags"], + }, + // Note: Optimizations are disabled by default if unspecified in + // the java_library rule. + conditions_default: {}, + }, + }, +} + filegroup { name: "services-main-sources", srcs: [ @@ -81,6 +111,7 @@ java_library { // ============================================================ java_library { name: "services", + defaults: ["services_java_defaults"], installable: true, dex_preopt: { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 7d75b738d818..0aa50bd1fe93 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -407,7 +407,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) { synchronized (mLock) { - if (mSecurityPolicy.canPerformGestures(this)) { + if (mServiceInterface != null && mSecurityPolicy.canPerformGestures(this)) { MotionEventInjector motionEventInjector = mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId); if (motionEventInjector != null diff --git a/services/core/Android.bp b/services/core/Android.bp index 55b982b40611..d65969c11357 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -111,6 +111,7 @@ java_library_static { "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/wm/EventLogTags.logtags", "java/com/android/server/policy/EventLogTags.logtags", + ":services.connectivity-nsd-sources", ], libs: [ @@ -138,9 +139,11 @@ java_library_static { "android.hardware.boot-V1.1-java", "android.hardware.boot-V1.2-java", "android.hardware.broadcastradio-V2.0-java", - "android.hardware.health-V1.0-java", - "android.hardware.health-V2.0-java", - "android.hardware.health-V2.1-java", + "android.hardware.health-V1.0-java", // HIDL + "android.hardware.health-V2.0-java", // HIDL + "android.hardware.health-V2.1-java", // HIDL + "android.hardware.health-V1-java", // AIDL + "android.hardware.health-translate-java", "android.hardware.light-V1-java", "android.hardware.tv.cec-V1.1-java", "android.hardware.weaver-V1.0-java", @@ -149,7 +152,7 @@ java_library_static { "android.hardware.biometrics.fingerprint-V2.3-java", "android.hardware.biometrics.fingerprint-V1-java", "android.hardware.oemlock-V1.0-java", - "android.hardware.configstore-V1.0-java", + "android.hardware.configstore-V1.1-java", "android.hardware.contexthub-V1.0-java", "android.hardware.rebootescrow-V1-java", "android.hardware.soundtrigger-V2.3-java", diff --git a/services/core/java/com/android/server/AppFuseMountException.java b/services/core/java/com/android/server/AppFuseMountException.java new file mode 100644 index 000000000000..9a9379e4a1c7 --- /dev/null +++ b/services/core/java/com/android/server/AppFuseMountException.java @@ -0,0 +1,41 @@ +/* + * 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; + +import android.os.Parcel; + +/** + * An exception that indicates there was an error with a + * app fuse mount operation. + */ +public class AppFuseMountException extends Exception { + public AppFuseMountException(String detailMessage) { + super(detailMessage); + } + + public AppFuseMountException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } + + /** + * Rethrow as a {@link RuntimeException} subclass that is handled by + * {@link Parcel#writeException(Exception)}. + */ + public IllegalArgumentException rethrowAsParcelableException() { + throw new IllegalStateException(getMessage(), this); + } +} diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 0146aa82a217..844ac86e8eb5 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -17,6 +17,7 @@ package com.android.server; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import static com.android.server.health.Utils.copyV1Battery; import android.annotation.Nullable; import android.app.ActivityManager; @@ -25,14 +26,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; -import android.hardware.health.V1_0.HealthInfo; -import android.hardware.health.V2_0.IHealth; -import android.hardware.health.V2_0.Result; +import android.hardware.health.HealthInfo; import android.hardware.health.V2_1.BatteryCapacityLevel; -import android.hardware.health.V2_1.Constants; -import android.hardware.health.V2_1.IHealthInfoCallback; -import android.hidl.manager.V1_0.IServiceManager; -import android.hidl.manager.V1_0.IServiceNotification; import android.metrics.LogMaker; import android.os.BatteryManager; import android.os.BatteryManagerInternal; @@ -44,7 +39,6 @@ import android.os.Bundle; import android.os.DropBoxManager; import android.os.FileUtils; import android.os.Handler; -import android.os.HandlerThread; import android.os.IBatteryPropertiesRegistrar; import android.os.IBinder; import android.os.OsProtoEnums; @@ -62,15 +56,14 @@ import android.provider.Settings; import android.service.battery.BatteryServiceDumpProto; import android.sysprop.PowerProperties; import android.util.EventLog; -import android.util.MutableInt; import android.util.Slog; import android.util.proto.ProtoOutputStream; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.DumpUtils; import com.android.server.am.BatteryStatsService; +import com.android.server.health.HealthServiceWrapper; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; @@ -82,8 +75,6 @@ import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; /** * <p>BatteryService monitors the charging status, and charge level of the device @@ -147,7 +138,6 @@ public final class BatteryService extends SystemService { private HealthInfo mHealthInfo; private final HealthInfo mLastHealthInfo = new HealthInfo(); - private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1; private boolean mBatteryLevelCritical; private int mLastBatteryStatus; private int mLastBatteryHealth; @@ -191,7 +181,6 @@ public final class BatteryService extends SystemService { private ActivityManagerInternal mActivityManagerInternal; private HealthServiceWrapper mHealthServiceWrapper; - private HealthHalCallback mHealthHalCallback; private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar; private ArrayDeque<Bundle> mBatteryLevelsEventQueue; private long mLastBatteryLevelChangedSentMs; @@ -274,13 +263,9 @@ public final class BatteryService extends SystemService { private void registerHealthCallback() { traceBegin("HealthInitWrapper"); - mHealthServiceWrapper = new HealthServiceWrapper(); - mHealthHalCallback = new HealthHalCallback(); // IHealth is lazily retrieved. try { - mHealthServiceWrapper.init(mHealthHalCallback, - new HealthServiceWrapper.IServiceManagerSupplier() {}, - new HealthServiceWrapper.IHealthSupplier() {}); + mHealthServiceWrapper = HealthServiceWrapper.create(this::update); } catch (RemoteException ex) { Slog.e(TAG, "health: cannot register callback. (RemoteException)"); throw ex.rethrowFromSystemServer(); @@ -368,8 +353,8 @@ public final class BatteryService extends SystemService { } private boolean shouldShutdownLocked() { - if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) { - return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL); + if (mHealthInfo.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) { + return (mHealthInfo.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL); } if (mHealthInfo.batteryLevel > 0) { return false; @@ -411,7 +396,7 @@ public final class BatteryService extends SystemService { // shut down gracefully if temperature is too high (> 68.0C by default) // wait until the system has booted before attempting to display the // shutdown dialog. - if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) { + if (mHealthInfo.batteryTemperatureTenthsCelsius > mShutdownBatteryTemperature) { mHandler.post(new Runnable() { @Override public void run() { @@ -428,51 +413,28 @@ public final class BatteryService extends SystemService { } } - private void update(android.hardware.health.V2_1.HealthInfo info) { + private void update(android.hardware.health.HealthInfo info) { traceBegin("HealthInfoUpdate"); - Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter", - info.legacy.legacy.batteryChargeCounter); - Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", - info.legacy.legacy.batteryCurrent); - Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", - plugType(info.legacy.legacy)); - Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", - info.legacy.legacy.batteryStatus); + Trace.traceCounter( + Trace.TRACE_TAG_POWER, "BatteryChargeCounter", info.batteryChargeCounterUah); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.batteryCurrentMicroamps); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", plugType(info)); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", info.batteryStatus); synchronized (mLock) { if (!mUpdatesStopped) { - mHealthInfo = info.legacy.legacy; - mHealthInfo2p1 = info; + mHealthInfo = info; // Process the new values. processValuesLocked(false); mLock.notifyAll(); // for any waiters on new info } else { - copy(mLastHealthInfo, info.legacy.legacy); + copyV1Battery(mLastHealthInfo, info); } } traceEnd(); } - private static void copy(HealthInfo dst, HealthInfo src) { - dst.chargerAcOnline = src.chargerAcOnline; - dst.chargerUsbOnline = src.chargerUsbOnline; - dst.chargerWirelessOnline = src.chargerWirelessOnline; - dst.maxChargingCurrent = src.maxChargingCurrent; - dst.maxChargingVoltage = src.maxChargingVoltage; - dst.batteryStatus = src.batteryStatus; - dst.batteryHealth = src.batteryHealth; - dst.batteryPresent = src.batteryPresent; - dst.batteryLevel = src.batteryLevel; - dst.batteryVoltage = src.batteryVoltage; - dst.batteryTemperature = src.batteryTemperature; - dst.batteryCurrent = src.batteryCurrent; - dst.batteryCycleCount = src.batteryCycleCount; - dst.batteryFullCharge = src.batteryFullCharge; - dst.batteryChargeCounter = src.batteryChargeCounter; - dst.batteryTechnology = src.batteryTechnology; - } - private static int plugType(HealthInfo healthInfo) { if (healthInfo.chargerAcOnline) { return BatteryManager.BATTERY_PLUGGED_AC; @@ -503,11 +465,16 @@ public final class BatteryService extends SystemService { // Let the battery stats keep track of the current level. try { - mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, - mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature, - mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter, - mHealthInfo.batteryFullCharge, - mHealthInfo2p1.batteryChargeTimeToFullNowSeconds); + mBatteryStats.setBatteryState( + mHealthInfo.batteryStatus, + mHealthInfo.batteryHealth, + mPlugType, + mHealthInfo.batteryLevel, + mHealthInfo.batteryTemperatureTenthsCelsius, + mHealthInfo.batteryVoltageMillivolts, + mHealthInfo.batteryChargeCounterUah, + mHealthInfo.batteryFullChargeUah, + mHealthInfo.batteryChargeTimeToFullNowSeconds); } catch (RemoteException e) { // Should never happen. } @@ -515,17 +482,18 @@ public final class BatteryService extends SystemService { shutdownIfNoPowerLocked(); shutdownIfOverTempLocked(); - if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus || - mHealthInfo.batteryHealth != mLastBatteryHealth || - mHealthInfo.batteryPresent != mLastBatteryPresent || - mHealthInfo.batteryLevel != mLastBatteryLevel || - mPlugType != mLastPlugType || - mHealthInfo.batteryVoltage != mLastBatteryVoltage || - mHealthInfo.batteryTemperature != mLastBatteryTemperature || - mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent || - mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage || - mHealthInfo.batteryChargeCounter != mLastChargeCounter || - mInvalidCharger != mLastInvalidCharger)) { + if (force + || (mHealthInfo.batteryStatus != mLastBatteryStatus + || mHealthInfo.batteryHealth != mLastBatteryHealth + || mHealthInfo.batteryPresent != mLastBatteryPresent + || mHealthInfo.batteryLevel != mLastBatteryLevel + || mPlugType != mLastPlugType + || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage + || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature + || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent + || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage + || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter + || mInvalidCharger != mLastInvalidCharger)) { if (mPlugType != mLastPlugType) { if (mLastPlugType == BATTERY_PLUGGED_NONE) { @@ -582,8 +550,11 @@ public final class BatteryService extends SystemService { if (mHealthInfo.batteryLevel != mLastBatteryLevel) { // Don't do this just from voltage or temperature changes, that is // too noisy. - EventLog.writeEvent(EventLogTags.BATTERY_LEVEL, - mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature); + EventLog.writeEvent( + EventLogTags.BATTERY_LEVEL, + mHealthInfo.batteryLevel, + mHealthInfo.batteryVoltageMillivolts, + mHealthInfo.batteryTemperatureTenthsCelsius); } if (mBatteryLevelCritical && !mLastBatteryLevelCritical && mPlugType == BATTERY_PLUGGED_NONE) { @@ -689,11 +660,11 @@ public final class BatteryService extends SystemService { mLastBatteryPresent = mHealthInfo.batteryPresent; mLastBatteryLevel = mHealthInfo.batteryLevel; mLastPlugType = mPlugType; - mLastBatteryVoltage = mHealthInfo.batteryVoltage; - mLastBatteryTemperature = mHealthInfo.batteryTemperature; - mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent; - mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage; - mLastChargeCounter = mHealthInfo.batteryChargeCounter; + mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts; + mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius; + mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps; + mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts; + mLastChargeCounter = mHealthInfo.batteryChargeCounterUah; mLastBatteryLevelCritical = mBatteryLevelCritical; mLastInvalidCharger = mInvalidCharger; } @@ -716,13 +687,17 @@ public final class BatteryService extends SystemService { intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon); intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType); - intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage); - intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature); + intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts); + intent.putExtra( + BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius); intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology); intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger); - intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent); - intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage); - intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter); + intent.putExtra( + BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrentMicroamps); + intent.putExtra( + BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, + mHealthInfo.maxChargingVoltageMicrovolts); + intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah); if (DEBUG) { Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE + ", info:" + mHealthInfo.toString()); @@ -742,9 +717,9 @@ public final class BatteryService extends SystemService { event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast); event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE); event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType); - event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage); - event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature); - event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter); + event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts); + event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius); + event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah); event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now); boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty(); @@ -936,7 +911,7 @@ public final class BatteryService extends SystemService { } try { if (!mUpdatesStopped) { - copy(mLastHealthInfo, mHealthInfo); + copyV1Battery(mLastHealthInfo, mHealthInfo); } boolean update = true; switch (key) { @@ -959,10 +934,10 @@ public final class BatteryService extends SystemService { mHealthInfo.batteryLevel = Integer.parseInt(value); break; case "counter": - mHealthInfo.batteryChargeCounter = Integer.parseInt(value); + mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value); break; case "temp": - mHealthInfo.batteryTemperature = Integer.parseInt(value); + mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value); break; case "invalid": mInvalidCharger = Integer.parseInt(value); @@ -1006,7 +981,7 @@ public final class BatteryService extends SystemService { private void setChargerAcOnline(boolean online, boolean forceUpdate) { if (!mUpdatesStopped) { - copy(mLastHealthInfo, mHealthInfo); + copyV1Battery(mLastHealthInfo, mHealthInfo); } mHealthInfo.chargerAcOnline = online; mUpdatesStopped = true; @@ -1015,7 +990,7 @@ public final class BatteryService extends SystemService { private void setBatteryLevel(int level, boolean forceUpdate) { if (!mUpdatesStopped) { - copy(mLastHealthInfo, mHealthInfo); + copyV1Battery(mLastHealthInfo, mHealthInfo); } mHealthInfo.batteryLevel = level; mUpdatesStopped = true; @@ -1024,7 +999,7 @@ public final class BatteryService extends SystemService { private void unplugBattery(boolean forceUpdate, PrintWriter pw) { if (!mUpdatesStopped) { - copy(mLastHealthInfo, mHealthInfo); + copyV1Battery(mLastHealthInfo, mHealthInfo); } mHealthInfo.chargerAcOnline = false; mHealthInfo.chargerUsbOnline = false; @@ -1036,7 +1011,7 @@ public final class BatteryService extends SystemService { private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) { if (mUpdatesStopped) { mUpdatesStopped = false; - copy(mHealthInfo, mLastHealthInfo); + copyV1Battery(mHealthInfo, mLastHealthInfo); Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw)); } if (mBatteryInputSuspended) { @@ -1071,16 +1046,16 @@ public final class BatteryService extends SystemService { pw.println(" AC powered: " + mHealthInfo.chargerAcOnline); pw.println(" USB powered: " + mHealthInfo.chargerUsbOnline); pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline); - pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrent); - pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltage); - pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounter); + pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps); + pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts); + pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounterUah); pw.println(" status: " + mHealthInfo.batteryStatus); pw.println(" health: " + mHealthInfo.batteryHealth); pw.println(" present: " + mHealthInfo.batteryPresent); pw.println(" level: " + mHealthInfo.batteryLevel); pw.println(" scale: " + BATTERY_SCALE); - pw.println(" voltage: " + mHealthInfo.batteryVoltage); - pw.println(" temperature: " + mHealthInfo.batteryTemperature); + pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts); + pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius); pw.println(" technology: " + mHealthInfo.batteryTechnology); } else { Shell shell = new Shell(); @@ -1103,16 +1078,23 @@ public final class BatteryService extends SystemService { batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS; } proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue); - proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent); - proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage); - proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounter); + proto.write( + BatteryServiceDumpProto.MAX_CHARGING_CURRENT, + mHealthInfo.maxChargingCurrentMicroamps); + proto.write( + BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, + mHealthInfo.maxChargingVoltageMicrovolts); + proto.write( + BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah); proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus); proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth); proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent); proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel); proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE); - proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltage); - proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.batteryTemperature); + proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltageMillivolts); + proto.write( + BatteryServiceDumpProto.TEMPERATURE, + mHealthInfo.batteryTemperatureTenthsCelsius); proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology); } proto.flush(); @@ -1184,64 +1166,6 @@ public final class BatteryService extends SystemService { } } - private final class HealthHalCallback extends IHealthInfoCallback.Stub - implements HealthServiceWrapper.Callback { - @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) { - android.hardware.health.V2_1.HealthInfo propsLatest = - new android.hardware.health.V2_1.HealthInfo(); - propsLatest.legacy = props; - - propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED; - propsLatest.batteryChargeTimeToFullNowSeconds = - Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED; - - BatteryService.this.update(propsLatest); - } - - @Override public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) { - BatteryService.this.update(props); - } - - // on new service registered - @Override public void onRegistration(IHealth oldService, IHealth newService, - String instance) { - if (newService == null) return; - - traceBegin("HealthUnregisterCallback"); - try { - if (oldService != null) { - int r = oldService.unregisterCallback(this); - if (r != Result.SUCCESS) { - Slog.w(TAG, "health: cannot unregister previous callback: " + - Result.toString(r)); - } - } - } catch (RemoteException ex) { - Slog.w(TAG, "health: cannot unregister previous callback (transaction error): " - + ex.getMessage()); - } finally { - traceEnd(); - } - - traceBegin("HealthRegisterCallback"); - try { - int r = newService.registerCallback(this); - if (r != Result.SUCCESS) { - Slog.w(TAG, "health: cannot register callback: " + Result.toString(r)); - return; - } - // registerCallback does NOT guarantee that update is called - // immediately, so request a manual update here. - newService.update(); - } catch (RemoteException ex) { - Slog.e(TAG, "health: cannot register callback (transaction error): " - + ex.getMessage()); - } finally { - traceEnd(); - } - } - } - private final class BinderService extends Binder { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -1265,71 +1189,11 @@ public final class BatteryService extends SystemService { private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub { @Override public int getProperty(int id, final BatteryProperty prop) throws RemoteException { - traceBegin("HealthGetProperty"); - try { - IHealth service = mHealthServiceWrapper.getLastService(); - if (service == null) throw new RemoteException("no health service"); - final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED); - switch(id) { - case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: - service.getChargeCounter((int result, int value) -> { - outResult.value = result; - if (result == Result.SUCCESS) prop.setLong(value); - }); - break; - case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW: - service.getCurrentNow((int result, int value) -> { - outResult.value = result; - if (result == Result.SUCCESS) prop.setLong(value); - }); - break; - case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE: - service.getCurrentAverage((int result, int value) -> { - outResult.value = result; - if (result == Result.SUCCESS) prop.setLong(value); - }); - break; - case BatteryManager.BATTERY_PROPERTY_CAPACITY: - service.getCapacity((int result, int value) -> { - outResult.value = result; - if (result == Result.SUCCESS) prop.setLong(value); - }); - break; - case BatteryManager.BATTERY_PROPERTY_STATUS: - service.getChargeStatus((int result, int value) -> { - outResult.value = result; - if (result == Result.SUCCESS) prop.setLong(value); - }); - break; - case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER: - service.getEnergyCounter((int result, long value) -> { - outResult.value = result; - if (result == Result.SUCCESS) prop.setLong(value); - }); - break; - } - return outResult.value; - } finally { - traceEnd(); - } + return mHealthServiceWrapper.getProperty(id, prop); } @Override public void scheduleUpdate() throws RemoteException { - mHealthServiceWrapper.getHandlerThread().getThreadHandler().post(() -> { - traceBegin("HealthScheduleUpdate"); - try { - IHealth service = mHealthServiceWrapper.getLastService(); - if (service == null) { - Slog.e(TAG, "no health service"); - return; - } - service.update(); - } catch (RemoteException ex) { - Slog.e(TAG, "Cannot call update on health HAL", ex); - } finally { - traceEnd(); - } - }); + mHealthServiceWrapper.scheduleUpdate(); } } @@ -1358,14 +1222,14 @@ public final class BatteryService extends SystemService { @Override public int getBatteryChargeCounter() { synchronized (mLock) { - return mHealthInfo.batteryChargeCounter; + return mHealthInfo.batteryChargeCounterUah; } } @Override public int getBatteryFullCharge() { synchronized (mLock) { - return mHealthInfo.batteryFullCharge; + return mHealthInfo.batteryFullChargeUah; } } @@ -1418,184 +1282,4 @@ public final class BatteryService extends SystemService { BatteryService.this.suspendBatteryInput(); } } - - /** - * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when - * necessary. - * - * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and - * the internal service is refreshed. - * On death of an existing IHealth service, the internal service is NOT cleared to avoid - * race condition between death notification and new service notification. Hence, - * a caller must check for transaction errors when calling into the service. - * - * @hide Should only be used internally. - */ - public static final class HealthServiceWrapper { - private static final String TAG = "HealthServiceWrapper"; - public static final String INSTANCE_VENDOR = "default"; - - private final IServiceNotification mNotification = new Notification(); - private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder"); - // These variables are fixed after init. - private Callback mCallback; - private IHealthSupplier mHealthSupplier; - private String mInstanceName; - - // Last IHealth service received. - private final AtomicReference<IHealth> mLastService = new AtomicReference<>(); - - /** - * init should be called after constructor. For testing purposes, init is not called by - * constructor. - */ - public HealthServiceWrapper() { - } - - public IHealth getLastService() { - return mLastService.get(); - } - - /** - * See {@link #init(Callback, IServiceManagerSupplier, IHealthSupplier)} - */ - public void init() throws RemoteException, NoSuchElementException { - init(/* callback= */null, new HealthServiceWrapper.IServiceManagerSupplier() {}, - new HealthServiceWrapper.IHealthSupplier() {}); - } - - /** - * Start monitoring registration of new IHealth services. Only instance - * {@link #INSTANCE_VENDOR} and in device / framework manifest are used. This function should - * only be called once. - * - * mCallback.onRegistration() is called synchronously (aka in init thread) before - * this method returns if callback is not null. - * - * @throws RemoteException transaction error when talking to IServiceManager - * @throws NoSuchElementException if one of the following cases: - * - No service manager; - * - {@link #INSTANCE_VENDOR} is not in manifests (i.e. not - * available on this device), or none of these instances are available to current - * process. - * @throws NullPointerException when supplier is null - */ - void init(@Nullable Callback callback, - IServiceManagerSupplier managerSupplier, - IHealthSupplier healthSupplier) - throws RemoteException, NoSuchElementException, NullPointerException { - if (managerSupplier == null || healthSupplier == null) { - throw new NullPointerException(); - } - IServiceManager manager; - - mHealthSupplier = healthSupplier; - - // Initialize mLastService and call callback for the first time (in init thread) - IHealth newService = null; - traceBegin("HealthInitGetService_" + INSTANCE_VENDOR); - try { - newService = healthSupplier.get(INSTANCE_VENDOR); - } catch (NoSuchElementException ex) { - /* ignored, handled below */ - } finally { - traceEnd(); - } - if (newService != null) { - mInstanceName = INSTANCE_VENDOR; - mLastService.set(newService); - } - - if (mInstanceName == null || newService == null) { - throw new NoSuchElementException(String.format( - "IHealth service instance %s isn't available. Perhaps no permission?", - INSTANCE_VENDOR)); - } - - if (callback != null) { - mCallback = callback; - mCallback.onRegistration(null, newService, mInstanceName); - } - - // Register for future service registrations - traceBegin("HealthInitRegisterNotification"); - mHandlerThread.start(); - try { - managerSupplier.get().registerForNotifications( - IHealth.kInterfaceName, mInstanceName, mNotification); - } finally { - traceEnd(); - } - Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName); - } - - @VisibleForTesting - HandlerThread getHandlerThread() { - return mHandlerThread; - } - - interface Callback { - /** - * This function is invoked asynchronously when a new and related IServiceNotification - * is received. - * @param service the recently retrieved service from IServiceManager. - * Can be a dead service before service notification of a new service is delivered. - * Implementation must handle cases for {@link RemoteException}s when calling - * into service. - * @param instance instance name. - */ - void onRegistration(IHealth oldService, IHealth newService, String instance); - } - - /** - * Supplier of services. - * Must not return null; throw {@link NoSuchElementException} if a service is not available. - */ - interface IServiceManagerSupplier { - default IServiceManager get() throws NoSuchElementException, RemoteException { - return IServiceManager.getService(); - } - } - /** - * Supplier of services. - * Must not return null; throw {@link NoSuchElementException} if a service is not available. - */ - interface IHealthSupplier { - default IHealth get(String name) throws NoSuchElementException, RemoteException { - return IHealth.getService(name, true /* retry */); - } - } - - private class Notification extends IServiceNotification.Stub { - @Override - public final void onRegistration(String interfaceName, String instanceName, - boolean preexisting) { - if (!IHealth.kInterfaceName.equals(interfaceName)) return; - if (!mInstanceName.equals(instanceName)) return; - - // This runnable only runs on mHandlerThread and ordering is ensured, hence - // no locking is needed inside the runnable. - mHandlerThread.getThreadHandler().post(new Runnable() { - @Override - public void run() { - try { - IHealth newService = mHealthSupplier.get(mInstanceName); - IHealth oldService = mLastService.getAndSet(newService); - - // preexisting may be inaccurate (race). Check for equality here. - if (Objects.equals(newService, oldService)) return; - - Slog.i(TAG, "health: new instance registered " + mInstanceName); - // #init() may be called with null callback. Skip null callbacks. - if (mCallback == null) return; - mCallback.onRegistration(oldService, newService, mInstanceName); - } catch (NoSuchElementException | RemoteException ex) { - Slog.e(TAG, "health: Cannot get instance '" + mInstanceName - + "': " + ex.getMessage() + ". Perhaps no permission?"); - } - } - }); - } - } - } } diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java index 197321f1cb6a..263ff189a288 100644 --- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java +++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java @@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting; * when Bluetooth is on and Bluetooth is in one of the following situations: * 1. Bluetooth A2DP is connected. * 2. Bluetooth Hearing Aid profile is connected. + * 3. Bluetooth LE Audio is connected */ class BluetoothAirplaneModeListener { private static final String TAG = "BluetoothAirplaneModeListener"; @@ -132,7 +133,7 @@ class BluetoothAirplaneModeListener { return false; } if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn() - || !mAirplaneHelper.isA2dpOrHearingAidConnected()) { + || !mAirplaneHelper.isMediaProfileConnected()) { return false; } return true; diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index f62935ab1b13..8860a8164109 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -35,6 +35,7 @@ import android.app.BroadcastOptions; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.IBluetooth; @@ -456,12 +457,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) - || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { + || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action) + || BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(action)) { final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED) && state == BluetoothProfile.STATE_DISCONNECTED - && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + && !mBluetoothModeChangeHelper.isMediaProfileConnected()) { Slog.i(TAG, "Device disconnected, reactivating pending flag changes"); onInitFlagsChanged(); } @@ -2291,7 +2293,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); } mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + if (mBluetoothModeChangeHelper.isMediaProfileConnected()) { Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by " + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS + " ms due to existing connections"); diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java index 3642e4dccf34..e5854c968207 100644 --- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java +++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java @@ -20,6 +20,7 @@ import android.annotation.RequiresPermission; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.Context; @@ -37,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting; public class BluetoothModeChangeHelper { private volatile BluetoothA2dp mA2dp; private volatile BluetoothHearingAid mHearingAid; + private volatile BluetoothLeAudio mLeAudio; private final BluetoothAdapter mAdapter; private final Context mContext; @@ -47,6 +49,7 @@ public class BluetoothModeChangeHelper { mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.HEARING_AID); + mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.LE_AUDIO); } private final ServiceListener mProfileServiceListener = new ServiceListener() { @@ -60,6 +63,9 @@ public class BluetoothModeChangeHelper { case BluetoothProfile.HEARING_AID: mHearingAid = (BluetoothHearingAid) proxy; break; + case BluetoothProfile.LE_AUDIO: + mLeAudio = (BluetoothLeAudio) proxy; + break; default: break; } @@ -75,6 +81,9 @@ public class BluetoothModeChangeHelper { case BluetoothProfile.HEARING_AID: mHearingAid = null; break; + case BluetoothProfile.LE_AUDIO: + mLeAudio = null; + break; default: break; } @@ -82,8 +91,8 @@ public class BluetoothModeChangeHelper { }; @VisibleForTesting - public boolean isA2dpOrHearingAidConnected() { - return isA2dpConnected() || isHearingAidConnected(); + public boolean isMediaProfileConnected() { + return isA2dpConnected() || isHearingAidConnected() || isLeAudioConnected(); } @VisibleForTesting @@ -142,4 +151,12 @@ public class BluetoothModeChangeHelper { } return hearingAid.getConnectedDevices().size() > 0; } + + private boolean isLeAudioConnected() { + final BluetoothLeAudio leAudio = mLeAudio; + if (leAudio == null) { + return false; + } + return leAudio.getConnectedDevices().size() > 0; + } } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index c7e906876526..a2c2dbd407a5 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -39,8 +39,6 @@ import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.TrafficStats.UID_TETHERING; -import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; - import android.annotation.NonNull; import android.app.ActivityManager; import android.content.Context; @@ -72,7 +70,6 @@ import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.StrictMode; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.Trace; import android.text.TextUtils; import android.util.Log; @@ -446,9 +443,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub { // push any existing quota or UID rules synchronized (mQuotaLock) { - // Netd unconditionally enable bandwidth control - SystemProperties.set(PROP_QTAGUID_ENABLED, "1"); - mStrictEnabled = true; setDataSaverModeEnabled(mDataSaverMode); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 69c29269b7a9..32f6496af151 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1230,7 +1230,7 @@ class StorageManagerService extends IStorageManager.Stub } for (int i = 0; i < mVolumes.size(); i++) { final VolumeInfo vol = mVolumes.valueAt(i); - if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) { + if (vol.isVisibleForUser(userId) && vol.isMountedReadable()) { final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); @@ -1558,13 +1558,13 @@ class StorageManagerService extends IStorageManager.Stub && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) { Slog.v(TAG, "Found primary storage at " + vol); vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; - vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE; mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) { Slog.v(TAG, "Found primary storage at " + vol); vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; - vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE; mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); } @@ -1574,13 +1574,13 @@ class StorageManagerService extends IStorageManager.Stub && vol.disk.isDefaultPrimary()) { Slog.v(TAG, "Found primary storage at " + vol); vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; - vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE; } // Adoptable public disks are visible to apps, since they meet // public API requirement of being in a stable location. if (vol.disk.isAdoptable()) { - vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE; } vol.mountUserId = mCurrentUserId; @@ -1591,7 +1591,9 @@ class StorageManagerService extends IStorageManager.Stub } else if (vol.type == VolumeInfo.TYPE_STUB) { if (vol.disk.isStubVisible()) { - vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE; + } else { + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_READ; } vol.mountUserId = mCurrentUserId; mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); @@ -1738,7 +1740,7 @@ class StorageManagerService extends IStorageManager.Stub // started after this point will trigger additional // user-specific broadcasts. for (int userId : mSystemUnlockedUsers) { - if (vol.isVisibleForRead(userId)) { + if (vol.isVisibleForUser(userId)) { final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); @@ -3560,24 +3562,24 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public ParcelFileDescriptor open() throws NativeDaemonConnectorException { + public ParcelFileDescriptor open() throws AppFuseMountException { try { final FileDescriptor fd = mVold.mountAppFuse(uid, mountId); mMounted = true; return new ParcelFileDescriptor(fd); } catch (Exception e) { - throw new NativeDaemonConnectorException("Failed to mount", e); + throw new AppFuseMountException("Failed to mount", e); } } @Override public ParcelFileDescriptor openFile(int mountId, int fileId, int flags) - throws NativeDaemonConnectorException { + throws AppFuseMountException { try { return new ParcelFileDescriptor( mVold.openAppFuseFile(uid, mountId, fileId, flags)); } catch (Exception e) { - throw new NativeDaemonConnectorException("Failed to open", e); + throw new AppFuseMountException("Failed to open", e); } } @@ -3617,7 +3619,7 @@ class StorageManagerService extends IStorageManager.Stub // It seems the thread of mAppFuseBridge has already been terminated. mAppFuseBridge = null; } - } catch (NativeDaemonConnectorException e) { + } catch (AppFuseMountException e) { throw e.rethrowAsParcelableException(); } } @@ -3766,7 +3768,7 @@ class StorageManagerService extends IStorageManager.Stub if (forWrite) { match = vol.isVisibleForWrite(userId); } else { - match = vol.isVisibleForRead(userId) + match = vol.isVisibleForUser(userId) || (includeInvisible && vol.getPath() != null); } if (!match) continue; diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 43bd9e773070..5ed6c86bdc18 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -47,6 +47,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.DeviceConfig; import android.telecom.TelecomManager; +import android.telephony.AccessNetworkConstants; import android.telephony.Annotation; import android.telephony.Annotation.RadioPowerState; import android.telephony.Annotation.SrvccState; @@ -1164,17 +1165,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains( - TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { - updateReportSignalStrengthDecision(r.subId); - try { - if (mSignalStrength[r.phoneId] != null) { - r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]); - } - } catch (RemoteException ex) { - remove(r.binder); - } - } if (validateEventAndUserLocked( r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) { try { @@ -1353,27 +1343,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - private void updateReportSignalStrengthDecision(int subscriptionId) { - synchronized (mRecords) { - TelephonyManager telephonyManager = (TelephonyManager) mContext - .getSystemService(Context.TELEPHONY_SERVICE); - for (Record r : mRecords) { - // If any of the system clients wants to always listen to signal strength, - // we need to set it on. - if (r.matchTelephonyCallbackEvent( - TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { - telephonyManager.createForSubscriptionId(subscriptionId) - .setAlwaysReportSignalStrength(true); - return; - } - } - // If none of the system clients wants to always listen to signal strength, - // we need to set it off. - telephonyManager.createForSubscriptionId(subscriptionId) - .setAlwaysReportSignalStrength(false); - } - } - private String getCallIncomingNumber(Record record, int phoneId) { // Only reveal the incoming number if the record has read call log permission. return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : ""; @@ -1457,14 +1426,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } mRecords.remove(i); - - // Every time a client that is registrating to always receive the signal - // strength is removed from registry records, we need to check if - // the signal strength decision needs to update on its slot. - if (r.matchTelephonyCallbackEvent( - TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { - updateReportSignalStrengthDecision(r.subId); - } return; } } @@ -1696,10 +1657,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId + " phoneId=" + phoneId + " ss=" + signalStrength); } - if ((r.matchTelephonyCallbackEvent( + if (r.matchTelephonyCallbackEvent( TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED) - || r.matchTelephonyCallbackEvent( - TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) && idMatch(r, subId, phoneId)) { try { if (DBG) { @@ -2002,42 +1961,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { ApnSetting apnSetting = preciseState.getApnSetting(); - int apnTypes = apnSetting.getApnTypeBitmask(); - int state = preciseState.getState(); - int networkType = preciseState.getNetworkType(); - synchronized (mRecords) { if (validatePhoneId(phoneId)) { - // We only call the callback when the change is for default APN type. - if ((ApnSetting.TYPE_DEFAULT & apnTypes) != 0 - && (mDataConnectionState[phoneId] != state - || mDataConnectionNetworkType[phoneId] != networkType)) { - String str = "onDataConnectionStateChanged(" - + TelephonyUtils.dataStateToString(state) - + ", " + getNetworkTypeName(networkType) - + ") subId=" + subId + ", phoneId=" + phoneId; - log(str); - mLocalLog.log(str); - for (Record r : mRecords) { - if (r.matchTelephonyCallbackEvent( - TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED) - && idMatch(r, subId, phoneId)) { - try { - if (DBG) { - log("Notify data connection state changed on sub: " + subId); - } - r.callback.onDataConnectionStateChanged(state, networkType); - } catch (RemoteException ex) { - mRemoveList.add(r.binder); - } - } - } - handleRemoveListLocked(); - - mDataConnectionState[phoneId] = state; - mDataConnectionNetworkType[phoneId] = networkType; - } - Pair<Integer, ApnSetting> key = Pair.create(preciseState.getTransportType(), preciseState.getApnSetting()); PreciseDataConnectionState oldState = mPreciseDataConnectionStates.get(phoneId) @@ -2069,6 +1994,73 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (preciseState.getState() != TelephonyManager.DATA_DISCONNECTED) { mPreciseDataConnectionStates.get(phoneId).put(key, preciseState); } + + // Note that below is just the workaround for reporting the correct data connection + // state. The actual fix should be put in the new data stack in T. + // TODO: Remove the code below in T. + + // Collect all possible candidate data connection state for internet. Key is the + // data connection state, value is the precise data connection state. + Map<Integer, PreciseDataConnectionState> internetConnections = new ArrayMap<>(); + if (preciseState.getState() == TelephonyManager.DATA_DISCONNECTED + && preciseState.getApnSetting().getApnTypes() + .contains(ApnSetting.TYPE_DEFAULT)) { + internetConnections.put(TelephonyManager.DATA_DISCONNECTED, preciseState); + } + for (Map.Entry<Pair<Integer, ApnSetting>, PreciseDataConnectionState> entry : + mPreciseDataConnectionStates.get(phoneId).entrySet()) { + if (entry.getKey().first == AccessNetworkConstants.TRANSPORT_TYPE_WWAN + && entry.getKey().second.getApnTypes() + .contains(ApnSetting.TYPE_DEFAULT)) { + internetConnections.put(entry.getValue().getState(), entry.getValue()); + } + } + + // If any internet data is in connected state, then report connected, then check + // suspended, connecting, disconnecting, and disconnected. The order is very + // important. + int[] statesInPriority = new int[]{TelephonyManager.DATA_CONNECTED, + TelephonyManager.DATA_SUSPENDED, TelephonyManager.DATA_CONNECTING, + TelephonyManager.DATA_DISCONNECTING, + TelephonyManager.DATA_DISCONNECTED}; + int state = TelephonyManager.DATA_DISCONNECTED; + int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + for (int s : statesInPriority) { + if (internetConnections.containsKey(s)) { + state = s; + networkType = internetConnections.get(s).getNetworkType(); + break; + } + } + + if (mDataConnectionState[phoneId] != state + || mDataConnectionNetworkType[phoneId] != networkType) { + String str = "onDataConnectionStateChanged(" + + TelephonyUtils.dataStateToString(state) + + ", " + TelephonyManager.getNetworkTypeName(networkType) + + ") subId=" + subId + ", phoneId=" + phoneId; + log(str); + mLocalLog.log(str); + for (Record r : mRecords) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED) + && idMatch(r, subId, phoneId)) { + try { + if (DBG) { + log("Notify data connection state changed on sub: " + subId); + } + r.callback.onDataConnectionStateChanged(state, networkType); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + + mDataConnectionState[phoneId] = state; + mDataConnectionNetworkType[phoneId] = networkType; + + handleRemoveListLocked(); + } } } } @@ -3119,11 +3111,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null); } - if ((events.contains(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null); - } - if (isPrivilegedPhoneStatePermissionRequired(events)) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); @@ -3284,9 +3271,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED) - || events.contains( - TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) { + if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) { try { if (mSignalStrength[phoneId] != null) { SignalStrength signalStrength = mSignalStrength[phoneId]; diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index b068f86ff0ab..0c990ecfc827 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -141,7 +141,7 @@ import java.util.concurrent.TimeUnit; * | or its properties * v | * +-----------------------------------------------------------------------+ - * | UnderlyingNetworkTracker | + * | UnderlyingNetworkController | * | | * | Manages lifecycle of underlying physical networks, filing requests to | * | bring them up, and releasing them as they become no longer necessary | diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 731f7fec466c..ab2147dff853 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1834,6 +1834,11 @@ public class AccountManagerService + ", skipping since the account already exists"); return false; } + if (accounts.accountsDb.findAllDeAccounts().size() > 100) { + Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() + + ", skipping since more than 50 accounts on device exist"); + return false; + } long accountId = accounts.accountsDb.insertCeAccount(account, password); if (accountId < 0) { Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString() diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 3ccacd84a9f3..c5ac3907ecfe 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -94,6 +94,8 @@ public class SettingsToPropertiesMapper { DeviceConfig.NAMESPACE_STATSD_NATIVE, DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT, DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + DeviceConfig.NAMESPACE_TETHERING, + DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE, DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT, }; diff --git a/services/core/java/com/android/server/app/OWNERS b/services/core/java/com/android/server/app/OWNERS new file mode 100644 index 000000000000..aaebbfa8e253 --- /dev/null +++ b/services/core/java/com/android/server/app/OWNERS @@ -0,0 +1 @@ +per-file GameManager* = file:/GAME_MANAGER_OWNERS diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index c383f5120407..0b2311bc563a 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -17,12 +17,9 @@ package com.android.server.audio; import android.annotation.NonNull; import android.annotation.Nullable; -import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothLeAudio; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -32,6 +29,7 @@ import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.media.IAudioRoutesObserver; import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.ICommunicationDeviceDispatcher; @@ -516,29 +514,82 @@ import java.util.concurrent.atomic.AtomicBoolean; } }; - /*package*/ static final class BtDeviceConnectionInfo { + /*package*/ static final class BtDeviceChangedData { + final @Nullable BluetoothDevice mNewDevice; + final @Nullable BluetoothDevice mPreviousDevice; + final @NonNull BtProfileConnectionInfo mInfo; + final @NonNull String mEventSource; + + BtDeviceChangedData(@Nullable BluetoothDevice newDevice, + @Nullable BluetoothDevice previousDevice, + @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) { + mNewDevice = newDevice; + mPreviousDevice = previousDevice; + mInfo = info; + mEventSource = eventSource; + } + + @Override + public String toString() { + return "BtDeviceChangedData profile=" + BluetoothProfile.getProfileName( + mInfo.getProfile()) + + ", switch device: [" + mPreviousDevice + "] -> [" + mNewDevice + "]"; + } + } + + /*package*/ static final class BtDeviceInfo { final @NonNull BluetoothDevice mDevice; final @AudioService.BtProfileConnectionState int mState; - final int mProfile; + final @AudioService.BtProfile int mProfile; final boolean mSupprNoisy; final int mVolume; + final boolean mIsLeOutput; + final @NonNull String mEventSource; + final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec; + final int mAudioSystemDevice; + final int mMusicDevice; - BtDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int vol) { + BtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, int state, + int audioDevice, @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) { mDevice = device; mState = state; + mProfile = d.mInfo.getProfile(); + mSupprNoisy = d.mInfo.getSuppressNoisyIntent(); + mVolume = d.mInfo.getVolume(); + mIsLeOutput = d.mInfo.getIsLeOutput(); + mEventSource = d.mEventSource; + mAudioSystemDevice = audioDevice; + mMusicDevice = AudioSystem.DEVICE_NONE; + mCodec = codec; + } + + // constructor used by AudioDeviceBroker to search similar message + BtDeviceInfo(@NonNull BluetoothDevice device, int profile) { + mDevice = device; mProfile = profile; - mSupprNoisy = suppressNoisyIntent; - mVolume = vol; - } - - BtDeviceConnectionInfo(@NonNull BtDeviceConnectionInfo info) { - mDevice = info.mDevice; - mState = info.mState; - mProfile = info.mProfile; - mSupprNoisy = info.mSupprNoisy; - mVolume = info.mVolume; + mEventSource = ""; + mMusicDevice = AudioSystem.DEVICE_NONE; + mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; + mAudioSystemDevice = 0; + mState = 0; + mSupprNoisy = false; + mVolume = -1; + mIsLeOutput = false; + } + + // constructor used by AudioDeviceInventory when config change failed + BtDeviceInfo(@NonNull BluetoothDevice device, int profile, int state, int musicDevice, + int audioSystemDevice) { + mDevice = device; + mProfile = profile; + mEventSource = ""; + mMusicDevice = musicDevice; + mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; + mAudioSystemDevice = audioSystemDevice; + mState = state; + mSupprNoisy = false; + mVolume = -1; + mIsLeOutput = false; } // redefine equality op so we can match messages intended for this device @@ -550,16 +601,52 @@ import java.util.concurrent.atomic.AtomicBoolean; if (this == o) { return true; } - if (o instanceof BtDeviceConnectionInfo) { - return mDevice.equals(((BtDeviceConnectionInfo) o).mDevice); + if (o instanceof BtDeviceInfo) { + return mProfile == ((BtDeviceInfo) o).mProfile + && mDevice.equals(((BtDeviceInfo) o).mDevice); } return false; } + } - @Override - public String toString() { - return "BtDeviceConnectionInfo dev=" + mDevice.toString(); + BtDeviceInfo createBtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, + int state) { + int audioDevice; + int codec = AudioSystem.AUDIO_FORMAT_DEFAULT; + switch (d.mInfo.getProfile()) { + case BluetoothProfile.A2DP_SINK: + audioDevice = AudioSystem.DEVICE_IN_BLUETOOTH_A2DP; + break; + case BluetoothProfile.A2DP: + audioDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP; + synchronized (mDeviceStateLock) { + codec = mBtHelper.getA2dpCodec(device); + } + break; + case BluetoothProfile.HEARING_AID: + audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID; + break; + case BluetoothProfile.LE_AUDIO: + if (d.mInfo.getIsLeOutput()) { + audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET; + } else { + audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET; + } + break; + default: throw new IllegalArgumentException("Invalid profile " + d.mInfo.getProfile()); } + return new BtDeviceInfo(d, device, state, audioDevice, codec); + } + + private void btMediaMetricRecord(@NonNull BluetoothDevice device, String state, + @NonNull BtDeviceChangedData data) { + final String name = TextUtils.emptyIfNull(device.getName()); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + + "queueOnBluetoothActiveDeviceChanged") + .set(MediaMetrics.Property.STATE, state) + .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile()) + .set(MediaMetrics.Property.NAME, name) + .record(); } /** @@ -567,116 +654,37 @@ import java.util.concurrent.atomic.AtomicBoolean; * not just a simple message post * @param info struct with the (dis)connection information */ - /*package*/ void queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - @NonNull BtDeviceConnectionInfo info) { - final String name = TextUtils.emptyIfNull(info.mDevice.getName()); - new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR - + "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent") - .set(MediaMetrics.Property.STATE, info.mState == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .set(MediaMetrics.Property.INDEX, info.mVolume) - .set(MediaMetrics.Property.NAME, name) - .record(); - - // operations of removing and posting messages related to A2DP device state change must be - // mutually exclusive - synchronized (mDeviceStateLock) { - // when receiving a request to change the connection state of a device, this last - // request is the source of truth, so cancel all previous requests that are already in - // the handler - removeScheduledA2dpEvents(info.mDevice); - - sendLMsgNoDelay( - info.mState == BluetoothProfile.STATE_CONNECTED - ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION - : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - SENDMSG_QUEUE, info); - } - } - - /** remove all previously scheduled connection and state change events for the given device */ - @GuardedBy("mDeviceStateLock") - private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device) { - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, device); - - final BtDeviceConnectionInfo connectionInfoToRemove = new BtDeviceConnectionInfo(device, - // the next parameters of the constructor will be ignored when finding the message - // to remove as the equality of the message's object is tested on the device itself - // (see BtDeviceConnectionInfo.equals() method override) - BluetoothProfile.STATE_CONNECTED, 0, false, -1); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - connectionInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION, - connectionInfoToRemove); - - final BtHelper.BluetoothA2dpDeviceInfo devInfoToRemove = - new BtHelper.BluetoothA2dpDeviceInfo(device); - mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, - devInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - devInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, - devInfoToRemove); - } - - private static final class HearingAidDeviceConnectionInfo { - final @NonNull BluetoothDevice mDevice; - final @AudioService.BtProfileConnectionState int mState; - final boolean mSupprNoisy; - final int mMusicDevice; - final @NonNull String mEventSource; - - HearingAidDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) { - mDevice = device; - mState = state; - mSupprNoisy = suppressNoisyIntent; - mMusicDevice = musicDevice; - mEventSource = eventSource; - } - } - - /*package*/ void postBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) { - final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo( - device, state, suppressNoisyIntent, musicDevice, eventSource); - sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - - private static final class LeAudioDeviceConnectionInfo { - final @NonNull BluetoothDevice mDevice; - final @AudioService.BtProfileConnectionState int mState; - final boolean mSupprNoisy; - final @NonNull String mEventSource; - - LeAudioDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, @NonNull String eventSource) { - mDevice = device; - mState = state; - mSupprNoisy = suppressNoisyIntent; - mEventSource = eventSource; + /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) { + if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null + && data.mPreviousDevice.equals(data.mNewDevice)) { + final String name = TextUtils.emptyIfNull(data.mNewDevice.getName()); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + + "queueOnBluetoothActiveDeviceChanged_update") + .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile()) + .record(); + synchronized (mDeviceStateLock) { + postBluetoothA2dpDeviceConfigChange(data.mNewDevice); + } + } else { + synchronized (mDeviceStateLock) { + if (data.mPreviousDevice != null) { + btMediaMetricRecord(data.mPreviousDevice, MediaMetrics.Value.DISCONNECTED, + data); + sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, + createBtDeviceInfo(data, data.mPreviousDevice, + BluetoothProfile.STATE_DISCONNECTED)); + } + if (data.mNewDevice != null) { + btMediaMetricRecord(data.mNewDevice, MediaMetrics.Value.CONNECTED, data); + sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, + createBtDeviceInfo(data, data.mNewDevice, + BluetoothProfile.STATE_CONNECTED)); + } + } } } - /*package*/ void postBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, @NonNull String eventSource) { - final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo( - device, state, suppressNoisyIntent, eventSource); - sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - - /*package*/ void postBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - @NonNull String eventSource) { - final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo( - device, state, false, eventSource); - sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - /** * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn(). */ @@ -926,19 +934,8 @@ import java.util.concurrent.atomic.AtomicBoolean; } @GuardedBy("mDeviceStateLock") - /*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { - sendILMsg(state == BluetoothA2dp.STATE_CONNECTED - ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED - : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - SENDMSG_QUEUE, - state, btDeviceInfo, delay); - } - - /*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { - sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, - state, btDeviceInfo, delay); + /*package*/ void postBluetoothActiveDevice(BtDeviceInfo info, int delay) { + sendLMsg(MSG_L_SET_BT_ACTIVE_DEVICE, SENDMSG_QUEUE, info, delay); } /*package*/ void postSetWiredDeviceConnectionState( @@ -946,72 +943,12 @@ import java.util.concurrent.atomic.AtomicBoolean; sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay); } - /*package*/ void postSetHearingAidConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device, int delay) { - sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device, - delay); - } - - /*package*/ void postSetLeAudioOutConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device, int delay) { - sendILMsg(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device, - delay); - } - - /*package*/ void postSetLeAudioInConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device) { - sendILMsgNoDelay(MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device); - } - - /*package*/ void postDisconnectA2dp() { - sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectA2dpSink() { - sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectHearingAid() { - sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectLeAudio() { - sendMsgNoDelay(MSG_DISCONNECT_BT_LE_AUDIO, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectHeadset() { - sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE); - } - - /*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile); - } - - /*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile); - } - - /*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile); - } - - /*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE, - hearingAidProfile); + /*package*/ void postBtProfileDisconnected(int profile) { + sendIMsgNoDelay(MSG_I_BT_SERVICE_DISCONNECTED_PROFILE, SENDMSG_QUEUE, profile); } - /*package*/ void postBtLeAudioProfileConnected(BluetoothLeAudio leAudioProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO, SENDMSG_QUEUE, - leAudioProfile); + /*package*/ void postBtProfileConnected(int profile, BluetoothProfile proxy) { + sendILMsgNoDelay(MSG_IL_BT_SERVICE_CONNECTED_PROFILE, SENDMSG_QUEUE, profile, proxy); } /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) { @@ -1069,13 +1006,6 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { - final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0; - sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state, - btDeviceInfo); - } - /*package*/ void handleFailureToConnectToBtHeadsetService(int delay) { sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay); } @@ -1088,19 +1018,10 @@ import java.util.concurrent.atomic.AtomicBoolean; sendMsgNoDelay(fromA2dp ? MSG_REPORT_NEW_ROUTES_A2DP : MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP); } - /*package*/ void postA2dpActiveDeviceChange( - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { - sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo); - } - // must be called synchronized on mConnectedDevices - /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) { - final BtHelper.BluetoothA2dpDeviceInfo devInfoToCheck = - new BtHelper.BluetoothA2dpDeviceInfo(btDevice); - return (mBrokerHandler.hasEqualMessages( - MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, devInfoToCheck) - || mBrokerHandler.hasEqualMessages( - MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, devInfoToCheck)); + /*package*/ boolean hasScheduledA2dpConnection(BluetoothDevice btDevice) { + final BtDeviceInfo devInfoToCheck = new BtDeviceInfo(btDevice, BluetoothProfile.A2DP); + return mBrokerHandler.hasEqualMessages(MSG_L_SET_BT_ACTIVE_DEVICE, devInfoToCheck); } /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) { @@ -1124,12 +1045,6 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) { - synchronized (mDeviceStateLock) { - return mBtHelper.getA2dpCodec(device); - } - } - /*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) { mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent); } @@ -1285,39 +1200,11 @@ import java.util.concurrent.atomic.AtomicBoolean; mDeviceInventory.onReportNewRoutes(); } break; - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetA2dpSinkConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); - } - break; - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetA2dpSourceConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); - } - break; - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: synchronized (mDeviceStateLock) { - mDeviceInventory.onSetHearingAidConnectionState( - (BluetoothDevice) msg.obj, msg.arg1, + mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj, mAudioService.getBluetoothContextualVolumeStream()); } - break; - case MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetLeAudioOutConnectionState( - (BluetoothDevice) msg.obj, msg.arg1, - mAudioService.getBluetoothContextualVolumeStream()); - } - break; - case MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetLeAudioInConnectionState( - (BluetoothDevice) msg.obj, msg.arg1); - } - break; case MSG_BT_HEADSET_CNCT_FAILED: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { @@ -1332,14 +1219,10 @@ import java.util.concurrent.atomic.AtomicBoolean; } break; case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: - final int a2dpCodec; final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; synchronized (mDeviceStateLock) { - a2dpCodec = mBtHelper.getA2dpCodec(btDevice); - // TODO: name of method being called on AudioDeviceInventory is currently - // misleading (config change vs active device change), to be - // reconciliated once the BT side has been updated. - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( + final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice); + mDeviceInventory.onBluetoothA2dpDeviceConfigChange( new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec), BtHelper.EVENT_DEVICE_CONFIG_CHANGE); } @@ -1392,96 +1275,47 @@ import java.util.concurrent.atomic.AtomicBoolean; mDeviceInventory.onToggleHdmi(); } break; - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, - BtHelper.EVENT_ACTIVE_DEVICE_CHANGE); - } - break; - case MSG_DISCONNECT_A2DP: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectA2dp(); - } - break; - case MSG_DISCONNECT_A2DP_SINK: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectA2dpSink(); - } - break; - case MSG_DISCONNECT_BT_HEARING_AID: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectHearingAid(); - } - break; - case MSG_DISCONNECT_BT_HEADSET: - synchronized (mSetModeLock) { + case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE: + if (msg.arg1 != BluetoothProfile.HEADSET) { synchronized (mDeviceStateLock) { - mBtHelper.disconnectHeadset(); + mDeviceInventory.onBtProfileDisconnected(msg.arg1); + } + } else { + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectHeadset(); + } } } break; - case MSG_DISCONNECT_BT_LE_AUDIO: - synchronized(mDeviceStateLock) { - mDeviceInventory.disconnectLeAudio(); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: - synchronized (mDeviceStateLock) { - mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK: - synchronized (mDeviceStateLock) { - mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID: - synchronized (mDeviceStateLock) { - mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj); - } - break; - - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO: - synchronized(mDeviceStateLock) { - mBtHelper.onLeAudioProfileConnected((BluetoothLeAudio) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: - synchronized (mSetModeLock) { + case MSG_IL_BT_SERVICE_CONNECTED_PROFILE: + if (msg.arg1 != BluetoothProfile.HEADSET) { synchronized (mDeviceStateLock) { - mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + mBtHelper.onBtProfileConnected(msg.arg1, (BluetoothProfile) msg.obj); + } + } else { + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + } } } break; - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: { - final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj; + case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: { + final BtDeviceInfo info = (BtDeviceInfo) msg.obj; + if (info.mDevice == null) break; AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "msg: setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent " + "msg: onBluetoothActiveDeviceChange " + " state=" + info.mState // only querying address as this is the only readily available // field on the device + " addr=" + info.mDevice.getAddress() - + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy - + " vol=" + info.mVolume)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothA2dpDeviceConnectionState( - info.mDevice, info.mState, info.mProfile, info.mSupprNoisy, - AudioSystem.DEVICE_NONE, info.mVolume); - } - } break; - case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: { - final HearingAidDeviceConnectionInfo info = - (HearingAidDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "msg: setHearingAidDeviceConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() + + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy - + " src=" + info.mEventSource)).printLog(TAG)); + + " src=" + info.mEventSource + )).printLog(TAG)); synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothHearingAidDeviceConnectionState( - info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice); + mDeviceInventory.setBluetoothActiveDevice(info); } } break; case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: { @@ -1524,31 +1358,6 @@ import java.util.concurrent.atomic.AtomicBoolean; final int capturePreset = msg.arg1; mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset); } break; - case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: { - final LeAudioDeviceConnectionInfo info = - (LeAudioDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "setLeAudioDeviceOutConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() - + " supprNoisy=" + info.mSupprNoisy - + " src=" + info.mEventSource)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothLeAudioOutDeviceConnectionState( - info.mDevice, info.mState, info.mSupprNoisy); - } - } break; - case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: { - final LeAudioDeviceConnectionInfo info = - (LeAudioDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "setLeAudioDeviceInConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() - + " src=" + info.mEventSource)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothLeAudioInDeviceConnectionState(info.mDevice, - info.mState); - } - } break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -1579,8 +1388,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IIL_SET_FORCE_USE = 4; private static final int MSG_IIL_SET_FORCE_BT_A2DP_USE = 5; private static final int MSG_TOGGLE_HDMI = 6; - private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7; - private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8; + private static final int MSG_L_SET_BT_ACTIVE_DEVICE = 7; private static final int MSG_BT_HEADSET_CNCT_FAILED = 9; private static final int MSG_IL_BTA2DP_TIMEOUT = 10; @@ -1593,25 +1401,11 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15; private static final int MSG_I_SET_MODE_OWNER_PID = 16; - // process active A2DP device change, obj is BtHelper.BluetoothA2dpDeviceInfo - private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18; - - private static final int MSG_DISCONNECT_A2DP = 19; - private static final int MSG_DISCONNECT_A2DP_SINK = 20; - private static final int MSG_DISCONNECT_BT_HEARING_AID = 21; - private static final int MSG_DISCONNECT_BT_HEADSET = 22; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP = 23; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26; - - // process change of state, obj is BtHelper.BluetoothA2dpDeviceInfo - private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED = 27; - private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED = 28; + private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22; + private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23; // process external command to (dis)connect an A2DP device, obj is BtDeviceConnectionInfo - private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION = 29; - private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION = 30; + private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT = 29; // process external command to (dis)connect a hearing aid device private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31; @@ -1630,33 +1424,21 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40; private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41; - private static final int MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE = 42; - private static final int MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE = 43; - private static final int MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT = 44; - private static final int MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT = 45; + private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45; + // // process set volume for Le Audio, obj is BleVolumeInfo private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO = 47; - private static final int MSG_DISCONNECT_BT_LE_AUDIO = 48; - private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: case MSG_TOGGLE_HDMI: - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: + case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: case MSG_CHECK_MUTE_MUSIC: - case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: - case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: return true; default: return false; @@ -1739,14 +1521,10 @@ import java.util.concurrent.atomic.AtomicBoolean; long time = SystemClock.uptimeMillis() + delay; switch (msg) { - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: if (sLastDeviceConnectMsgTime >= time) { // add a little delay to make sure messages are ordered as expected time = sLastDeviceConnectMsgTime + 30; @@ -1765,14 +1543,9 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final Set<Integer> MESSAGES_MUTE_MUSIC; static { MESSAGES_MUTE_MUSIC = new HashSet<>(); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE); + MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE); MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION); - MESSAGES_MUTE_MUSIC.add(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT); + MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT); MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE); MESSAGES_MUTE_MUSIC.add(MSG_REPORT_NEW_ROUTES_A2DP); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 6c3c736aeb93..0a114b924063 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -16,11 +16,8 @@ package com.android.server.audio; import android.annotation.NonNull; -import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.Intent; import android.media.AudioDeviceAttributes; @@ -286,186 +283,102 @@ public class AudioDeviceInventory { } } - // only public for mocking/spying @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - @VisibleForTesting - public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, - @AudioService.BtProfileConnectionState int state) { - final BluetoothDevice btDevice = btInfo.getBtDevice(); - int a2dpVolume = btInfo.getVolume(); - if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state=" - + state + " vol=" + a2dpVolume); - } - String address = btDevice.getAddress(); - if (address == null) { - address = ""; - } - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - - final @AudioSystem.AudioFormatNativeEnumForBtCodec int a2dpCodec = btInfo.getCodec(); - - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "A2DP sink connected: device addr=" + address + " state=" + state - + " codec=" + AudioSystem.audioFormatToString(a2dpCodec) - + " vol=" + a2dpVolume)); - - new MediaMetrics.Item(mMetricsId + "a2dp") - .set(MediaMetrics.Property.ADDRESS, address) - .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec)) - .set(MediaMetrics.Property.EVENT, "onSetA2dpSinkConnectionState") - .set(MediaMetrics.Property.INDEX, a2dpVolume) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .record(); - - synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - btDevice.getAddress()); - final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - - if (isConnected) { - if (state == BluetoothProfile.STATE_CONNECTED) { - // device is already connected, but we are receiving a connection again, - // it could be for a codec change - if (a2dpCodec != di.mDeviceCodecFormat) { - mDeviceBroker.postBluetoothA2dpDeviceConfigChange(btDevice); - } - } else { - makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat); - } - } else if (state == BluetoothProfile.STATE_CONNECTED) { - // device is not already connected - if (a2dpVolume != -1) { - mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, - // convert index to internal representation in VolumeStreamState - a2dpVolume * 10, - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState"); - } - makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice), - "onSetA2dpSinkConnectionState", a2dpCodec); - } - } - } - - /*package*/ void onSetA2dpSourceConnectionState( - @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int state) { - final BluetoothDevice btDevice = btInfo.getBtDevice(); + void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) { if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state=" - + state); + Log.d(TAG, "onSetBtActiveDevice" + + " btDevice=" + btInfo.mDevice + + " profile=" + BluetoothProfile.getProfileName(btInfo.mProfile) + + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState)); } - String address = btDevice.getAddress(); + String address = btInfo.mDevice.getAddress(); if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } - synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey( - AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address); - final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - - new MediaMetrics.Item(mMetricsId + "onSetA2dpSourceConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) - .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .record(); - - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeA2dpSrcUnavailable(address); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeA2dpSrcAvailable(address); - } - } - } - - /*package*/ void onSetHearingAidConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType) { - String address = btDevice.getAddress(); - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onSetHearingAidConnectionState addr=" + address)); + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent("BT connected:" + + " addr=" + address + + " profile=" + btInfo.mProfile + + " state=" + btInfo.mState + + " codec=" + AudioSystem.audioFormatToString(btInfo.mCodec))); - new MediaMetrics.Item(mMetricsId + "onSetHearingAidConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) + new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice") + .set(MediaMetrics.Property.STATUS, btInfo.mProfile) .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + AudioSystem.getDeviceName(btInfo.mAudioSystemDevice)) + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ENCODING, + AudioSystem.audioFormatToString(btInfo.mCodec)) + .set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice") .set(MediaMetrics.Property.STREAM_TYPE, AudioSystem.streamToString(streamType)) + .set(MediaMetrics.Property.STATE, + btInfo.mState == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) .record(); synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, - btDevice.getAddress()); + final String key = DeviceInfo.makeDeviceListKey(btInfo.mAudioSystemDevice, address); final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeHearingAidDeviceUnavailable(address); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeHearingAidDeviceAvailable(address, BtHelper.getName(btDevice), streamType, - "onSetHearingAidConnectionState"); - } - } - } + final boolean isConnected = di != null; - /*package*/ void onSetLeAudioConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType, int device) { - String address = btDevice.getAddress(); - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onSetLeAudioConnectionState addr=" + address)); + final boolean switchToUnavailable = isConnected + && btInfo.mState != BluetoothProfile.STATE_CONNECTED; + final boolean switchToAvailable = !isConnected + && btInfo.mState == BluetoothProfile.STATE_CONNECTED; - synchronized (mDevicesLock) { - DeviceInfo di = null; - boolean isConnected = false; - - String key = DeviceInfo.makeDeviceListKey(device, btDevice.getAddress()); - di = mConnectedDevices.get(key); - isConnected = di != null; - - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeLeAudioDeviceUnavailable(address, device); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeLeAudioDeviceAvailable(address, BtHelper.getName(btDevice), streamType, - device, "onSetLeAudioConnectionState"); + switch (btInfo.mProfile) { + case BluetoothProfile.A2DP_SINK: + if (switchToUnavailable) { + makeA2dpSrcUnavailable(address); + } else if (switchToAvailable) { + makeA2dpSrcAvailable(address); + } + break; + case BluetoothProfile.A2DP: + if (switchToUnavailable) { + makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat); + } else if (switchToAvailable) { + // device is not already connected + if (btInfo.mVolume != -1) { + mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, + // convert index to internal representation in VolumeStreamState + btInfo.mVolume * 10, btInfo.mAudioSystemDevice, + "onSetBtActiveDevice"); + } + makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + "onSetBtActiveDevice", btInfo.mCodec); + } + break; + case BluetoothProfile.HEARING_AID: + if (switchToUnavailable) { + makeHearingAidDeviceUnavailable(address); + } else if (switchToAvailable) { + makeHearingAidDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + streamType, "onSetBtActiveDevice"); + } + break; + case BluetoothProfile.LE_AUDIO: + if (switchToUnavailable) { + makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice); + } else if (switchToAvailable) { + makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + streamType, btInfo.mAudioSystemDevice, "onSetBtActiveDevice"); + } + break; + default: throw new IllegalArgumentException("Invalid profile " + + BluetoothProfile.getProfileName(btInfo.mProfile)); } } } - /*package*/ void onSetLeAudioOutConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType) { - // TODO: b/198610537 clarify DEVICE_OUT_BLE_HEADSET vs DEVICE_OUT_BLE_SPEAKER criteria - onSetLeAudioConnectionState(btDevice, state, streamType, - AudioSystem.DEVICE_OUT_BLE_HEADSET); - } - - /*package*/ void onSetLeAudioInConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state) { - onSetLeAudioConnectionState(btDevice, state, AudioSystem.STREAM_DEFAULT, - AudioSystem.DEVICE_IN_BLE_HEADSET); - } @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - /*package*/ void onBluetoothA2dpActiveDeviceChange( + /*package*/ void onBluetoothA2dpDeviceConfigChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId - + "onBluetoothA2dpActiveDeviceChange") + + "onBluetoothA2dpDeviceConfigChange") .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event)); final BluetoothDevice btDevice = btInfo.getBtDevice(); @@ -474,7 +387,7 @@ public class AudioDeviceInventory { return; } if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onBluetoothA2dpActiveDeviceChange btDevice=" + btDevice); + Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice); } int a2dpVolume = btInfo.getVolume(); @AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec(); @@ -484,11 +397,11 @@ public class AudioDeviceInventory { address = ""; } AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onBluetoothA2dpActiveDeviceChange addr=" + address + "onBluetoothA2dpDeviceConfigChange addr=" + address + " event=" + BtHelper.a2dpDeviceEventToString(event))); synchronized (mDevicesLock) { - if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) { + if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "A2dp config change ignored (scheduled connection change)") .printLog(TAG)); @@ -500,7 +413,7 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { - Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpActiveDeviceChange"); + Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange"); mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record(); return; } @@ -518,7 +431,7 @@ public class AudioDeviceInventory { // convert index to internal representation in VolumeStreamState a2dpVolume * 10, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - "onBluetoothA2dpActiveDeviceChange"); + "onBluetoothA2dpDeviceConfigChange"); } } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { if (di.mDeviceCodecFormat != a2dpCodec) { @@ -539,10 +452,9 @@ public class AudioDeviceInventory { int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC); // force A2DP device disconnection in case of error so that AudioService state is // consistent with audio policy manager state - setBluetoothA2dpDeviceConnectionState( - btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP, - false /* suppressNoisyIntent */, musicDevice, - -1 /* a2dpVolume */); + setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice, + BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED, + musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); } else { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "APM handleDeviceConfigChange success for A2DP device addr=" + address @@ -828,7 +740,7 @@ public class AudioDeviceInventory { } - /*package*/ void disconnectA2dp() { + private void disconnectA2dp() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices @@ -850,7 +762,7 @@ public class AudioDeviceInventory { } } - /*package*/ void disconnectA2dpSink() { + private void disconnectA2dpSink() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices @@ -865,7 +777,7 @@ public class AudioDeviceInventory { } } - /*package*/ void disconnectHearingAid() { + private void disconnectHearingAid() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_HEARING_AID devices @@ -887,6 +799,28 @@ public class AudioDeviceInventory { } } + /*package*/ synchronized void onBtProfileDisconnected(int profile) { + switch (profile) { + case BluetoothProfile.A2DP: + disconnectA2dp(); + break; + case BluetoothProfile.A2DP_SINK: + disconnectA2dpSink(); + break; + case BluetoothProfile.HEARING_AID: + disconnectHearingAid(); + break; + case BluetoothProfile.LE_AUDIO: + disconnectLeAudio(); + break; + default: + // Not a valid profile to disconnect + Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect " + + BluetoothProfile.getProfileName(profile)); + break; + } + } + /*package*/ void disconnectLeAudio() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); @@ -934,46 +868,39 @@ public class AudioDeviceInventory { // only public for mocking/spying @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @VisibleForTesting - public void setBluetoothA2dpDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) { + public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) { int delay; - if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) { - throw new IllegalArgumentException("invalid profile " + profile); - } synchronized (mDevicesLock) { - if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) { + if (!info.mSupprNoisy + && ((info.mProfile == BluetoothProfile.LE_AUDIO && info.mIsLeOutput) + || info.mProfile == BluetoothProfile.HEARING_AID + || info.mProfile == BluetoothProfile.A2DP)) { @AudioService.ConnectionState int asState = - (state == BluetoothA2dp.STATE_CONNECTED) + (info.mState == BluetoothProfile.STATE_CONNECTED) ? AudioService.CONNECTION_STATE_CONNECTED : AudioService.CONNECTION_STATE_DISCONNECTED; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - asState, musicDevice); + delay = checkSendBecomingNoisyIntentInt(info.mAudioSystemDevice, asState, + info.mMusicDevice); } else { delay = 0; } - final int a2dpCodec = mDeviceBroker.getA2dpCodec(device); - if (AudioService.DEBUG_DEVICES) { - Log.i(TAG, "setBluetoothA2dpDeviceConnectionState device: " + device - + " state: " + state + " delay(ms): " + delay - + " codec:" + Integer.toHexString(a2dpCodec) - + " suppressNoisyIntent: " + suppressNoisyIntent); + Log.i(TAG, "setBluetoothActiveDevice device: " + info.mDevice + + " profile: " + BluetoothProfile.getProfileName(info.mProfile) + + " state: " + BluetoothProfile.getConnectionStateName(info.mState) + + " delay(ms): " + delay + + " codec:" + Integer.toHexString(info.mCodec) + + " suppressNoisyIntent: " + info.mSupprNoisy); } - - final BtHelper.BluetoothA2dpDeviceInfo a2dpDeviceInfo = - new BtHelper.BluetoothA2dpDeviceInfo(device, a2dpVolume, a2dpCodec); - if (profile == BluetoothProfile.A2DP) { - mDeviceBroker.postA2dpSinkConnection(state, - a2dpDeviceInfo, - delay); - } else { //profile == BluetoothProfile.A2DP_SINK - mDeviceBroker.postA2dpSourceConnection(state, - a2dpDeviceInfo, - delay); + mDeviceBroker.postBluetoothActiveDevice(info, delay); + if (info.mProfile == BluetoothProfile.HEARING_AID + && info.mState == BluetoothProfile.STATE_CONNECTED) { + mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE, + "HEARING_AID set to CONNECTED"); } } + return delay; } /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, @@ -987,50 +914,6 @@ public class AudioDeviceInventory { } } - /*package*/ int setBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice) { - int delay; - synchronized (mDevicesLock) { - if (!suppressNoisyIntent) { - int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID, - intState, musicDevice); - } else { - delay = 0; - } - mDeviceBroker.postSetHearingAidConnectionState(state, device, delay); - if (state == BluetoothHearingAid.STATE_CONNECTED) { - mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE, - "HEARING_AID set to CONNECTED"); - } - return delay; - } - } - - /*package*/ int setBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent) { - synchronized (mDevicesLock) { - /* Active device become null and it's previous device is not connected anymore */ - int delay = 0; - if (!suppressNoisyIntent) { - int intState = (state == BluetoothLeAudio.STATE_CONNECTED) ? 1 : 0; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLE_HEADSET, - intState, AudioSystem.DEVICE_NONE); - } - mDeviceBroker.postSetLeAudioOutConnectionState(state, device, delay); - return delay; - } - } - - /*package*/ void setBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state) { - synchronized (mDevicesLock) { - mDeviceBroker.postSetLeAudioInConnectionState(state, device); - } - } - //------------------------------------------------------------------- // Internal utilities diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d75f21c4ef85..51784bbce2e0 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -83,6 +83,7 @@ import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -309,8 +310,8 @@ public class AudioService extends IAudioService.Stub private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35; private static final int MSG_UPDATE_AUDIO_MODE = 36; private static final int MSG_RECORDING_CONFIG_CHANGE = 37; - private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38; - private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39; + private static final int MSG_BT_DEV_CHANGED = 38; + private static final int MSG_DISPATCH_AUDIO_MODE = 40; // start of messages handled under wakelock @@ -6266,77 +6267,44 @@ public class AudioService extends IAudioService.Stub public @interface BtProfileConnectionState {} /** - * See AudioManager.setBluetoothHearingAidDeviceConnectionState() - */ - public void setBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice) - { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); - } - mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( - device, state, suppressNoisyIntent, musicDevice, "AudioService"); - } - - private void setBluetoothLeAudioDeviceConnectionState(@NonNull BluetoothDevice device, - @BtProfileConnectionState int state) { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); - } - } - - /** - * See AudioManager.setBluetoothLeAudioOutDeviceConnectionState() + * @hide + * The profiles that can be used with AudioService.handleBluetoothActiveDeviceChanged() */ - public void setBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - boolean suppressNoisyIntent) { - setBluetoothLeAudioDeviceConnectionState(device, state); - mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState(device, state, - suppressNoisyIntent, "AudioService"); - } + @IntDef({ + BluetoothProfile.HEARING_AID, + BluetoothProfile.A2DP, + BluetoothProfile.A2DP_SINK, + BluetoothProfile.LE_AUDIO, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BtProfile {} - /** - * See AudioManager.setBluetoothLeAudioInDeviceConnectionState() - */ - public void setBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state) { - setBluetoothLeAudioDeviceConnectionState(device, state); - mDeviceBroker.postBluetoothLeAudioInDeviceConnectionState(device, state, "AudioService"); - } /** - * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() + * See AudioManager.handleBluetoothActiveDeviceChanged(...) */ - public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int a2dpVolume) { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); + public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice, + BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Bluetooth is the only caller allowed"); } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); + if (info == null) { + throw new IllegalArgumentException("Illegal null BtProfileConnectionInfo for device " + + previousDevice + " -> " + newDevice); } - - AudioDeviceBroker.BtDeviceConnectionInfo info = - new AudioDeviceBroker.BtDeviceConnectionInfo(device, state, - profile, suppressNoisyIntent, a2dpVolume); - sendMsg(mAudioHandler, MSG_SET_A2DP_DEV_CONNECTION_STATE, SENDMSG_QUEUE, - 0 /*arg1*/, 0 /*arg2*/, - /*obj*/ info, 0 /*delay*/); + final int profile = info.getProfile(); + if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK + && profile != BluetoothProfile.LE_AUDIO + && profile != BluetoothProfile.HEARING_AID) { + throw new IllegalArgumentException("Illegal BluetoothProfile profile for device " + + previousDevice + " -> " + newDevice + ". Got: " + profile); + } + AudioDeviceBroker.BtDeviceChangedData data = + new AudioDeviceBroker.BtDeviceChangedData(newDevice, previousDevice, info, + "AudioService"); + sendMsg(mAudioHandler, MSG_BT_DEV_CHANGED, SENDMSG_QUEUE, 0, 0, + /*obj*/ data, /*delay*/ 0); } /** only public for mocking/spying, do not call outside of AudioService */ @@ -6345,19 +6313,6 @@ public class AudioService extends IAudioService.Stub mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute); } - /** - * See AudioManager.handleBluetoothA2dpDeviceConfigChange() - * @param device - */ - public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) - { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - sendMsg(mAudioHandler, MSG_A2DP_DEV_CONFIG_CHANGE, SENDMSG_QUEUE, 0, 0, - /*obj*/ device, /*delay*/ 0); - } - private static final Set<Integer> DEVICE_MEDIA_UNMUTED_ON_PLUG_SET; static { DEVICE_MEDIA_UNMUTED_ON_PLUG_SET = new HashSet<>(); @@ -7709,13 +7664,9 @@ public class AudioService extends IAudioService.Stub } break; - case MSG_SET_A2DP_DEV_CONNECTION_STATE: - mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - (AudioDeviceBroker.BtDeviceConnectionInfo) msg.obj); - break; - - case MSG_A2DP_DEV_CONFIG_CHANGE: - mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj); + case MSG_BT_DEV_CHANGED: + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + (AudioDeviceBroker.BtDeviceChangedData) msg.obj); break; case MSG_DISPATCH_AUDIO_MODE: diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index c924fde23f9d..9273a5d5cf9c 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.os.Binder; import android.os.UserHandle; import android.provider.Settings; @@ -451,11 +452,11 @@ public class BtHelper { } /*package*/ synchronized void disconnectAllBluetoothProfiles() { - mDeviceBroker.postDisconnectA2dp(); - mDeviceBroker.postDisconnectA2dpSink(); - mDeviceBroker.postDisconnectHeadset(); - mDeviceBroker.postDisconnectHearingAid(); - mDeviceBroker.postDisconnectLeAudio(); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP_SINK); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEADSET); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEARING_AID); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.LE_AUDIO); } // @GuardedBy("AudioDeviceBroker.mSetModeLock") @@ -474,63 +475,32 @@ public class BtHelper { mBluetoothHeadset = null; } - /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) { - mA2dp = a2dp; - final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices(); - if (deviceList.isEmpty()) { + /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.HEADSET) { + onHeadsetProfileConnected((BluetoothHeadset) proxy); return; } - final BluetoothDevice btDevice = deviceList.get(0); - // the device is guaranteed CONNECTED - mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(btDevice, - BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, - true, -1)); - } - - /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) { - final List<BluetoothDevice> deviceList = profile.getConnectedDevices(); - if (deviceList.isEmpty()) { - return; + if (profile == BluetoothProfile.A2DP) { + mA2dp = (BluetoothA2dp) proxy; + } else if (profile == BluetoothProfile.LE_AUDIO) { + mLeAudio = (BluetoothLeAudio) proxy; } - final BluetoothDevice btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - profile.getConnectionState(btDevice); - mDeviceBroker.postSetA2dpSourceConnectionState( - state, new BluetoothA2dpDeviceInfo(btDevice)); - } - - /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) { - mHearingAid = hearingAid; - final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices(); + final List<BluetoothDevice> deviceList = proxy.getConnectedDevices(); if (deviceList.isEmpty()) { return; } final BluetoothDevice btDevice = deviceList.get(0); final @BluetoothProfile.BtProfileState int state = - mHearingAid.getConnectionState(btDevice); - mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( - btDevice, state, - /*suppressNoisyIntent*/ false, - /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, - /*eventSource*/ "mBluetoothProfileServiceListener"); - } - - /*package*/ synchronized void onLeAudioProfileConnected(BluetoothLeAudio leAudio) { - mLeAudio = leAudio; - final List<BluetoothDevice> deviceList = mLeAudio.getConnectedDevices(); - if (deviceList.isEmpty()) { - return; + proxy.getConnectionState(btDevice); + if (state == BluetoothProfile.STATE_CONNECTED) { + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(btDevice, null, + new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); + } else { + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(null, btDevice, + new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); } - - final BluetoothDevice btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - mLeAudio.getConnectionState(btDevice); - mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState( - btDevice, state, - /*suppressNoisyIntent*/ false, - /*musicDevice android.media.AudioSystem.DEVICE_NONE,*/ - /*eventSource*/ "mBluetoothProfileServiceListener"); } // @GuardedBy("AudioDeviceBroker.mSetModeLock") @@ -677,36 +647,16 @@ public class BtHelper { public void onServiceConnected(int profile, BluetoothProfile proxy) { switch(profile) { case BluetoothProfile.A2DP: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting A2DP profile")); - mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy); - break; - case BluetoothProfile.A2DP_SINK: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting A2DP_SINK profile")); - mDeviceBroker.postBtA2dpSinkProfileConnected(proxy); - break; - case BluetoothProfile.HEADSET: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting HEADSET profile")); - mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy); - break; - case BluetoothProfile.HEARING_AID: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting HEARING_AID profile")); - mDeviceBroker.postBtHearingAidProfileConnected( - (BluetoothHearingAid) proxy); - break; - case BluetoothProfile.LE_AUDIO: AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting LE_AUDIO profile")); - mDeviceBroker.postBtLeAudioProfileConnected( - (BluetoothLeAudio) proxy); + "BT profile service: connecting " + + BluetoothProfile.getProfileName(profile) + " profile")); + mDeviceBroker.postBtProfileConnected(profile, proxy); break; + default: break; } @@ -715,22 +665,11 @@ public class BtHelper { switch (profile) { case BluetoothProfile.A2DP: - mDeviceBroker.postDisconnectA2dp(); - break; - case BluetoothProfile.A2DP_SINK: - mDeviceBroker.postDisconnectA2dpSink(); - break; - case BluetoothProfile.HEADSET: - mDeviceBroker.postDisconnectHeadset(); - break; - case BluetoothProfile.HEARING_AID: - mDeviceBroker.postDisconnectHearingAid(); - break; case BluetoothProfile.LE_AUDIO: - mDeviceBroker.postDisconnectLeAudio(); + mDeviceBroker.postBtProfileDisconnected(profile); break; default: diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING index 90246f849491..5a6c6a51bd97 100644 --- a/services/core/java/com/android/server/audio/TEST_MAPPING +++ b/services/core/java/com/android/server/audio/TEST_MAPPING @@ -1,13 +1,13 @@ { "presubmit-large": [ { - "name": "CtsMediaTestCases", + "name": "CtsMediaAudioTestCases", "options": [ { - "include-filter": "android.media.cts.AudioManagerTest" + "include-filter": "android.media.audio.cts.AudioManagerTest" }, { - "include-filter": "android.media.cts.AudioFocusTest" + "include-filter": "android.media.audio.cts.AudioFocusTest" } ] } diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 0cd2e3d0ff59..c97ad55ceeec 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -60,6 +60,7 @@ import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; @@ -70,6 +71,7 @@ import com.android.internal.util.ArrayUtils; import com.android.server.SystemService; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -81,6 +83,8 @@ public class AuthService extends SystemService { private static final String SETTING_HIDL_DISABLED = "com.android.server.biometrics.AuthService.hidlDisabled"; private static final int DEFAULT_HIDL_DISABLED = 0; + private static final String SYSPROP_FIRST_API_LEVEL = "ro.board.first_api_level"; + private static final String SYSPROP_API_LEVEL = "ro.board.api_level"; private final Injector mInjector; @@ -623,7 +627,16 @@ public class AuthService extends SystemService { final SensorConfig[] hidlConfigs; if (!mInjector.isHidlDisabled(getContext())) { - final String[] configStrings = mInjector.getConfiguration(getContext()); + final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0); + final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel); + String[] configStrings = mInjector.getConfiguration(getContext()); + if (configStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) { + // For backwards compatibility with R where biometrics could work without being + // configured in config_biometric_sensors. In the absence of a vendor provided + // configuration, we assume the weakest biometric strength (i.e. convenience). + Slog.w(TAG, "Found R vendor partition without config_biometric_sensors"); + configStrings = generateRSdkCompatibleConfiguration(); + } hidlConfigs = new SensorConfig[configStrings.length]; for (int i = 0; i < configStrings.length; ++i) { hidlConfigs[i] = new SensorConfig(configStrings[i]); @@ -639,6 +652,31 @@ public class AuthService extends SystemService { } /** + * Generates an array of string configs with entries that correspond to the biometric features + * declared on the device. Returns an empty array if no biometric features are declared. + * Biometrics are assumed to be of the weakest strength class, i.e. convenience. + */ + private @NonNull String[] generateRSdkCompatibleConfiguration() { + final PackageManager pm = getContext().getPackageManager(); + final ArrayList<String> modalities = new ArrayList<>(); + if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FINGERPRINT)); + } + if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FACE)); + } + final String strength = String.valueOf(Authenticators.BIOMETRIC_CONVENIENCE); + final String[] configStrings = new String[modalities.size()]; + for (int i = 0; i < modalities.size(); ++i) { + final String id = String.valueOf(i); + final String modality = modalities.get(i); + configStrings[i] = String.join(":" /* delimiter */, id, modality, strength); + } + Slog.d(TAG, "Generated config_biometric_sensors: " + Arrays.toString(configStrings)); + return configStrings; + } + + /** * Registers HIDL and AIDL authenticators for all of the available modalities. * * @param hidlSensors Array of {@link SensorConfig} configuration for all of the HIDL sensors diff --git a/services/core/java/com/android/server/compat/OWNERS b/services/core/java/com/android/server/compat/OWNERS index cfd0a4b079ad..ee3086ab2fdb 100644 --- a/services/core/java/com/android/server/compat/OWNERS +++ b/services/core/java/com/android/server/compat/OWNERS @@ -1,6 +1 @@ -# Use this reviewer by default. -platform-compat-eng+reviews@google.com - -andreionea@google.com -mathewi@google.com -satayev@google.com +include tools/platform-compat:/OWNERS diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 6ea84ce35002..ee5bda3abc91 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -171,25 +171,28 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } private NetworkMetrics getMetricsForNetwork(long timeMs, int netId) { - collectPendingMetricsSnapshot(timeMs); NetworkMetrics metrics = mNetworkMetrics.get(netId); - if (metrics == null) { - // TODO: allow to change transport for a given netid. - metrics = new NetworkMetrics(netId, getTransports(netId), mConnectTb); + final NetworkCapabilities nc = mCallback.getNetworkCapabilities(netId); + final long transports = (nc != null) ? BitUtils.packBits(nc.getTransportTypes()) : 0; + final boolean forceCollect = + (metrics != null && nc != null && metrics.transports != transports); + collectPendingMetricsSnapshot(timeMs, forceCollect); + if (metrics == null || forceCollect) { + metrics = new NetworkMetrics(netId, transports, mConnectTb); mNetworkMetrics.put(netId, metrics); } return metrics; } private NetworkMetricsSnapshot[] getNetworkMetricsSnapshots() { - collectPendingMetricsSnapshot(System.currentTimeMillis()); + collectPendingMetricsSnapshot(System.currentTimeMillis(), false /* forceCollect */); return mNetworkMetricsSnapshots.toArray(); } - private void collectPendingMetricsSnapshot(long timeMs) { + private void collectPendingMetricsSnapshot(long timeMs, boolean forceCollect) { // Detects time differences larger than the snapshot collection period. // This is robust against clock jumps and long inactivity periods. - if (Math.abs(timeMs - mLastSnapshot) <= METRICS_SNAPSHOT_SPAN_MS) { + if (!forceCollect && Math.abs(timeMs - mLastSnapshot) <= METRICS_SNAPSHOT_SPAN_MS) { return; } mLastSnapshot = projectSnapshotTime(timeMs); @@ -394,14 +397,6 @@ public class NetdEventListenerService extends INetdEventListener.Stub { return list; } - private long getTransports(int netId) { - final NetworkCapabilities nc = mCallback.getNetworkCapabilities(netId); - if (nc == null) { - return 0; - } - return BitUtils.packBits(nc.getTransportTypes()); - } - /** Helper class for buffering summaries of NetworkMetrics at regular time intervals */ static class NetworkMetricsSnapshot { diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS index 7311eee32a4c..62c5737a2e8e 100644 --- a/services/core/java/com/android/server/connectivity/OWNERS +++ b/services/core/java/com/android/server/connectivity/OWNERS @@ -1,8 +1,2 @@ set noparent - -codewiz@google.com -ek@google.com -jchalard@google.com -lorenzo@google.com -reminv@google.com -satk@google.com +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking diff --git a/services/core/java/com/android/server/health/HealthHalCallbackHidl.java b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java new file mode 100644 index 000000000000..7a6698085c0d --- /dev/null +++ b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java @@ -0,0 +1,117 @@ +/* + * 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.health; + +import static android.hardware.health.Translate.h2aTranslate; + +import android.annotation.NonNull; +import android.hardware.health.V2_0.IHealth; +import android.hardware.health.V2_0.Result; +import android.hardware.health.V2_1.BatteryCapacityLevel; +import android.hardware.health.V2_1.Constants; +import android.hardware.health.V2_1.IHealthInfoCallback; +import android.os.RemoteException; +import android.os.Trace; +import android.util.Slog; + +/** + * On service registration, {@link HealthServiceWrapperHidl.Callback#onRegistration} is called, + * which registers {@code this}, a {@link IHealthInfoCallback}, to the health service. + * + * <p>When the health service has updates to health info, {@link HealthInfoCallback#update} is + * called. + * + * @hide + */ +class HealthHalCallbackHidl extends IHealthInfoCallback.Stub + implements HealthServiceWrapperHidl.Callback { + + private static final String TAG = HealthHalCallbackHidl.class.getSimpleName(); + + private static void traceBegin(String name) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name); + } + + private static void traceEnd() { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + private HealthInfoCallback mCallback; + + HealthHalCallbackHidl(@NonNull HealthInfoCallback callback) { + mCallback = callback; + } + + @Override + public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) { + android.hardware.health.V2_1.HealthInfo propsLatest = + new android.hardware.health.V2_1.HealthInfo(); + propsLatest.legacy = props; + + propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED; + propsLatest.batteryChargeTimeToFullNowSeconds = + Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED; + + mCallback.update(h2aTranslate(propsLatest)); + } + + @Override + public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) { + mCallback.update(h2aTranslate(props)); + } + + // on new service registered + @Override + public void onRegistration(IHealth oldService, IHealth newService, String instance) { + if (newService == null) return; + + traceBegin("HealthUnregisterCallback"); + try { + if (oldService != null) { + int r = oldService.unregisterCallback(this); + if (r != Result.SUCCESS) { + Slog.w( + TAG, + "health: cannot unregister previous callback: " + Result.toString(r)); + } + } + } catch (RemoteException ex) { + Slog.w( + TAG, + "health: cannot unregister previous callback (transaction error): " + + ex.getMessage()); + } finally { + traceEnd(); + } + + traceBegin("HealthRegisterCallback"); + try { + int r = newService.registerCallback(this); + if (r != Result.SUCCESS) { + Slog.w(TAG, "health: cannot register callback: " + Result.toString(r)); + return; + } + // registerCallback does NOT guarantee that update is called + // immediately, so request a manual update here. + newService.update(); + } catch (RemoteException ex) { + Slog.e(TAG, "health: cannot register callback (transaction error): " + ex.getMessage()); + } finally { + traceEnd(); + } + } +} diff --git a/services/core/java/com/android/server/health/HealthInfoCallback.java b/services/core/java/com/android/server/health/HealthInfoCallback.java new file mode 100644 index 000000000000..c2a77fc862fa --- /dev/null +++ b/services/core/java/com/android/server/health/HealthInfoCallback.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 com.android.server.health; + +/** + * A wrapper over HIDL / AIDL IHealthInfoCallback. + * + * @hide + */ +public interface HealthInfoCallback { + /** + * Signals to the client that health info is changed. + * + * @param props the new health info. + */ + void update(android.hardware.health.HealthInfo props); +} diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java new file mode 100644 index 000000000000..629011a86bb4 --- /dev/null +++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java @@ -0,0 +1,119 @@ +/* + * 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.health; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.health.HealthInfo; +import android.hardware.health.IHealth; +import android.hardware.health.IHealthInfoCallback; +import android.os.RemoteException; +import android.os.Trace; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * On service registration, {@link #onRegistration} is called, which registers {@code this}, an + * {@link IHealthInfoCallback}, to the health service. + * + * <p>When the health service has updates to health info via {@link IHealthInfoCallback}, {@link + * HealthInfoCallback#update} is called. + * + * <p>AIDL variant of {@link HealthHalCallbackHidl}. + * + * @hide + */ +// It is made public so Mockito can access this class. It should have been package private if not +// for testing. +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public class HealthRegCallbackAidl { + private static final String TAG = "HealthRegCallbackAidl"; + private final HealthInfoCallback mServiceInfoCallback; + private final IHealthInfoCallback mHalInfoCallback = new HalInfoCallback(); + + HealthRegCallbackAidl(@Nullable HealthInfoCallback healthInfoCallback) { + mServiceInfoCallback = healthInfoCallback; + } + + /** + * Called when the service manager sees {@code newService} replacing {@code oldService}. + * This unregisters the health info callback from the old service (ignoring errors), then + * registers the health info callback to the new service. + * + * @param oldService the old IHealth service + * @param newService the new IHealth service + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void onRegistration(@Nullable IHealth oldService, @NonNull IHealth newService) { + if (mServiceInfoCallback == null) return; + + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthUnregisterCallbackAidl"); + try { + unregisterCallback(oldService, mHalInfoCallback); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthRegisterCallbackAidl"); + try { + registerCallback(newService, mHalInfoCallback); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } + + private static void unregisterCallback(@Nullable IHealth oldService, IHealthInfoCallback cb) { + if (oldService == null) return; + try { + oldService.unregisterCallback(cb); + } catch (RemoteException e) { + // Ignore errors. The service might have died. + Slog.w( + TAG, + "health: cannot unregister previous callback (transaction error): " + + e.getMessage()); + } + } + + private static void registerCallback(@NonNull IHealth newService, IHealthInfoCallback cb) { + try { + newService.registerCallback(cb); + } catch (RemoteException e) { + Slog.e( + TAG, + "health: cannot register callback, framework may cease to" + + " receive updates on health / battery info!", + e); + return; + } + // registerCallback does NOT guarantee that update is called immediately, so request a + // manual update here. + try { + newService.update(); + } catch (RemoteException e) { + Slog.e(TAG, "health: cannot update after registering health info callback", e); + } + } + + private class HalInfoCallback extends IHealthInfoCallback.Stub { + @Override + public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException { + mServiceInfoCallback.update(healthInfo); + } + } +} diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java new file mode 100644 index 000000000000..25d1a885bc18 --- /dev/null +++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java @@ -0,0 +1,119 @@ +/* + * 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.health; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.BatteryProperty; +import android.os.HandlerThread; +import android.os.IBatteryPropertiesRegistrar; +import android.os.RemoteException; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.NoSuchElementException; + +/** + * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when necessary. + * This is essentially a wrapper over IHealth that is useful for BatteryService. + * + * <p>The implementation may be backed by a HIDL or AIDL HAL. + * + * <p>On new registration of IHealth service, the internal service is refreshed. On death of an + * existing IHealth service, the internal service is NOT cleared to avoid race condition between + * death notification and new service notification. Hence, a caller must check for transaction + * errors when calling into the service. + * + * @hide Should only be used internally. + */ +public abstract class HealthServiceWrapper { + /** @return the handler thread. Exposed for testing. */ + @VisibleForTesting + abstract HandlerThread getHandlerThread(); + + /** + * Calls into get*() functions in the health HAL. This reads into the kernel interfaces + * directly. + * + * @see IBatteryPropertiesRegistrar#getProperty + */ + public abstract int getProperty(int id, BatteryProperty prop) throws RemoteException; + + /** + * Calls update() in the health HAL. + * + * @see IBatteryPropertiesRegistrar#scheduleUpdate + */ + public abstract void scheduleUpdate() throws RemoteException; + + /** + * Calls into getHealthInfo() in the health HAL. This returns a cached value in the health HAL + * implementation. + * + * @return health info. {@code null} if no health HAL service. {@code null} if any + * service-specific error when calling {@code getHealthInfo}, e.g. it is unsupported. + * @throws RemoteException for any transaction-level errors + */ + public abstract android.hardware.health.HealthInfo getHealthInfo() throws RemoteException; + + /** + * Create a new HealthServiceWrapper instance. + * + * @param healthInfoCallback the callback to call when health info changes + * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service. + * @throws RemoteException transaction errors + * @throws NoSuchElementException no HIDL or AIDL service is available + */ + public static HealthServiceWrapper create(@Nullable HealthInfoCallback healthInfoCallback) + throws RemoteException, NoSuchElementException { + return create( + healthInfoCallback == null ? null : new HealthRegCallbackAidl(healthInfoCallback), + new HealthServiceWrapperAidl.ServiceManagerStub() {}, + healthInfoCallback == null ? null : new HealthHalCallbackHidl(healthInfoCallback), + new HealthServiceWrapperHidl.IServiceManagerSupplier() {}, + new HealthServiceWrapperHidl.IHealthSupplier() {}); + } + + /** + * Create a new HealthServiceWrapper instance for testing. + * + * @param aidlRegCallback callback for AIDL service registration, or {@code null} if the client + * does not care about AIDL service registration notifications + * @param aidlServiceManager Stub for AIDL ServiceManager + * @param hidlRegCallback callback for HIDL service registration, or {@code null} if the client + * does not care about HIDL service registration notifications + * @param hidlServiceManagerSupplier supplier of HIDL service manager + * @param hidlHealthSupplier supplier of HIDL health HAL + * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service. + */ + @VisibleForTesting + static @NonNull HealthServiceWrapper create( + @Nullable HealthRegCallbackAidl aidlRegCallback, + @NonNull HealthServiceWrapperAidl.ServiceManagerStub aidlServiceManager, + @Nullable HealthServiceWrapperHidl.Callback hidlRegCallback, + @NonNull HealthServiceWrapperHidl.IServiceManagerSupplier hidlServiceManagerSupplier, + @NonNull HealthServiceWrapperHidl.IHealthSupplier hidlHealthSupplier) + throws RemoteException, NoSuchElementException { + try { + return new HealthServiceWrapperAidl(aidlRegCallback, aidlServiceManager); + } catch (NoSuchElementException e) { + // Ignore, try HIDL + } + return new HealthServiceWrapperHidl( + hidlRegCallback, hidlServiceManagerSupplier, hidlHealthSupplier); + } +} diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java new file mode 100644 index 000000000000..4f2ed68974fa --- /dev/null +++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java @@ -0,0 +1,215 @@ +/* + * 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.health; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.health.HealthInfo; +import android.hardware.health.IHealth; +import android.os.BatteryManager; +import android.os.BatteryProperty; +import android.os.Binder; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.IServiceCallback; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; +import android.os.Trace; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Implement {@link HealthServiceWrapper} backed by the AIDL HAL. + * + * @hide + */ +class HealthServiceWrapperAidl extends HealthServiceWrapper { + private static final String TAG = "HealthServiceWrapperAidl"; + @VisibleForTesting static final String SERVICE_NAME = IHealth.DESCRIPTOR + "/default"; + private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceBinder"); + private final AtomicReference<IHealth> mLastService = new AtomicReference<>(); + private final IServiceCallback mServiceCallback = new ServiceCallback(); + private final HealthRegCallbackAidl mRegCallback; + + /** Stub interface into {@link ServiceManager} for testing. */ + interface ServiceManagerStub { + default @Nullable IHealth waitForDeclaredService(@NonNull String name) { + return IHealth.Stub.asInterface(ServiceManager.waitForDeclaredService(name)); + } + + default void registerForNotifications( + @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException { + ServiceManager.registerForNotifications(name, callback); + } + } + + HealthServiceWrapperAidl( + @Nullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager) + throws RemoteException, NoSuchElementException { + + traceBegin("HealthInitGetServiceAidl"); + IHealth newService; + try { + newService = serviceManager.waitForDeclaredService(SERVICE_NAME); + } finally { + traceEnd(); + } + if (newService == null) { + throw new NoSuchElementException( + "IHealth service instance isn't available. Perhaps no permission?"); + } + mLastService.set(newService); + mRegCallback = regCallback; + if (mRegCallback != null) { + mRegCallback.onRegistration(null /* oldService */, newService); + } + + traceBegin("HealthInitRegisterNotificationAidl"); + mHandlerThread.start(); + try { + serviceManager.registerForNotifications(SERVICE_NAME, mServiceCallback); + } finally { + traceEnd(); + } + Slog.i(TAG, "health: HealthServiceWrapper listening to AIDL HAL"); + } + + @Override + @VisibleForTesting + public HandlerThread getHandlerThread() { + return mHandlerThread; + } + + @Override + public int getProperty(int id, BatteryProperty prop) throws RemoteException { + traceBegin("HealthGetPropertyAidl"); + try { + return getPropertyInternal(id, prop); + } finally { + traceEnd(); + } + } + + private int getPropertyInternal(int id, BatteryProperty prop) throws RemoteException { + IHealth service = mLastService.get(); + if (service == null) throw new RemoteException("no health service"); + try { + switch (id) { + case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: + prop.setLong(service.getChargeCounterUah()); + break; + case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW: + prop.setLong(service.getCurrentNowMicroamps()); + break; + case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE: + prop.setLong(service.getCurrentAverageMicroamps()); + break; + case BatteryManager.BATTERY_PROPERTY_CAPACITY: + prop.setLong(service.getCapacity()); + break; + case BatteryManager.BATTERY_PROPERTY_STATUS: + prop.setLong(service.getChargeStatus()); + break; + case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER: + prop.setLong(service.getEnergyCounterNwh()); + break; + } + } catch (UnsupportedOperationException e) { + // Leave prop untouched. + return -1; + } catch (ServiceSpecificException e) { + // Leave prop untouched. + return -2; + } + // throws RemoteException as-is. BatteryManager wraps it into a RuntimeException + // and throw it to apps. + + // If no error, return 0. + return 0; + } + + @Override + public void scheduleUpdate() throws RemoteException { + getHandlerThread() + .getThreadHandler() + .post( + () -> { + traceBegin("HealthScheduleUpdate"); + try { + IHealth service = mLastService.get(); + if (service == null) { + Slog.e(TAG, "no health service"); + return; + } + service.update(); + } catch (RemoteException | ServiceSpecificException ex) { + Slog.e(TAG, "Cannot call update on health AIDL HAL", ex); + } finally { + traceEnd(); + } + }); + } + + @Override + public HealthInfo getHealthInfo() throws RemoteException { + IHealth service = mLastService.get(); + if (service == null) return null; + try { + return service.getHealthInfo(); + } catch (UnsupportedOperationException | ServiceSpecificException ex) { + return null; + } + } + + private static void traceBegin(String name) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name); + } + + private static void traceEnd() { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + private class ServiceCallback extends IServiceCallback.Stub { + @Override + public void onRegistration(String name, @NonNull final IBinder newBinder) + throws RemoteException { + if (!SERVICE_NAME.equals(name)) return; + // This runnable only runs on mHandlerThread and ordering is ensured, hence + // no locking is needed inside the runnable. + getHandlerThread() + .getThreadHandler() + .post( + () -> { + IHealth newService = + IHealth.Stub.asInterface(Binder.allowBlocking(newBinder)); + IHealth oldService = mLastService.getAndSet(newService); + IBinder oldBinder = + oldService != null ? oldService.asBinder() : null; + if (Objects.equals(newBinder, oldBinder)) return; + + Slog.i(TAG, "New health AIDL HAL service registered"); + mRegCallback.onRegistration(oldService, newService); + }); + } + } +} diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java new file mode 100644 index 000000000000..0301174a45c6 --- /dev/null +++ b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java @@ -0,0 +1,313 @@ +/* + * 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.health; + +import static android.hardware.health.Translate.h2aTranslate; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.health.HealthInfo; +import android.hardware.health.V2_0.IHealth; +import android.hardware.health.V2_0.Result; +import android.hidl.manager.V1_0.IServiceManager; +import android.hidl.manager.V1_0.IServiceNotification; +import android.os.BatteryManager; +import android.os.BatteryProperty; +import android.os.HandlerThread; +import android.os.RemoteException; +import android.os.Trace; +import android.util.MutableInt; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Implement {@link HealthServiceWrapper} backed by the HIDL HAL. + * + * @hide + */ +final class HealthServiceWrapperHidl extends HealthServiceWrapper { + private static final String TAG = "HealthServiceWrapperHidl"; + public static final String INSTANCE_VENDOR = "default"; + + private final IServiceNotification mNotification = new Notification(); + private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder"); + // These variables are fixed after init. + private Callback mCallback; + private IHealthSupplier mHealthSupplier; + private String mInstanceName; + + // Last IHealth service received. + private final AtomicReference<IHealth> mLastService = new AtomicReference<>(); + + private static void traceBegin(String name) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name); + } + + private static void traceEnd() { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + @Override + public int getProperty(int id, final BatteryProperty prop) throws RemoteException { + traceBegin("HealthGetProperty"); + try { + IHealth service = mLastService.get(); + if (service == null) throw new RemoteException("no health service"); + final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED); + switch (id) { + case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: + service.getChargeCounter( + (int result, int value) -> { + outResult.value = result; + if (result == Result.SUCCESS) prop.setLong(value); + }); + break; + case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW: + service.getCurrentNow( + (int result, int value) -> { + outResult.value = result; + if (result == Result.SUCCESS) prop.setLong(value); + }); + break; + case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE: + service.getCurrentAverage( + (int result, int value) -> { + outResult.value = result; + if (result == Result.SUCCESS) prop.setLong(value); + }); + break; + case BatteryManager.BATTERY_PROPERTY_CAPACITY: + service.getCapacity( + (int result, int value) -> { + outResult.value = result; + if (result == Result.SUCCESS) prop.setLong(value); + }); + break; + case BatteryManager.BATTERY_PROPERTY_STATUS: + service.getChargeStatus( + (int result, int value) -> { + outResult.value = result; + if (result == Result.SUCCESS) prop.setLong(value); + }); + break; + case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER: + service.getEnergyCounter( + (int result, long value) -> { + outResult.value = result; + if (result == Result.SUCCESS) prop.setLong(value); + }); + break; + } + return outResult.value; + } finally { + traceEnd(); + } + } + + @Override + public void scheduleUpdate() throws RemoteException { + getHandlerThread() + .getThreadHandler() + .post( + () -> { + traceBegin("HealthScheduleUpdate"); + try { + IHealth service = mLastService.get(); + if (service == null) { + Slog.e(TAG, "no health service"); + return; + } + service.update(); + } catch (RemoteException ex) { + Slog.e(TAG, "Cannot call update on health HAL", ex); + } finally { + traceEnd(); + } + }); + } + + private static class Mutable<T> { + public T value; + } + + @Override + public HealthInfo getHealthInfo() throws RemoteException { + IHealth service = mLastService.get(); + if (service == null) return null; + final Mutable<HealthInfo> ret = new Mutable<>(); + service.getHealthInfo( + (result, value) -> { + if (result == Result.SUCCESS) { + ret.value = h2aTranslate(value.legacy); + } + }); + return ret.value; + } + + /** + * Start monitoring registration of new IHealth services. Only instance {@link #INSTANCE_VENDOR} + * and in device / framework manifest are used. This function should only be called once. + * + * <p>mCallback.onRegistration() is called synchronously (aka in init thread) before this method + * returns if callback is not null. + * + * @throws RemoteException transaction error when talking to IServiceManager + * @throws NoSuchElementException if one of the following cases: - No service manager; - {@link + * #INSTANCE_VENDOR} is not in manifests (i.e. not available on this device), or none of + * these instances are available to current process. + * @throws NullPointerException when supplier is null + */ + @VisibleForTesting + HealthServiceWrapperHidl( + @Nullable Callback callback, + @NonNull IServiceManagerSupplier managerSupplier, + @NonNull IHealthSupplier healthSupplier) + throws RemoteException, NoSuchElementException, NullPointerException { + if (managerSupplier == null || healthSupplier == null) { + throw new NullPointerException(); + } + mHealthSupplier = healthSupplier; + + // Initialize mLastService and call callback for the first time (in init thread) + IHealth newService = null; + traceBegin("HealthInitGetService_" + INSTANCE_VENDOR); + try { + newService = healthSupplier.get(INSTANCE_VENDOR); + } catch (NoSuchElementException ex) { + /* ignored, handled below */ + } finally { + traceEnd(); + } + if (newService != null) { + mInstanceName = INSTANCE_VENDOR; + mLastService.set(newService); + } + + if (mInstanceName == null || newService == null) { + throw new NoSuchElementException( + String.format( + "IHealth service instance %s isn't available. Perhaps no permission?", + INSTANCE_VENDOR)); + } + + if (callback != null) { + mCallback = callback; + mCallback.onRegistration(null, newService, mInstanceName); + } + + // Register for future service registrations + traceBegin("HealthInitRegisterNotification"); + mHandlerThread.start(); + try { + managerSupplier + .get() + .registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification); + } finally { + traceEnd(); + } + Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName); + } + + @VisibleForTesting + public HandlerThread getHandlerThread() { + return mHandlerThread; + } + + /** Service registration callback. */ + interface Callback { + /** + * This function is invoked asynchronously when a new and related IServiceNotification is + * received. + * + * @param service the recently retrieved service from IServiceManager. Can be a dead service + * before service notification of a new service is delivered. Implementation must handle + * cases for {@link RemoteException}s when calling into service. + * @param instance instance name. + */ + void onRegistration(IHealth oldService, IHealth newService, String instance); + } + + /** + * Supplier of services. Must not return null; throw {@link NoSuchElementException} if a service + * is not available. + */ + interface IServiceManagerSupplier { + default IServiceManager get() throws NoSuchElementException, RemoteException { + return IServiceManager.getService(); + } + } + + /** + * Supplier of services. Must not return null; throw {@link NoSuchElementException} if a service + * is not available. + */ + interface IHealthSupplier { + default IHealth get(String name) throws NoSuchElementException, RemoteException { + return IHealth.getService(name, true /* retry */); + } + } + + private class Notification extends IServiceNotification.Stub { + @Override + public final void onRegistration( + String interfaceName, String instanceName, boolean preexisting) { + if (!IHealth.kInterfaceName.equals(interfaceName)) return; + if (!mInstanceName.equals(instanceName)) return; + + // This runnable only runs on mHandlerThread and ordering is ensured, hence + // no locking is needed inside the runnable. + mHandlerThread + .getThreadHandler() + .post( + new Runnable() { + @Override + public void run() { + try { + IHealth newService = mHealthSupplier.get(mInstanceName); + IHealth oldService = mLastService.getAndSet(newService); + + // preexisting may be inaccurate (race). Check for equality + // here. + if (Objects.equals(newService, oldService)) return; + + Slog.i( + TAG, + "health: new instance registered " + mInstanceName); + // #init() may be called with null callback. Skip null + // callbacks. + if (mCallback == null) return; + mCallback.onRegistration( + oldService, newService, mInstanceName); + } catch (NoSuchElementException | RemoteException ex) { + Slog.e( + TAG, + "health: Cannot get instance '" + + mInstanceName + + "': " + + ex.getMessage() + + ". Perhaps no permission?"); + } + } + }); + } + } +} diff --git a/services/core/java/com/android/server/health/Utils.java b/services/core/java/com/android/server/health/Utils.java new file mode 100644 index 000000000000..a8c978c50e42 --- /dev/null +++ b/services/core/java/com/android/server/health/Utils.java @@ -0,0 +1,85 @@ +/* + * 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.health; + +/** + * Utils for {@link om.android.server.BatteryService} to deal with health info structs. + * + * @hide + */ +public class Utils { + private Utils() {} + + /** + * Copy health info struct. + * + * @param dst destination + * @param src source + */ + public static void copy( + android.hardware.health.V1_0.HealthInfo dst, + android.hardware.health.V1_0.HealthInfo src) { + dst.chargerAcOnline = src.chargerAcOnline; + dst.chargerUsbOnline = src.chargerUsbOnline; + dst.chargerWirelessOnline = src.chargerWirelessOnline; + dst.maxChargingCurrent = src.maxChargingCurrent; + dst.maxChargingVoltage = src.maxChargingVoltage; + dst.batteryStatus = src.batteryStatus; + dst.batteryHealth = src.batteryHealth; + dst.batteryPresent = src.batteryPresent; + dst.batteryLevel = src.batteryLevel; + dst.batteryVoltage = src.batteryVoltage; + dst.batteryTemperature = src.batteryTemperature; + dst.batteryCurrent = src.batteryCurrent; + dst.batteryCycleCount = src.batteryCycleCount; + dst.batteryFullCharge = src.batteryFullCharge; + dst.batteryChargeCounter = src.batteryChargeCounter; + dst.batteryTechnology = src.batteryTechnology; + } + + /** + * Copy battery fields of {@link android.hardware.health.HealthInfo} V1. This excludes + * non-battery fields like {@link android.hardware.health.HealthInfo#diskStats diskStats} and + * {@link android.hardware.health.HealthInfo#storageInfos storageInfos} + * + * @param dst destination + * @param src source + */ + public static void copyV1Battery( + android.hardware.health.HealthInfo dst, android.hardware.health.HealthInfo src) { + dst.chargerAcOnline = src.chargerAcOnline; + dst.chargerUsbOnline = src.chargerUsbOnline; + dst.chargerWirelessOnline = src.chargerWirelessOnline; + dst.maxChargingCurrentMicroamps = src.maxChargingCurrentMicroamps; + dst.maxChargingVoltageMicrovolts = src.maxChargingVoltageMicrovolts; + dst.batteryStatus = src.batteryStatus; + dst.batteryHealth = src.batteryHealth; + dst.batteryPresent = src.batteryPresent; + dst.batteryLevel = src.batteryLevel; + dst.batteryVoltageMillivolts = src.batteryVoltageMillivolts; + dst.batteryTemperatureTenthsCelsius = src.batteryTemperatureTenthsCelsius; + dst.batteryCurrentMicroamps = src.batteryCurrentMicroamps; + dst.batteryCycleCount = src.batteryCycleCount; + dst.batteryFullChargeUah = src.batteryFullChargeUah; + dst.batteryChargeCounterUah = src.batteryChargeCounterUah; + dst.batteryTechnology = src.batteryTechnology; + dst.batteryCurrentAverageMicroamps = src.batteryCurrentAverageMicroamps; + dst.batteryCapacityLevel = src.batteryCapacityLevel; + dst.batteryChargeTimeToFullNowSeconds = src.batteryChargeTimeToFullNowSeconds; + dst.batteryFullChargeDesignCapacityUah = src.batteryFullChargeDesignCapacityUah; + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index dc955337fdbc..3e52f5e07e62 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2582,14 +2582,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mCurToken != null) { - try { - if (DEBUG) { - Slog.v(TAG, "Removing window token: " + mCurToken + " for display: " - + mCurTokenDisplayId); - } - mIWindowManager.removeWindowToken(mCurToken, mCurTokenDisplayId); - } catch (RemoteException e) { + if (DEBUG) { + Slog.v(TAG, "Removing window token: " + mCurToken + " for display: " + + mCurTokenDisplayId); } + mWindowManagerInternal.removeWindowToken(mCurToken, false /* removeWindows */, + false /* animateExit */, mCurTokenDisplayId); // Set IME window status as invisible when unbind current method. mImeWindowVis = 0; mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 73de0f814325..ffc1aed4c672 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -51,6 +51,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; class BluetoothRouteProvider { private static final String TAG = "BTRouteProvider"; @@ -174,8 +175,9 @@ class BluetoothRouteProvider { private void buildBluetoothRoutes() { mBluetoothRoutes.clear(); - if (mBluetoothAdapter.getBondedDevices() != null) { - for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) { + Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices(); + if (bondedDevices != null) { + for (BluetoothDevice device : bondedDevices) { if (device.isConnected()) { BluetoothRouteInfo newBtRoute = createBluetoothRoute(device); if (newBtRoute.connectedProfiles.size() > 0) { diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java index df1eb6d9fe3c..d17dbde496ce 100644 --- a/services/core/java/com/android/server/net/IpConfigStore.java +++ b/services/core/java/com/android/server/net/IpConfigStore.java @@ -322,8 +322,11 @@ public class IpConfigStore { gateway = InetAddresses.parseNumericAddress(in.readUTF()); } // If the destination is a default IPv4 route, use the gateway - // address unless already set. - if (dest.getAddress() instanceof Inet4Address + // address unless already set. If there is no destination, assume + // it is default route and use the gateway address in all cases. + if (dest == null) { + gatewayAddress = gateway; + } else if (dest.getAddress() instanceof Inet4Address && dest.getPrefixLength() == 0 && gatewayAddress == null) { gatewayAddress = gateway; } else { diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 654b17fb9754..81106b1a5ca7 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -39,6 +39,7 @@ import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.RingBuffer; import com.android.server.am.ProcessList; +import com.android.server.net.NetworkPolicyManagerService.UidBlockedState; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -72,16 +73,6 @@ public class NetworkPolicyLogger { private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13; private static final int EVENT_APP_IDLE_WL_CHANGED = 14; - static final int NTWK_BLOCKED_POWER = 0; - static final int NTWK_ALLOWED_NON_METERED = 1; - static final int NTWK_BLOCKED_DENYLIST = 2; - static final int NTWK_ALLOWED_ALLOWLIST = 3; - static final int NTWK_ALLOWED_TMP_ALLOWLIST = 4; - static final int NTWK_BLOCKED_BG_RESTRICT = 5; - static final int NTWK_ALLOWED_DEFAULT = 6; - static final int NTWK_ALLOWED_SYSTEM = 7; - static final int NTWK_BLOCKED_RESTRICTED_MODE = 8; - private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE); private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE); private final LogBuffer mEventsBuffer = new LogBuffer(MAX_LOG_SIZE); @@ -90,12 +81,13 @@ public class NetworkPolicyLogger { private final Object mLock = new Object(); - void networkBlocked(int uid, int reason) { + void networkBlocked(int uid, UidBlockedState uidBlockedState) { synchronized (mLock) { if (LOGD || uid == mDebugUid) { - Slog.d(TAG, uid + " is " + getBlockedReason(reason)); + Slog.d(TAG, "Blocked state of uid: " + uidBlockedState.toString()); } - mNetworkBlockedBuffer.networkBlocked(uid, reason); + mNetworkBlockedBuffer.networkBlocked(uid, uidBlockedState.blockedReasons, + uidBlockedState.allowedReasons, uidBlockedState.effectiveBlockedReasons); } } @@ -269,29 +261,6 @@ public class NetworkPolicyLogger { } } - private static String getBlockedReason(int reason) { - switch (reason) { - case NTWK_BLOCKED_POWER: - return "blocked by power restrictions"; - case NTWK_ALLOWED_NON_METERED: - return "allowed on unmetered network"; - case NTWK_BLOCKED_DENYLIST: - return "denylisted on metered network"; - case NTWK_ALLOWED_ALLOWLIST: - return "allowlisted on metered network"; - case NTWK_ALLOWED_TMP_ALLOWLIST: - return "temporary allowlisted on metered network"; - case NTWK_BLOCKED_BG_RESTRICT: - return "blocked when background is restricted"; - case NTWK_ALLOWED_DEFAULT: - return "allowed by default"; - case NTWK_BLOCKED_RESTRICTED_MODE: - return "blocked by restricted networking mode"; - default: - return String.valueOf(reason); - } - } - private static String getPolicyChangedLog(int uid, int oldPolicy, int newPolicy) { return "Policy for " + uid + " changed from " + NetworkPolicyManager.uidPoliciesToString(oldPolicy) + " to " @@ -402,14 +371,17 @@ public class NetworkPolicyLogger { data.timeStamp = System.currentTimeMillis(); } - public void networkBlocked(int uid, int reason) { + public void networkBlocked(int uid, int blockedReasons, int allowedReasons, + int effectiveBlockedReasons) { final Data data = getNextSlot(); if (data == null) return; data.reset(); data.type = EVENT_NETWORK_BLOCKED; data.ifield1 = uid; - data.ifield2 = reason; + data.ifield2 = blockedReasons; + data.ifield3 = allowedReasons; + data.ifield4 = effectiveBlockedReasons; data.timeStamp = System.currentTimeMillis(); } @@ -554,7 +526,8 @@ public class NetworkPolicyLogger { case EVENT_TYPE_GENERIC: return data.sfield1; case EVENT_NETWORK_BLOCKED: - return data.ifield1 + "-" + getBlockedReason(data.ifield2); + return data.ifield1 + "-" + UidBlockedState.toString( + data.ifield2, data.ifield3, data.ifield4); case EVENT_UID_STATE_CHANGED: return data.ifield1 + ":" + ProcessList.makeProcStateString(data.ifield2) + ":" + ActivityManager.getCapabilitiesSummary(data.ifield3) @@ -593,17 +566,24 @@ public class NetworkPolicyLogger { } } - public final static class Data { - int type; - long timeStamp; - - int ifield1; - int ifield2; - int ifield3; - long lfield1; - boolean bfield1; - boolean bfield2; - String sfield1; + /** + * Container class for all networkpolicy events data. + * + * Note: This class needs to be public for RingBuffer class to be able to create + * new instances of this. + */ + public static final class Data { + public int type; + public long timeStamp; + + public int ifield1; + public int ifield2; + public int ifield3; + public int ifield4; + public long lfield1; + public boolean bfield1; + public boolean bfield2; + public String sfield1; public void reset(){ sfield1 = null; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 20687c6764db..5de5fd3cc79c 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -79,14 +79,10 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PE import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; -import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS; -import static android.net.NetworkPolicyManager.MASK_RESTRICTED_MODE_NETWORKS; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; -import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; @@ -135,15 +131,6 @@ import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_ALLOWLIST; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_ALLOWLIST; -import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT; -import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_DENYLIST; -import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER; -import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_RESTRICTED_MODE; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -518,8 +505,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Defined UID policies. */ @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidPolicy = new SparseIntArray(); - /** Currently derived rules for each UID. */ - @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray(); @@ -598,6 +583,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private final SparseArray<UidBlockedState> mUidBlockedState = new SparseArray<>(); + /** Objects used temporarily while computing the new blocked state for each uid. */ + @GuardedBy("mUidRulesFirstLock") + private final SparseArray<UidBlockedState> mTmpUidBlockedState = new SparseArray<>(); + /** Map from network ID to last observed meteredness state */ @GuardedBy("mNetworkPoliciesSecondLock") private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray(); @@ -3825,7 +3814,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseBooleanArray knownUids = new SparseBooleanArray(); collectKeys(mUidState, knownUids); - collectKeys(mUidRules, knownUids); + collectKeys(mUidBlockedState, knownUids); fout.println("Status for all known UIDs:"); fout.increaseIndent(); @@ -3843,23 +3832,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print(uidState.toString()); } - final int uidRules = mUidRules.get(uid, RULE_NONE); - fout.print(" rules="); - fout.print(uidRulesToString(uidRules)); - fout.println(); - } - fout.decreaseIndent(); - - fout.println("Status for just UIDs with rules:"); - fout.increaseIndent(); - size = mUidRules.size(); - for (int i = 0; i < size; i++) { - final int uid = mUidRules.keyAt(i); - fout.print("UID="); - fout.print(uid); - final int uidRules = mUidRules.get(uid, RULE_NONE); - fout.print(" rules="); - fout.print(uidRulesToString(uidRules)); + final UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + if (uidBlockedState == null) { + fout.print(" blocked_state={null}"); + } else { + fout.print(" blocked_state="); + fout.print(uidBlockedState.toString()); + } fout.println(); } fout.decreaseIndent(); @@ -4010,22 +3989,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { void updateRestrictedModeAllowlistUL() { mUidFirewallRestrictedModeRules.clear(); forEachUid("updateRestrictedModeAllowlist", uid -> { - final int oldUidRule = mUidRules.get(uid); - final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule); - final boolean hasUidRuleChanged = oldUidRule != newUidRule; - final int newFirewallRule = getRestrictedModeFirewallRule(newUidRule); - - // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add - // non-default rules. - if (newFirewallRule != FIREWALL_RULE_DEFAULT) { - mUidFirewallRestrictedModeRules.append(uid, newFirewallRule); - } - - if (hasUidRuleChanged) { - mUidRules.put(uid, newUidRule); - mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget(); + synchronized (mUidRulesFirstLock) { + final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL( + uid); + final int newFirewallRule = getRestrictedModeFirewallRule(uidBlockedState); + + // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add + // non-default rules. + if (newFirewallRule != FIREWALL_RULE_DEFAULT) { + mUidFirewallRestrictedModeRules.append(uid, newFirewallRule); + } } - updateBlockedReasonsForRestrictedModeUL(uid); }); if (mRestrictedNetworkingMode) { // firewall rules only need to be set when this mode is being enabled. @@ -4038,15 +4012,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @VisibleForTesting @GuardedBy("mUidRulesFirstLock") void updateRestrictedModeForUidUL(int uid) { - final int oldUidRule = mUidRules.get(uid); - final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule); - final boolean hasUidRuleChanged = oldUidRule != newUidRule; - - if (hasUidRuleChanged) { - mUidRules.put(uid, newUidRule); - mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget(); - } - updateBlockedReasonsForRestrictedModeUL(uid); + final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL(uid); // if restricted networking mode is on, and the app has an access exemption, the uid rule // will not change, but the firewall rule will have to be updated. @@ -4054,16 +4020,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules. // In this case, default firewall rules can also be added. setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid, - getRestrictedModeFirewallRule(newUidRule)); + getRestrictedModeFirewallRule(uidBlockedState)); } } - private void updateBlockedReasonsForRestrictedModeUL(int uid) { - UidBlockedState uidBlockedState = mUidBlockedState.get(uid); - if (uidBlockedState == null) { - uidBlockedState = new UidBlockedState(); - mUidBlockedState.put(uid, uidBlockedState); - } + @GuardedBy("mUidRulesFirstLock") + private UidBlockedState updateBlockedReasonsForRestrictedModeUL(int uid) { + final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid( + mUidBlockedState, uid); final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; if (mRestrictedNetworkingMode) { uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE; @@ -4077,23 +4041,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } uidBlockedState.updateEffectiveBlockedReasons(); if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { - mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, - uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) - .sendToTarget(); - } - } + postBlockedReasonsChangedMsg(uid, + uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons); - private int getNewRestrictedModeUidRule(int uid, int oldUidRule) { - int newRule = oldUidRule; - newRule &= ~MASK_RESTRICTED_MODE_NETWORKS; - if (mRestrictedNetworkingMode && !hasRestrictedModeAccess(uid)) { - newRule |= RULE_REJECT_RESTRICTED_MODE; + postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules()); } - return newRule; + return uidBlockedState; } - private static int getRestrictedModeFirewallRule(int uidRule) { - if ((uidRule & RULE_REJECT_RESTRICTED_MODE) != 0) { + private static int getRestrictedModeFirewallRule(UidBlockedState uidBlockedState) { + if ((uidBlockedState.effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) { // rejected in restricted mode, this is the default behavior. return FIREWALL_RULE_DEFAULT; } else { @@ -4301,16 +4258,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (!isUidValidForDenylistRulesUL(uid)) { continue; } - int oldRules = mUidRules.get(uid); - if (enableChain) { - // Chain wasn't enabled before and the other power-related - // chains are allowlists, so we can clear the - // MASK_ALL_NETWORKS part of the rules and re-inform listeners if - // the effective rules result in blocking network access. - oldRules &= MASK_METERED_NETWORKS; - } else { - // Skip if it had no restrictions to begin with - if ((oldRules & MASK_ALL_NETWORKS) == 0) continue; + final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid( + mUidBlockedState, uid); + if (!enableChain && (uidBlockedState.blockedReasons & ~BLOCKED_METERED_REASON_MASK) + == BLOCKED_REASON_NONE) { + // Chain isn't enabled and the uid had no restrictions to begin with. + continue; } final boolean isUidIdle = !paroled && isUidIdle(uid); if (isUidIdle && !mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid)) @@ -4320,13 +4273,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } else { mUidFirewallStandbyRules.put(uid, FIREWALL_RULE_DEFAULT); } - final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, - isUidIdle); - if (newUidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newUidRules); - } + updateRulesForPowerRestrictionsUL(uid, isUidIdle); } setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, blockedUids, enableChain ? CHAIN_TOGGLE_ENABLE : CHAIN_TOGGLE_DISABLE); @@ -4544,6 +4491,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mInternetPermissionMap.put(uid, hasPermission); return hasPermission; } catch (RemoteException e) { + // ignored; service lives in system_server } return true; } @@ -4554,7 +4502,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private void onUidDeletedUL(int uid) { // First cleanup in-memory state synchronously... - mUidRules.delete(uid); + mUidBlockedState.delete(uid); mUidPolicy.delete(uid); mUidFirewallStandbyRules.delete(uid); mUidFirewallDozableRules.delete(uid); @@ -4640,7 +4588,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * permission, since there is no need to change the {@code iptables} rule if the app does not * have permission to use the internet. * - * <p>The {@link #mUidRules} map is used to define the transtion of states of an UID. + * <p>The {@link #mUidBlockedState} map is used to define the transition of states of an UID. * */ private void updateRulesForDataUsageRestrictionsUL(int uid) { @@ -4655,6 +4603,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + @GuardedBy("mUidRulesFirstLock") private void updateRulesForDataUsageRestrictionsULInner(int uid) { if (!isUidValidForAllowlistRulesUL(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid); @@ -4662,38 +4611,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE); - final int oldUidRules = mUidRules.get(uid, RULE_NONE); final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid); final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid); - UidBlockedState uidBlockedState = mUidBlockedState.get(uid); - if (uidBlockedState == null) { - uidBlockedState = new UidBlockedState(); - mUidBlockedState.put(uid, uidBlockedState); - } + final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid( + mUidBlockedState, uid); + final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid( + mTmpUidBlockedState, uid); + previousUidBlockedState.copyFrom(uidBlockedState); final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0; - // copy oldUidRules and clear out METERED_NETWORKS rules. - int newUidRules = oldUidRules & (~MASK_METERED_NETWORKS); - - // First step: define the new rule based on user restrictions and foreground state. - if (isRestrictedByAdmin) { - newUidRules |= RULE_REJECT_METERED; - } else if (isForeground) { - if (isDenied || (mRestrictBackground && !isAllowed)) { - newUidRules |= RULE_TEMPORARY_ALLOW_METERED; - } else if (isAllowed) { - newUidRules |= RULE_ALLOW_METERED; - } - } else { - if (isDenied) { - newUidRules |= RULE_REJECT_METERED; - } else if (mRestrictBackground && isAllowed) { - newUidRules |= RULE_ALLOW_METERED; - } - } - int newBlockedReasons = BLOCKED_REASON_NONE; int newAllowedReasons = ALLOWED_REASON_NONE; newBlockedReasons |= (isRestrictedByAdmin ? BLOCKED_METERED_REASON_ADMIN_DISABLED : 0); @@ -4704,16 +4632,48 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newAllowedReasons |= (isForeground ? ALLOWED_METERED_REASON_FOREGROUND : 0); newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0); + uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons + & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons; + uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons + & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons; + uidBlockedState.updateEffectiveBlockedReasons(); + final int oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons; + final int newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; + if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) { + postBlockedReasonsChangedMsg(uid, + newEffectiveBlockedReasons, oldEffectiveBlockedReasons); + + postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules()); + } + + // Note that the conditionals below are for avoiding unnecessary calls to netd. + // TODO: Measure the performance for doing a no-op call to netd so that we can + // remove the conditionals to simplify the logic below. We can also further reduce + // some calls to netd if they turn out to be costly. + final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED + | BLOCKED_METERED_REASON_USER_RESTRICTED; + if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE + || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) { + setMeteredNetworkDenylist(uid, + (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE); + } + final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND + | ALLOWED_METERED_REASON_USER_EXEMPTED; + final int oldAllowedReasons = previousUidBlockedState.allowedReasons; + if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE + || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) { + setMeteredNetworkAllowlist(uid, + (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE); + } + if (LOGV) { Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")" + ": isForeground=" +isForeground + ", isDenied=" + isDenied + ", isAllowed=" + isAllowed + ", isRestrictedByAdmin=" + isRestrictedByAdmin - + ", oldRule=" + uidRulesToString(oldUidRules & MASK_METERED_NETWORKS) - + ", newRule=" + uidRulesToString(newUidRules & MASK_METERED_NETWORKS) - + ", newUidRules=" + uidRulesToString(newUidRules) - + ", oldUidRules=" + uidRulesToString(oldUidRules) + + ", oldBlockedState=" + previousUidBlockedState.toString() + + ", newBlockedState=" + ", oldBlockedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString( uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK) + ", oldBlockedMeteredEffectiveReasons=" @@ -4722,84 +4682,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", oldAllowedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString( uidBlockedState.allowedReasons & BLOCKED_METERED_REASON_MASK)); } - - if (newUidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newUidRules); - } - - // Second step: apply bw changes based on change of state. - if (newUidRules != oldUidRules) { - if (hasRule(newUidRules, RULE_TEMPORARY_ALLOW_METERED)) { - // Temporarily allow foreground app, removing from denylist if necessary - // (since bw_penalty_box prevails over bw_happy_box). - - setMeteredNetworkAllowlist(uid, true); - // TODO: if statement below is used to avoid an unnecessary call to netd / iptables, - // but ideally it should be just: - // setMeteredNetworkDenylist(uid, isDenied); - if (isDenied) { - setMeteredNetworkDenylist(uid, false); - } - } else if (hasRule(oldUidRules, RULE_TEMPORARY_ALLOW_METERED)) { - // Remove temporary exemption from app that is not on foreground anymore. - - // TODO: if statements below are used to avoid unnecessary calls to netd / iptables, - // but ideally they should be just: - // setMeteredNetworkAllowlist(uid, isAllowed); - // setMeteredNetworkDenylist(uid, isDenied); - if (!isAllowed) { - setMeteredNetworkAllowlist(uid, false); - } - if (isDenied || isRestrictedByAdmin) { - setMeteredNetworkDenylist(uid, true); - } - } else if (hasRule(newUidRules, RULE_REJECT_METERED) - || hasRule(oldUidRules, RULE_REJECT_METERED)) { - // Flip state because app was explicitly added or removed to denylist. - setMeteredNetworkDenylist(uid, (isDenied || isRestrictedByAdmin)); - if (hasRule(oldUidRules, RULE_REJECT_METERED) && isAllowed) { - // Since denial prevails over allowance, we need to handle the special case - // where app is allowed and denied at the same time (although such - // scenario should be blocked by the UI), then it is removed from the denylist. - setMeteredNetworkAllowlist(uid, isAllowed); - } - } else if (hasRule(newUidRules, RULE_ALLOW_METERED) - || hasRule(oldUidRules, RULE_ALLOW_METERED)) { - // Flip state because app was explicitly added or removed to allowlist. - setMeteredNetworkAllowlist(uid, isAllowed); - } else { - // All scenarios should have been covered above. - Log.wtf(TAG, "Unexpected change of metered UID state for " + uid - + ": foreground=" + isForeground - + ", allowlisted=" + isAllowed - + ", denylisted=" + isDenied - + ", isRestrictedByAdmin=" + isRestrictedByAdmin - + ", newRule=" + uidRulesToString(newUidRules) - + ", oldRule=" + uidRulesToString(oldUidRules)); - } - - // Dispatch changed rule to existing listeners. - mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); - } - - final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; - uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons - & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons; - uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons - & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons; - uidBlockedState.updateEffectiveBlockedReasons(); - if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { - mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, - uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) - .sendToTarget(); - } } /** - * Updates the power-related part of the {@link #mUidRules} for a given map, and notify external - * listeners in case of change. + * Updates the power-related part of the {@link #mUidBlockedState} for a given map, and + * notify external listeners in case of change. * <p> * There are 3 power-related rules that affects whether an app has background access on * non-metered networks, and when the condition applies and the UID is not allowed for power @@ -4810,23 +4697,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * <li>Battery Saver Mode is on: {@code fw_powersave} firewall chain. * </ul> * <p> - * This method updates the power-related part of the {@link #mUidRules} for a given uid based on - * these modes, the UID process state (foreground or not), and the UID allowlist state. + * This method updates the power-related part of the {@link #mUidBlockedState} for a given + * uid based on these modes, the UID process state (foreground or not), and the UID + * allowlist state. * <p> * <strong>NOTE: </strong>This method does not update the firewall rules on {@code netd}. */ @GuardedBy("mUidRulesFirstLock") private void updateRulesForPowerRestrictionsUL(int uid) { - final int oldUidRules = mUidRules.get(uid, RULE_NONE); - - final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, - isUidIdle(uid)); - - if (newUidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newUidRules); - } + updateRulesForPowerRestrictionsUL(uid, isUidIdle(uid)); } /** @@ -4835,56 +4714,37 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @param uid the uid of the app to update rules for * @param oldUidRules the current rules for the uid, in order to determine if there's a change * @param isUidIdle whether uid is idle or not - * - * @return the new computed rules for the uid */ @GuardedBy("mUidRulesFirstLock") - private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean isUidIdle) { + private void updateRulesForPowerRestrictionsUL(int uid, boolean isUidIdle) { if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, - "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/" + "updateRulesForPowerRestrictionsUL: " + uid + "/" + (isUidIdle ? "I" : "-")); } try { - return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, isUidIdle); + updateRulesForPowerRestrictionsULInner(uid, isUidIdle); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } @GuardedBy("mUidRulesFirstLock") - private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, - boolean isUidIdle) { + private void updateRulesForPowerRestrictionsULInner(int uid, boolean isUidIdle) { if (!isUidValidForDenylistRulesUL(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); - return RULE_NONE; + return; } - final boolean restrictMode = isUidIdle || mRestrictPower || mDeviceIdleMode; final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode); - // Copy existing uid rules and clear ALL_NETWORK rules. - int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS); - - UidBlockedState uidBlockedState = mUidBlockedState.get(uid); - if (uidBlockedState == null) { - uidBlockedState = new UidBlockedState(); - mUidBlockedState.put(uid, uidBlockedState); - } - - // First step: define the new rule based on user restrictions and foreground state. - - // NOTE: if statements below could be inlined, but it's easier to understand the logic - // by considering the foreground and non-foreground states. - if (isForeground) { - if (restrictMode) { - newUidRules |= RULE_ALLOW_ALL; - } - } else if (restrictMode) { - newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; - } + final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid( + mUidBlockedState, uid); + final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid( + mTmpUidBlockedState, uid); + previousUidBlockedState.copyFrom(uidBlockedState); int newBlockedReasons = BLOCKED_REASON_NONE; int newAllowedReasons = ALLOWED_REASON_NONE; @@ -4899,6 +4759,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0); newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid) ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0); + newAllowedReasons |= (uidBlockedState.allowedReasons + & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS); + + uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons + & BLOCKED_METERED_REASON_MASK) | newBlockedReasons; + uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons + & ALLOWED_METERED_REASON_MASK) | newAllowedReasons; + uidBlockedState.updateEffectiveBlockedReasons(); + if (previousUidBlockedState.effectiveBlockedReasons + != uidBlockedState.effectiveBlockedReasons) { + postBlockedReasonsChangedMsg(uid, + uidBlockedState.effectiveBlockedReasons, + previousUidBlockedState.effectiveBlockedReasons); + + postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules()); + } if (LOGV) { Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")" @@ -4907,43 +4783,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", mDeviceIdleMode: " + mDeviceIdleMode + ", isForeground=" + isForeground + ", isWhitelisted=" + isWhitelisted - + ", oldRule=" + uidRulesToString(oldUidRules & MASK_ALL_NETWORKS) - + ", newRule=" + uidRulesToString(newUidRules & MASK_ALL_NETWORKS) - + ", newUidRules=" + uidRulesToString(newUidRules) - + ", oldUidRules=" + uidRulesToString(oldUidRules)); - } - - // Second step: notify listeners if state changed. - if (newUidRules != oldUidRules) { - if ((newUidRules & MASK_ALL_NETWORKS) == RULE_NONE || hasRule(newUidRules, - RULE_ALLOW_ALL)) { - if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid); - } else if (hasRule(newUidRules, RULE_REJECT_ALL)) { - if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid); - } else { - // All scenarios should have been covered above - Log.wtf(TAG, "Unexpected change of non-metered UID state for " + uid - + ": foreground=" + isForeground - + ", whitelisted=" + isWhitelisted - + ", newRule=" + uidRulesToString(newUidRules) - + ", oldRule=" + uidRulesToString(oldUidRules)); - } - mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); - } - - final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; - uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons - & BLOCKED_METERED_REASON_MASK) | newBlockedReasons; - uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons - & ALLOWED_METERED_REASON_MASK) | newAllowedReasons; - uidBlockedState.updateEffectiveBlockedReasons(); - if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { - mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, - uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) - .sendToTarget(); + + ", oldUidBlockedState=" + previousUidBlockedState.toString() + + ", newUidBlockedState=" + uidBlockedState.toString()); } - - return newUidRules; } private class NetPolicyAppIdleStateChangeListener extends AppIdleStateChangeListener { @@ -4971,10 +4813,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void postBlockedReasonsChangedMsg(int uid, int newEffectiveBlockedReasons, + int oldEffectiveBlockedReasons) { + mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, + newEffectiveBlockedReasons, oldEffectiveBlockedReasons) + .sendToTarget(); + } + + private void postUidRulesChangedMsg(int uid, int uidRules) { + mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules) + .sendToTarget(); + } + private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) { try { listener.onUidRulesChanged(uid, uidRules); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -4983,6 +4838,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onMeteredIfacesChanged(meteredIfaces); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -4991,6 +4847,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onRestrictBackgroundChanged(restrictBackground); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -4999,6 +4856,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onUidPoliciesChanged(uid, uidPolicies); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5007,6 +4865,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5015,6 +4874,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onSubscriptionPlansChanged(subId, plans); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5023,6 +4883,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onBlockedReasonChanged(uid, oldBlockedReasons, newBlockedReasons); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5033,6 +4894,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { case MSG_RULES_CHANGED: { final int uid = msg.arg1; final int uidRules = msg.arg2; + if (LOGV) { + Slog.v(TAG, "Dispatching rules=" + uidRulesToString(uidRules) + + " for uid=" + uid); + } final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -5605,7 +5470,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private static void collectKeys(SparseArray<UidState> source, SparseBooleanArray target) { + private static <T> void collectKeys(SparseArray<T> source, SparseBooleanArray target) { final int size = source.size(); for (int i = 0; i < size; i++) { target.put(source.keyAt(i), true); @@ -5653,90 +5518,38 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long startTime = mStatLogger.getTime(); mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); - final int uidRules; - final boolean isBackgroundRestricted; + int blockedReasons; synchronized (mUidRulesFirstLock) { - uidRules = mUidRules.get(uid, RULE_NONE); - isBackgroundRestricted = mRestrictBackground; + final UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + blockedReasons = uidBlockedState == null + ? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons; + if (!isNetworkMetered) { + blockedReasons &= ~BLOCKED_METERED_REASON_MASK; + } + mLogger.networkBlocked(uid, uidBlockedState); } - final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered, - isBackgroundRestricted, mLogger); mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime); - return ret; + return blockedReasons != BLOCKED_REASON_NONE; } @Override public boolean isUidRestrictedOnMeteredNetworks(int uid) { mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); - final int uidRules; - final boolean isBackgroundRestricted; synchronized (mUidRulesFirstLock) { - uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); - isBackgroundRestricted = mRestrictBackground; + final UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + int blockedReasons = uidBlockedState == null + ? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons; + blockedReasons &= BLOCKED_METERED_REASON_MASK; + return blockedReasons != BLOCKED_REASON_NONE; } - // TODO(b/177490332): The logic here might not be correct because it doesn't consider - // RULE_REJECT_METERED condition. And it could be replaced by - // isUidNetworkingBlockedInternal(). - return isBackgroundRestricted - && !hasRule(uidRules, RULE_ALLOW_METERED) - && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); } private static boolean isSystem(int uid) { return uid < Process.FIRST_APPLICATION_UID; } - static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered, - boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) { - final int reason; - // Networks are never blocked for system components - if (isSystem(uid)) { - reason = NTWK_ALLOWED_SYSTEM; - } else if (hasRule(uidRules, RULE_REJECT_RESTRICTED_MODE)) { - reason = NTWK_BLOCKED_RESTRICTED_MODE; - } else if (hasRule(uidRules, RULE_REJECT_ALL)) { - reason = NTWK_BLOCKED_POWER; - } else if (!isNetworkMetered) { - reason = NTWK_ALLOWED_NON_METERED; - } else if (hasRule(uidRules, RULE_REJECT_METERED)) { - reason = NTWK_BLOCKED_DENYLIST; - } else if (hasRule(uidRules, RULE_ALLOW_METERED)) { - reason = NTWK_ALLOWED_ALLOWLIST; - } else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { - reason = NTWK_ALLOWED_TMP_ALLOWLIST; - } else if (isBackgroundRestricted) { - reason = NTWK_BLOCKED_BG_RESTRICT; - } else { - reason = NTWK_ALLOWED_DEFAULT; - } - - final boolean blocked; - switch(reason) { - case NTWK_ALLOWED_DEFAULT: - case NTWK_ALLOWED_NON_METERED: - case NTWK_ALLOWED_TMP_ALLOWLIST: - case NTWK_ALLOWED_ALLOWLIST: - case NTWK_ALLOWED_SYSTEM: - blocked = false; - break; - case NTWK_BLOCKED_RESTRICTED_MODE: - case NTWK_BLOCKED_POWER: - case NTWK_BLOCKED_DENYLIST: - case NTWK_BLOCKED_BG_RESTRICT: - blocked = true; - break; - default: - throw new IllegalArgumentException(); - } - if (logger != null) { - logger.networkBlocked(uid, reason); - } - - return blocked; - } - private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal { @Override @@ -5945,6 +5758,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue; } + private static UidBlockedState getOrCreateUidBlockedStateForUid( + SparseArray<UidBlockedState> uidBlockedStates, int uid) { + UidBlockedState uidBlockedState = uidBlockedStates.get(uid); + if (uidBlockedState == null) { + uidBlockedState = new UidBlockedState(); + uidBlockedStates.put(uid, uidBlockedState); + } + return uidBlockedState; + } + @VisibleForTesting static final class UidBlockedState { public int blockedReasons; @@ -6008,9 +5831,180 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } return effectiveBlockedReasons; } + + @Override + public String toString() { + return toString(blockedReasons, allowedReasons, effectiveBlockedReasons); + } + + public static String toString(int blockedReasons, int allowedReasons, + int effectiveBlockedReasons) { + final StringBuilder sb = new StringBuilder(); + sb.append("{"); + sb.append("blocked=").append(blockedReasonsToString(blockedReasons)).append(","); + sb.append("allowed=").append(allowedReasonsToString(allowedReasons)).append(","); + sb.append("effective=").append(blockedReasonsToString(effectiveBlockedReasons)); + sb.append("}"); + return sb.toString(); + } + + private static final int[] BLOCKED_REASONS = { + 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, + }; + + private static final int[] ALLOWED_REASONS = { + ALLOWED_REASON_SYSTEM, + ALLOWED_REASON_FOREGROUND, + ALLOWED_REASON_POWER_SAVE_ALLOWLIST, + ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST, + ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS, + ALLOWED_METERED_REASON_USER_EXEMPTED, + ALLOWED_METERED_REASON_SYSTEM, + ALLOWED_METERED_REASON_FOREGROUND, + }; + + private static String blockedReasonToString(int blockedReason) { + switch (blockedReason) { + case BLOCKED_REASON_NONE: + return "NONE"; + case BLOCKED_REASON_BATTERY_SAVER: + return "BATTERY_SAVER"; + case BLOCKED_REASON_DOZE: + return "DOZE"; + case BLOCKED_REASON_APP_STANDBY: + return "APP_STANDBY"; + case BLOCKED_REASON_RESTRICTED_MODE: + return "RESTRICTED_MODE"; + case BLOCKED_METERED_REASON_DATA_SAVER: + return "DATA_SAVER"; + case BLOCKED_METERED_REASON_USER_RESTRICTED: + return "METERED_USER_RESTRICTED"; + case BLOCKED_METERED_REASON_ADMIN_DISABLED: + return "METERED_ADMIN_DISABLED"; + default: + Slog.wtfStack(TAG, "Unknown blockedReason: " + blockedReason); + return String.valueOf(blockedReason); + } + } + + private static String allowedReasonToString(int allowedReason) { + switch (allowedReason) { + case ALLOWED_REASON_NONE: + return "NONE"; + case ALLOWED_REASON_SYSTEM: + return "SYSTEM"; + case ALLOWED_REASON_FOREGROUND: + return "FOREGROUND"; + case ALLOWED_REASON_POWER_SAVE_ALLOWLIST: + return "POWER_SAVE_ALLOWLIST"; + case ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST: + return "POWER_SAVE_EXCEPT_IDLE_ALLOWLIST"; + case ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS: + return "RESTRICTED_MODE_PERMISSIONS"; + case ALLOWED_METERED_REASON_USER_EXEMPTED: + return "METERED_USER_EXEMPTED"; + case ALLOWED_METERED_REASON_SYSTEM: + return "METERED_SYSTEM"; + case ALLOWED_METERED_REASON_FOREGROUND: + return "METERED_FOREGROUND"; + default: + Slog.wtfStack(TAG, "Unknown allowedReason: " + allowedReason); + return String.valueOf(allowedReason); + } + } + + public static String blockedReasonsToString(int blockedReasons) { + if (blockedReasons == BLOCKED_REASON_NONE) { + return blockedReasonToString(BLOCKED_REASON_NONE); + } + final StringBuilder sb = new StringBuilder(); + for (int reason : BLOCKED_REASONS) { + if ((blockedReasons & reason) != 0) { + sb.append(sb.length() == 0 ? "" : "|"); + sb.append(blockedReasonToString(reason)); + blockedReasons &= ~reason; + } + } + if (blockedReasons != 0) { + sb.append(sb.length() == 0 ? "" : "|"); + sb.append(String.valueOf(blockedReasons)); + Slog.wtfStack(TAG, "Unknown blockedReasons: " + blockedReasons); + } + return sb.toString(); + } + + public static String allowedReasonsToString(int allowedReasons) { + if (allowedReasons == ALLOWED_REASON_NONE) { + return allowedReasonToString(ALLOWED_REASON_NONE); + } + final StringBuilder sb = new StringBuilder(); + for (int reason : ALLOWED_REASONS) { + if ((allowedReasons & reason) != 0) { + sb.append(sb.length() == 0 ? "" : "|"); + sb.append(allowedReasonToString(reason)); + allowedReasons &= ~reason; + } + } + if (allowedReasons != 0) { + sb.append(sb.length() == 0 ? "" : "|"); + sb.append(String.valueOf(allowedReasons)); + Slog.wtfStack(TAG, "Unknown allowedReasons: " + allowedReasons); + } + return sb.toString(); + } + + public void copyFrom(UidBlockedState uidBlockedState) { + blockedReasons = uidBlockedState.blockedReasons; + allowedReasons = uidBlockedState.allowedReasons; + effectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; + } + + public int deriveUidRules() { + int uidRule = RULE_NONE; + if ((effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) { + uidRule |= RULE_REJECT_RESTRICTED_MODE; + } + + int powerBlockedReasons = BLOCKED_REASON_APP_STANDBY + | BLOCKED_REASON_DOZE + | BLOCKED_REASON_BATTERY_SAVER; + if ((effectiveBlockedReasons & powerBlockedReasons) != 0) { + uidRule |= RULE_REJECT_ALL; + } else if ((blockedReasons & powerBlockedReasons) != 0) { + uidRule |= RULE_ALLOW_ALL; + } + + // UidRule doesn't include RestrictBackground (DataSaver) state, so not including in + // metered blocked reasons below. + int meteredBlockedReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED + | BLOCKED_METERED_REASON_USER_RESTRICTED; + if ((effectiveBlockedReasons & meteredBlockedReasons) != 0) { + uidRule |= RULE_REJECT_METERED; + } else if ((blockedReasons & BLOCKED_METERED_REASON_USER_RESTRICTED) != 0 + && (allowedReasons & ALLOWED_METERED_REASON_FOREGROUND) != 0) { + uidRule |= RULE_TEMPORARY_ALLOW_METERED; + } else if ((blockedReasons & BLOCKED_METERED_REASON_DATA_SAVER) != 0) { + if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) { + uidRule |= RULE_ALLOW_ALL; + } else if ((allowedReasons & ALLOWED_METERED_REASON_FOREGROUND) != 0) { + uidRule |= RULE_TEMPORARY_ALLOW_METERED; + } + } + if (LOGV) { + Slog.v(TAG, "uidBlockedState=" + this.toString() + + " -> uidRule=" + uidRulesToString(uidRule)); + } + return uidRule; + } } - private class NotificationId { + private static class NotificationId { private final String mTag; private final int mId; diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index 431b00914f02..e6433db11d7b 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -159,7 +159,7 @@ public class NetworkStatsFactory { } public NetworkStatsFactory() { - this(new File("/proc/"), new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists()); + this(new File("/proc/"), true); } @VisibleForTesting diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 097b0711eff7..c876d411fac1 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -150,6 +150,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; +import com.android.net.module.util.BinderUtils; import com.android.server.EventLogTags; import com.android.server.LocalServices; @@ -215,8 +216,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final PowerManager.WakeLock mWakeLock; - private final boolean mUseBpfTrafficStats; - private final ContentObserver mContentObserver; private final ContentResolver mContentResolver; @@ -438,7 +437,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mStatsObservers = Objects.requireNonNull(statsObservers, "missing NetworkStatsObservers"); mSystemDir = Objects.requireNonNull(systemDir, "missing systemDir"); mBaseDir = Objects.requireNonNull(baseDir, "missing baseDir"); - mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists(); mDeps = Objects.requireNonNull(deps, "missing Dependencies"); final HandlerThread handlerThread = mDeps.makeHandlerThread(); @@ -1084,13 +1082,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { if (callingUid != android.os.Process.SYSTEM_UID && callingUid != uid) { return UNSUPPORTED; } - return nativeGetUidStat(uid, type, checkBpfStatsEnable()); + return nativeGetUidStat(uid, type); } @Override public long getIfaceStats(@NonNull String iface, int type) { Objects.requireNonNull(iface); - long nativeIfaceStats = nativeGetIfaceStat(iface, type, checkBpfStatsEnable()); + long nativeIfaceStats = nativeGetIfaceStat(iface, type); if (nativeIfaceStats == -1) { return nativeIfaceStats; } else { @@ -1104,7 +1102,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public long getTotalStats(int type) { - long nativeTotalStats = nativeGetTotalStat(type, checkBpfStatsEnable()); + long nativeTotalStats = nativeGetTotalStat(type); if (nativeTotalStats == -1) { return nativeTotalStats; } else { @@ -1137,10 +1135,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private boolean checkBpfStatsEnable() { - return mUseBpfTrafficStats; - } - /** * Update {@link NetworkStatsRecorder} and {@link #mGlobalAlertBytes} to * reflect current {@link #mPersistThreshold} value. Always defers to @@ -2104,14 +2098,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public void notifyAlertReached() throws RemoteException { - mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */); + // This binder object can only have been obtained by a process that holds + // NETWORK_STATS_PROVIDER. Thus, no additional permission check is required. + BinderUtils.withCleanCallingIdentity(() -> + mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */)); } @Override public void notifyWarningOrLimitReached() { Log.d(TAG, mTag + ": notifyWarningOrLimitReached"); - LocalServices.getService(NetworkPolicyManagerInternal.class) - .onStatsProviderWarningOrLimitReached(mTag); + BinderUtils.withCleanCallingIdentity(() -> + LocalServices.getService(NetworkPolicyManagerInternal.class) + .onStatsProviderWarningOrLimitReached(mTag)); } @Override @@ -2249,7 +2247,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private static native long nativeGetTotalStat(int type, boolean useBpfStats); - private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats); - private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats); + private static native long nativeGetTotalStat(int type); + private static native long nativeGetIfaceStat(String iface, int type); + private static native long nativeGetUidStat(int uid, int type); } diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS index 28ae6a417bd3..9c96d46f15b8 100644 --- a/services/core/java/com/android/server/net/OWNERS +++ b/services/core/java/com/android/server/net/OWNERS @@ -1,11 +1,6 @@ set noparent +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking -codewiz@google.com -jchalard@google.com jsharkey@android.com -junyulai@google.com -lorenzo@google.com -reminv@google.com -satk@google.com sudheersai@google.com yamasani@google.com diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java index 3019439a430b..5643873bef4d 100644 --- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java @@ -215,7 +215,7 @@ public class DefaultCrossProfileIntentFiltersUtils { private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH = new DefaultCrossProfileIntentFilter.Builder( DefaultCrossProfileIntentFilter.Direction.TO_PARENT, - /* flags= */0, + /* flags= */ ONLY_IF_NO_MATCH_FOUND, /* letsPersonalDataIntoProfile= */ false) .addAction(ACTION_RECOGNIZE_SPEECH) .addCategory(Intent.CATEGORY_DEFAULT) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 827dfc0caa16..8e18508adc90 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3538,9 +3538,9 @@ public class PackageManagerService extends IPackageManager.Stub list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); - } - if (listUninstalled) { - list.addAll(mApexManager.getInactivePackages()); + if (listUninstalled) { + list.addAll(mApexManager.getInactivePackages()); + } } } return new ParceledListSlice<>(list); @@ -21917,8 +21917,10 @@ public class PackageManagerService extends IPackageManager.Stub ApexManager.ActiveApexInfo apexInfo) { for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { ScanPartition sp = SYSTEM_PARTITIONS.get(i); - if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( - sp.getFolder().getAbsolutePath())) { + if (apexInfo.preInstalledApexPath.getAbsolutePath().equals( + sp.getFolder().getAbsolutePath()) + || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( + sp.getFolder().getAbsolutePath() + File.separator)) { return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX); } } diff --git a/services/core/java/com/android/server/pm/permission/OWNERS b/services/core/java/com/android/server/pm/permission/OWNERS index 8c1a90c13513..fc0ee23c4859 100644 --- a/services/core/java/com/android/server/pm/permission/OWNERS +++ b/services/core/java/com/android/server/pm/permission/OWNERS @@ -1,7 +1,3 @@ include platform/frameworks/base:/core/java/android/permission/OWNERS -per-file DefaultPermissionGrantPolicy.java = hackbod@android.com -per-file DefaultPermissionGrantPolicy.java = jsharkey@android.com -per-file DefaultPermissionGrantPolicy.java = toddke@google.com -per-file DefaultPermissionGrantPolicy.java = yamasani@google.com -per-file DefaultPermissionGrantPolicy.java = patb@google.com +per-file DefaultPermissionGrantPolicy.java = file:platform/frameworks/base:/core/java/android/permission/DEFAULT_PERMISSION_GRANT_POLICY_OWNERS diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java index cda48063e914..94e551a11dae 100644 --- a/services/core/java/com/android/server/pm/permission/Permission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -480,9 +480,10 @@ public final class Permission { r.append("DUP:"); r.append(permissionInfo.name); } - if (permission.isRuntime() && (ownerChanged || wasNonRuntime)) { - // If this is a runtime permission and the owner has changed, or this wasn't a runtime - // permission, then permission state should be cleaned up + if ((permission.isInternal() && ownerChanged) + || (permission.isRuntime() && (ownerChanged || wasNonRuntime))) { + // If this is an internal/runtime permission and the owner has changed, or this wasn't a + // runtime permission, then permission state should be cleaned up. permission.mDefinitionChanged = true; } if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 1133faabcf69..7b12709e4efd 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1643,7 +1643,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { isRolePermission = permission.isRole(); } final boolean mayRevokeRolePermission = isRolePermission - && mayManageRolePermission(callingUid); + // Allow ourselves to revoke role permissions due to definition changes. + && (callingUid == Process.myUid() || mayManageRolePermission(callingUid)); final boolean isRuntimePermission; synchronized (mLock) { @@ -2321,11 +2322,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int permNum = 0; permNum < numPermissions; permNum++) { final String permName = permissionsToRevoke.get(permNum); + final boolean isInternalPermission; synchronized (mLock) { final Permission bp = mRegistry.getPermission(permName); - if (bp == null || !bp.isRuntime()) { + if (bp == null || !(bp.isInternal() || bp.isRuntime())) { continue; } + isInternalPermission = bp.isInternal(); } mPackageManagerInt.forEachPackage(pkg -> { final String packageName = pkg.getPackageName(); @@ -2345,12 +2348,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (permissionState == PackageManager.PERMISSION_GRANTED && (flags & flagMask) == 0) { final int uid = UserHandle.getUid(userId, appId); - EventLog.writeEvent(0x534e4554, "154505240", uid, - "Revoking permission " + permName + " from package " - + packageName + " due to definition change"); - EventLog.writeEvent(0x534e4554, "168319670", uid, - "Revoking permission " + permName + " from package " - + packageName + " due to definition change"); + if (isInternalPermission) { + EventLog.writeEvent(0x534e4554, "195338390", uid, + "Revoking permission " + permName + " from package " + + packageName + " due to definition change"); + } else { + EventLog.writeEvent(0x534e4554, "154505240", uid, + "Revoking permission " + permName + " from package " + + packageName + " due to definition change"); + EventLog.writeEvent(0x534e4554, "168319670", uid, + "Revoking permission " + permName + " from package " + + packageName + " due to definition change"); + } Slog.e(TAG, "Revoking permission " + permName + " from package " + packageName + " due to definition change"); try { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index e40882268e67..4a9772093bb7 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2666,6 +2666,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in" + " interceptKeyBeforeQueueing"); return key_consumed; + case KeyEvent.KEYCODE_VIDEO_APP_1: + case KeyEvent.KEYCODE_VIDEO_APP_2: + case KeyEvent.KEYCODE_VIDEO_APP_3: + case KeyEvent.KEYCODE_VIDEO_APP_4: + case KeyEvent.KEYCODE_VIDEO_APP_5: + case KeyEvent.KEYCODE_VIDEO_APP_6: + case KeyEvent.KEYCODE_VIDEO_APP_7: + case KeyEvent.KEYCODE_VIDEO_APP_8: + case KeyEvent.KEYCODE_FEATURED_APP_1: + case KeyEvent.KEYCODE_FEATURED_APP_2: + case KeyEvent.KEYCODE_FEATURED_APP_3: + case KeyEvent.KEYCODE_FEATURED_APP_4: + case KeyEvent.KEYCODE_DEMO_APP_1: + case KeyEvent.KEYCODE_DEMO_APP_2: + case KeyEvent.KEYCODE_DEMO_APP_3: + case KeyEvent.KEYCODE_DEMO_APP_4: + Slog.wtf(TAG, "KEYCODE_APP_X should be handled in interceptKeyBeforeQueueing"); + return key_consumed; case KeyEvent.KEYCODE_SYSRQ: if (down && repeatCount == 0) { mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); @@ -3773,6 +3791,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { } break; } + case KeyEvent.KEYCODE_VIDEO_APP_1: + case KeyEvent.KEYCODE_VIDEO_APP_2: + case KeyEvent.KEYCODE_VIDEO_APP_3: + case KeyEvent.KEYCODE_VIDEO_APP_4: + case KeyEvent.KEYCODE_VIDEO_APP_5: + case KeyEvent.KEYCODE_VIDEO_APP_6: + case KeyEvent.KEYCODE_VIDEO_APP_7: + case KeyEvent.KEYCODE_VIDEO_APP_8: + case KeyEvent.KEYCODE_FEATURED_APP_1: + case KeyEvent.KEYCODE_FEATURED_APP_2: + case KeyEvent.KEYCODE_FEATURED_APP_3: + case KeyEvent.KEYCODE_FEATURED_APP_4: + case KeyEvent.KEYCODE_DEMO_APP_1: + case KeyEvent.KEYCODE_DEMO_APP_2: + case KeyEvent.KEYCODE_DEMO_APP_3: + case KeyEvent.KEYCODE_DEMO_APP_4: { + // Just drop if keys are not intercepted for direct key. + result &= ~ACTION_PASS_TO_USER; + break; + } } // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users. diff --git a/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java b/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java new file mode 100644 index 000000000000..0d420a535415 --- /dev/null +++ b/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java @@ -0,0 +1,98 @@ +/* + * 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.bootstrap; + +import android.content.Context; +import android.os.IStatsBootstrapAtomService; +import android.os.StatsBootstrapAtom; +import android.os.StatsBootstrapAtomValue; +import android.util.Slog; +import android.util.StatsEvent; +import android.util.StatsLog; + +import com.android.server.SystemService; + +/** + * Proxy service for logging pushed atoms to statsd + * + * @hide + */ +public class StatsBootstrapAtomService extends IStatsBootstrapAtomService.Stub { + + private static final String TAG = "StatsBootstrapAtomService"; + private static final boolean DEBUG = false; + + @Override + public void reportBootstrapAtom(StatsBootstrapAtom atom) { + if (atom.atomId < 1 || atom.atomId >= 10000) { + Slog.e(TAG, "Atom ID " + atom.atomId + " is not a valid atom ID"); + return; + } + StatsEvent.Builder builder = StatsEvent.newBuilder().setAtomId(atom.atomId); + for (StatsBootstrapAtomValue value : atom.values) { + switch (value.getTag()) { + case StatsBootstrapAtomValue.boolValue: + builder.writeBoolean(value.getBoolValue()); + break; + case StatsBootstrapAtomValue.intValue: + builder.writeInt(value.getIntValue()); + break; + case StatsBootstrapAtomValue.longValue: + builder.writeLong(value.getLongValue()); + break; + case StatsBootstrapAtomValue.floatValue: + builder.writeFloat(value.getFloatValue()); + break; + case StatsBootstrapAtomValue.stringValue: + builder.writeString(value.getStringValue()); + break; + case StatsBootstrapAtomValue.bytesValue: + builder.writeByteArray(value.getBytesValue()); + break; + default: + Slog.e(TAG, "Unexpected value type " + value.getTag() + + " when logging atom " + atom.atomId); + return; + + } + } + StatsLog.write(builder.usePooledBuffer().build()); + } + + /** + * Lifecycle and related code + */ + public static final class Lifecycle extends SystemService { + private StatsBootstrapAtomService mStatsBootstrapAtomService; + + public Lifecycle(Context context) { + super(context); + } + + @Override + public void onStart() { + mStatsBootstrapAtomService = new StatsBootstrapAtomService(); + try { + publishBinderService(Context.STATS_BOOTSTRAP_ATOM_SERVICE, + mStatsBootstrapAtomService); + if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_BOOTSTRAP_ATOM_SERVICE); + } catch (Exception e) { + Slog.e(TAG, "Failed to publishBinderService", e); + } + } + } + +} diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 68b760a1be34..1ef202511452 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -92,7 +92,6 @@ import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; -import android.hardware.health.V2_0.IHealth; import android.net.ConnectivityManager; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; @@ -190,13 +189,13 @@ import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThrea import com.android.internal.util.CollectionUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.role.RoleManagerLocal; -import com.android.server.BatteryService; import com.android.server.BinderCallsStatsService; import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.am.MemoryStatUtil.MemoryStat; +import com.android.server.health.HealthServiceWrapper; import com.android.server.notification.NotificationManagerService; import com.android.server.stats.pull.IonMemoryUtil.IonAllocations; import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot; @@ -226,6 +225,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.MissingResourceException; +import java.util.NoSuchElementException; import java.util.Random; import java.util.Set; import java.util.UUID; @@ -354,7 +354,7 @@ public class StatsPullAtomService extends SystemService { private File mBaseDir; @GuardedBy("mHealthHalLock") - private BatteryService.HealthServiceWrapper mHealthService; + private HealthServiceWrapper mHealthService; @Nullable @GuardedBy("mCpuTimePerThreadFreqLock") @@ -799,10 +799,9 @@ public class StatsPullAtomService extends SystemService { KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext); // Initialize HealthService - mHealthService = new BatteryService.HealthServiceWrapper(); try { - mHealthService.init(); - } catch (RemoteException e) { + mHealthService = HealthServiceWrapper.create(null); + } catch (RemoteException | NoSuchElementException e) { Slog.e(TAG, "failed to initialize healthHalWrapper"); } @@ -3975,38 +3974,40 @@ public class StatsPullAtomService extends SystemService { } int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) { - IHealth healthService = mHealthService.getLastService(); - if (healthService == null) { + if (mHealthService == null) { return StatsManager.PULL_SKIP; } + android.hardware.health.HealthInfo healthInfo; try { - healthService.getHealthInfo((result, value) -> { - int pulledValue; - switch(atomTag) { - case FrameworkStatsLog.BATTERY_LEVEL: - pulledValue = value.legacy.batteryLevel; - break; - case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY: - pulledValue = value.legacy.batteryChargeCounter; - break; - case FrameworkStatsLog.FULL_BATTERY_CAPACITY: - pulledValue = value.legacy.batteryFullCharge; - break; - case FrameworkStatsLog.BATTERY_VOLTAGE: - pulledValue = value.legacy.batteryVoltage; - break; - case FrameworkStatsLog.BATTERY_CYCLE_COUNT: - pulledValue = value.legacy.batteryCycleCount; - break; - default: - throw new IllegalStateException("Invalid atomTag in healthHal puller: " - + atomTag); - } - pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue)); - }); + healthInfo = mHealthService.getHealthInfo(); } catch (RemoteException | IllegalStateException e) { return StatsManager.PULL_SKIP; } + if (healthInfo == null) { + return StatsManager.PULL_SKIP; + } + + int pulledValue; + switch (atomTag) { + case FrameworkStatsLog.BATTERY_LEVEL: + pulledValue = healthInfo.batteryLevel; + break; + case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY: + pulledValue = healthInfo.batteryChargeCounterUah; + break; + case FrameworkStatsLog.FULL_BATTERY_CAPACITY: + pulledValue = healthInfo.batteryFullChargeUah; + break; + case FrameworkStatsLog.BATTERY_VOLTAGE: + pulledValue = healthInfo.batteryVoltageMillivolts; + break; + case FrameworkStatsLog.BATTERY_CYCLE_COUNT: + pulledValue = healthInfo.batteryCycleCount; + break; + default: + return StatsManager.PULL_SKIP; + } + pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue)); return StatsManager.PULL_SUCCESS; } diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java index b00540fd2a52..292314840efb 100644 --- a/services/core/java/com/android/server/storage/AppFuseBridge.java +++ b/services/core/java/com/android/server/storage/AppFuseBridge.java @@ -24,7 +24,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.util.Preconditions; -import com.android.server.NativeDaemonConnectorException; +import com.android.server.AppFuseMountException; import libcore.io.IoUtils; import java.util.concurrent.CountDownLatch; @@ -55,7 +55,7 @@ public class AppFuseBridge implements Runnable { } public ParcelFileDescriptor addBridge(MountScope mountScope) - throws FuseUnavailableMountException, NativeDaemonConnectorException { + throws FuseUnavailableMountException, AppFuseMountException { /* ** Dead Lock between Java lock (AppFuseBridge.java) and Native lock (FuseBridgeLoop.cc) ** @@ -112,7 +112,7 @@ public class AppFuseBridge implements Runnable { try { int flags = FileUtils.translateModePfdToPosix(mode); return scope.openFile(mountId, fileId, flags); - } catch (NativeDaemonConnectorException error) { + } catch (AppFuseMountException error) { throw new FuseUnavailableMountException(mountId); } } @@ -160,9 +160,9 @@ public class AppFuseBridge implements Runnable { return mMountResult; } - public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException; + public abstract ParcelFileDescriptor open() throws AppFuseMountException; public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags) - throws NativeDaemonConnectorException; + throws AppFuseMountException; } private native long native_new(); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 36a854e5374c..2f54f302af6c 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -315,6 +315,7 @@ public final class TvInputManagerService extends SystemService { PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, userId); List<TvInputInfo> inputList = new ArrayList<>(); + List<ComponentName> hardwareComponents = new ArrayList<>(); for (ResolveInfo ri : services) { ServiceInfo si = ri.serviceInfo; if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { @@ -325,6 +326,7 @@ public final class TvInputManagerService extends SystemService { ComponentName component = new ComponentName(si.packageName, si.name); if (hasHardwarePermission(pm, component)) { + hardwareComponents.add(component); ServiceState serviceState = userState.serviceStateMap.get(component); if (serviceState == null) { // New hardware input found. Create a new ServiceState and connect to the @@ -397,6 +399,15 @@ public final class TvInputManagerService extends SystemService { } } + // Clean up ServiceState corresponding to the removed hardware inputs + Iterator<ServiceState> it = userState.serviceStateMap.values().iterator(); + while (it.hasNext()) { + ServiceState serviceState = it.next(); + if (serviceState.isHardware && !hardwareComponents.contains(serviceState.component)) { + it.remove(); + } + } + userState.inputMap.clear(); userState.inputMap = inputMap; } @@ -2304,10 +2315,9 @@ public final class TvInputManagerService extends SystemService { public void requestChannelBrowsable(Uri channelUri, int userId) throws RemoteException { final String callingPackageName = getCallingPackageName(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), + Binder.getCallingUid(), userId, "requestChannelBrowsable"); final long identity = Binder.clearCallingIdentity(); - final int callingUid = Binder.getCallingUid(); - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, - userId, "requestChannelBrowsable"); try { Intent intent = new Intent(TvContract.ACTION_CHANNEL_BROWSABLE_REQUESTED); List<ResolveInfo> list = getContext().getPackageManager() @@ -2975,32 +2985,47 @@ public final class TvInputManagerService extends SystemService { public void addHardwareInput(int deviceId, TvInputInfo inputInfo) { ensureHardwarePermission(); ensureValidInput(inputInfo); - synchronized (mLock) { - mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo); - addHardwareInputLocked(inputInfo); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo); + addHardwareInputLocked(inputInfo); + } + } finally { + Binder.restoreCallingIdentity(identity); } } public void addHdmiInput(int id, TvInputInfo inputInfo) { ensureHardwarePermission(); ensureValidInput(inputInfo); - synchronized (mLock) { - mTvInputHardwareManager.addHdmiInput(id, inputInfo); - addHardwareInputLocked(inputInfo); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + mTvInputHardwareManager.addHdmiInput(id, inputInfo); + addHardwareInputLocked(inputInfo); + } + } finally { + Binder.restoreCallingIdentity(identity); } } public void removeHardwareInput(String inputId) { ensureHardwarePermission(); - synchronized (mLock) { - ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); - boolean removed = serviceState.hardwareInputMap.remove(inputId) != null; - if (removed) { - buildTvInputListLocked(mUserId, null); - mTvInputHardwareManager.removeHardwareInput(inputId); - } else { - Slog.e(TAG, "failed to remove input " + inputId); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); + boolean removed = serviceState.hardwareInputMap.remove(inputId) != null; + if (removed) { + buildTvInputListLocked(mUserId, null); + mTvInputHardwareManager.removeHardwareInput(inputId); + } else { + Slog.e(TAG, "failed to remove input " + inputId); + } } + } finally { + Binder.restoreCallingIdentity(identity); } } } diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 7dec4e785f5c..584530c0f3bd 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -77,6 +77,7 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.Process; import android.os.SystemClock; +import android.provider.Settings; import android.util.ArraySet; import android.util.Slog; @@ -87,9 +88,10 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; -import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; +import com.android.server.vcn.routeselection.UnderlyingNetworkController; +import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback; +import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; import com.android.server.vcn.util.LogUtils; import com.android.server.vcn.util.MtuUtils; import com.android.server.vcn.util.OneWayBoolean; @@ -98,6 +100,7 @@ import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.NetworkInterface; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -200,7 +203,7 @@ public class VcnGatewayConnection extends StateMachine { private interface EventInfo {} /** - * Sent when there are changes to the underlying network (per the UnderlyingNetworkTracker). + * Sent when there are changes to the underlying network (per the UnderlyingNetworkController). * * <p>May indicate an entirely new underlying network, OR a change in network properties. * @@ -521,11 +524,14 @@ public class VcnGatewayConnection extends StateMachine { @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; - @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker; + @NonNull private final UnderlyingNetworkController mUnderlyingNetworkController; @NonNull private final VcnGatewayConnectionConfig mConnectionConfig; @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback; @NonNull private final Dependencies mDeps; - @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback; + + @NonNull + private final VcnUnderlyingNetworkControllerCallback mUnderlyingNetworkControllerCallback; + private final boolean mIsMobileDataEnabled; @NonNull private final IpSecManager mIpSecManager; @@ -673,17 +679,17 @@ public class VcnGatewayConnection extends StateMachine { mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); - mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback(); + mUnderlyingNetworkControllerCallback = new VcnUnderlyingNetworkControllerCallback(); mWakeLock = mDeps.newWakeLock(mVcnContext.getContext(), PowerManager.PARTIAL_WAKE_LOCK, TAG); - mUnderlyingNetworkTracker = - mDeps.newUnderlyingNetworkTracker( + mUnderlyingNetworkController = + mDeps.newUnderlyingNetworkController( mVcnContext, subscriptionGroup, mLastSnapshot, - mUnderlyingNetworkTrackerCallback); + mUnderlyingNetworkControllerCallback); mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class); addState(mDisconnectedState); @@ -747,7 +753,7 @@ public class VcnGatewayConnection extends StateMachine { cancelRetryTimeoutAlarm(); cancelSafeModeAlarm(); - mUnderlyingNetworkTracker.teardown(); + mUnderlyingNetworkController.teardown(); mGatewayStatusCallback.onQuit(); } @@ -763,12 +769,13 @@ public class VcnGatewayConnection extends StateMachine { mVcnContext.ensureRunningOnLooperThread(); mLastSnapshot = snapshot; - mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot); + mUnderlyingNetworkController.updateSubscriptionSnapshot(mLastSnapshot); sendMessageAndAcquireWakeLock(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL); } - private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback { + private class VcnUnderlyingNetworkControllerCallback + implements UnderlyingNetworkControllerCallback { @Override public void onSelectedUnderlyingNetworkChanged( @Nullable UnderlyingNetworkRecord underlying) { @@ -782,8 +789,19 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/179091925): Move the delayed-message handling to BaseState // If underlying is null, all underlying networks have been lost. Disconnect VCN after a - // timeout. + // timeout (or immediately if in airplane mode, since the device user has indicated that + // the radios should all be turned off). if (underlying == null) { + if (mDeps.isAirplaneModeOn(mVcnContext)) { + sendMessageAndAcquireWakeLock( + EVENT_UNDERLYING_NETWORK_CHANGED, + TOKEN_ALL, + new EventUnderlyingNetworkChangedInfo(null)); + sendDisconnectRequestedAndAcquireWakelock( + DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */); + return; + } + setDisconnectRequestAlarm(); } else { // Received a new Network so any previous alarm is irrelevant - cancel + clear it, @@ -1663,8 +1681,6 @@ public class VcnGatewayConnection extends StateMachine { } /* validationStatusCallback */); agent.register(); - agent.setUnderlyingNetworks( - mUnderlying == null ? null : Collections.singletonList(mUnderlying.network)); agent.markConnected(); return agent; @@ -2039,6 +2055,7 @@ public class VcnGatewayConnection extends StateMachine { "Unknown transport type or missing TransportInfo/NetworkSpecifier for" + " non-null underlying network"); } + builder.setUnderlyingNetworks(List.of(underlying.network)); } else { Slog.wtf( TAG, @@ -2049,7 +2066,8 @@ public class VcnGatewayConnection extends StateMachine { return builder.build(); } - private static LinkProperties buildConnectedLinkProperties( + @VisibleForTesting(visibility = Visibility.PRIVATE) + LinkProperties buildConnectedLinkProperties( @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig, @@ -2077,6 +2095,13 @@ public class VcnGatewayConnection extends StateMachine { lp.setTcpBufferSizes(underlyingLp.getTcpBufferSizes()); underlyingMtu = underlyingLp.getMtu(); + + // WiFi LinkProperties uses DHCP as the sole source of MTU information, and as a result + // often lists MTU as 0 (see b/184678973). Use the interface MTU as retrieved by + // NetworkInterface APIs. + if (underlyingMtu == 0 && underlyingLp.getInterfaceName() != null) { + underlyingMtu = mDeps.getUnderlyingIfaceMtu(underlyingLp.getInterfaceName()); + } } else { Slog.wtf( TAG, @@ -2256,7 +2281,7 @@ public class VcnGatewayConnection extends StateMachine { + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork())); pw.println(); - mUnderlyingNetworkTracker.dump(pw); + mUnderlyingNetworkController.dump(pw); pw.println(); pw.decreaseIndent(); @@ -2268,8 +2293,8 @@ public class VcnGatewayConnection extends StateMachine { } @VisibleForTesting(visibility = Visibility.PRIVATE) - UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() { - return mUnderlyingNetworkTrackerCallback; + UnderlyingNetworkControllerCallback getUnderlyingNetworkControllerCallback() { + return mUnderlyingNetworkControllerCallback; } @VisibleForTesting(visibility = Visibility.PRIVATE) @@ -2348,17 +2373,14 @@ public class VcnGatewayConnection extends StateMachine { /** External dependencies used by VcnGatewayConnection, for injection in tests */ @VisibleForTesting(visibility = Visibility.PRIVATE) public static class Dependencies { - /** Builds a new UnderlyingNetworkTracker. */ - public UnderlyingNetworkTracker newUnderlyingNetworkTracker( + /** Builds a new UnderlyingNetworkController. */ + public UnderlyingNetworkController newUnderlyingNetworkController( VcnContext vcnContext, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkTrackerCallback callback) { - return new UnderlyingNetworkTracker( - vcnContext, - subscriptionGroup, - snapshot, - callback); + UnderlyingNetworkControllerCallback callback) { + return new UnderlyingNetworkController( + vcnContext, subscriptionGroup, snapshot, callback); } /** Builds a new IkeSession. */ @@ -2414,10 +2436,27 @@ public class VcnGatewayConnection extends StateMachine { validationStatusCallback); } + /** Checks if airplane mode is enabled. */ + public boolean isAirplaneModeOn(@NonNull VcnContext vcnContext) { + return Settings.Global.getInt(vcnContext.getContext().getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) != 0; + } + /** Gets the elapsed real time since boot, in millis. */ public long getElapsedRealTime() { return SystemClock.elapsedRealtime(); } + + /** Gets the MTU for the given underlying interface. */ + public int getUnderlyingIfaceMtu(String ifaceName) { + try { + final NetworkInterface underlyingIface = NetworkInterface.getByName(ifaceName); + return underlyingIface == null ? 0 : underlyingIface.getMTU(); + } catch (IOException e) { + Slog.d(TAG, "Could not get MTU of underlying network", e); + return 0; + } + } } /** diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java new file mode 100644 index 000000000000..bea8ae932a9d --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java @@ -0,0 +1,196 @@ +/* + * 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.vcn.routeselection; + +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + +import static com.android.server.VcnManagementService.LOCAL_LOG; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.NetworkCapabilities; +import android.net.vcn.VcnManager; +import android.os.ParcelUuid; +import android.os.PersistableBundle; +import android.telephony.SubscriptionManager; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; + +import java.util.Set; + +/** @hide */ +class NetworkPriorityClassifier { + @NonNull private static final String TAG = NetworkPriorityClassifier.class.getSimpleName(); + /** + * Minimum signal strength for a WiFi network to be eligible for switching to + * + * <p>A network that satisfies this is eligible to become the selected underlying network with + * no additional conditions + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70; + /** + * Minimum signal strength to continue using a WiFi network + * + * <p>A network that satisfies the conditions may ONLY continue to be used if it is already + * selected as the underlying network. A WiFi network satisfying this condition, but NOT the + * prospective-network RSSI threshold CANNOT be switched to. + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74; + /** Priority for any cellular network for which the subscription is listed as opportunistic */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0; + /** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_WIFI_IN_USE = 1; + /** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_WIFI_PROSPECTIVE = 2; + /** Priority for any standard macro cellular network */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_MACRO_CELLULAR = 3; + /** Priority for any other networks (including unvalidated, etc) */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int PRIORITY_ANY = Integer.MAX_VALUE; + + private static final SparseArray<String> PRIORITY_TO_STRING_MAP = new SparseArray<>(); + + static { + PRIORITY_TO_STRING_MAP.put( + PRIORITY_OPPORTUNISTIC_CELLULAR, "PRIORITY_OPPORTUNISTIC_CELLULAR"); + PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_IN_USE, "PRIORITY_WIFI_IN_USE"); + PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_PROSPECTIVE, "PRIORITY_WIFI_PROSPECTIVE"); + PRIORITY_TO_STRING_MAP.put(PRIORITY_MACRO_CELLULAR, "PRIORITY_MACRO_CELLULAR"); + PRIORITY_TO_STRING_MAP.put(PRIORITY_ANY, "PRIORITY_ANY"); + } + + /** + * Gives networks a priority class, based on the following priorities: + * + * <ol> + * <li>Opportunistic cellular + * <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT + * <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT + * <li>Macro cellular + * <li>Any others + * </ol> + */ + static int calculatePriorityClass( + UnderlyingNetworkRecord networkRecord, + ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, + UnderlyingNetworkRecord currentlySelected, + PersistableBundle carrierConfig) { + final NetworkCapabilities caps = networkRecord.networkCapabilities; + + // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED + + if (networkRecord.isBlocked) { + logWtf("Network blocked for System Server: " + networkRecord.network); + return PRIORITY_ANY; + } + + if (caps.hasTransport(TRANSPORT_CELLULAR) + && isOpportunistic(snapshot, caps.getSubscriptionIds())) { + // If this carrier is the active data provider, ensure that opportunistic is only + // ever prioritized if it is also the active data subscription. This ensures that + // if an opportunistic subscription is still in the process of being switched to, + // or switched away from, the VCN does not attempt to continue using it against the + // decision made at the telephony layer. Failure to do so may result in the modem + // switching back and forth. + // + // Allow the following two cases: + // 1. Active subId is NOT in the group that this VCN is supporting + // 2. This opportunistic subscription is for the active subId + if (!snapshot.getAllSubIdsInGroup(subscriptionGroup) + .contains(SubscriptionManager.getActiveDataSubscriptionId()) + || caps.getSubscriptionIds() + .contains(SubscriptionManager.getActiveDataSubscriptionId())) { + return PRIORITY_OPPORTUNISTIC_CELLULAR; + } + } + + if (caps.hasTransport(TRANSPORT_WIFI)) { + if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig) + && currentlySelected != null + && networkRecord.network.equals(currentlySelected.network)) { + return PRIORITY_WIFI_IN_USE; + } + + if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) { + return PRIORITY_WIFI_PROSPECTIVE; + } + } + + // Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might + // be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be + // the case if the Default Data SubId does not support certain services (eg voice + // calling) + if (caps.hasTransport(TRANSPORT_CELLULAR) + && !isOpportunistic(snapshot, caps.getSubscriptionIds())) { + return PRIORITY_MACRO_CELLULAR; + } + + return PRIORITY_ANY; + } + + static boolean isOpportunistic( + @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) { + if (snapshot == null) { + logWtf("Got null snapshot"); + return false; + } + for (int subId : subIds) { + if (snapshot.isOpportunistic(subId)) { + return true; + } + } + return false; + } + + static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) { + if (carrierConfig != null) { + return carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, + WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT); + } + return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT; + } + + static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) { + if (carrierConfig != null) { + return carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + WIFI_EXIT_RSSI_THRESHOLD_DEFAULT); + } + return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT; + } + + static String priorityClassToString(int priorityClass) { + return PRIORITY_TO_STRING_MAP.get(priorityClass, "unknown"); + } + + private static void logWtf(String msg) { + Slog.wtf(TAG, msg); + LOCAL_LOG.log(TAG + " WTF: " + msg); + } +} diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index 7ddd1355a2d6..071c7a683cbf 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package com.android.server.vcn; +package com.android.server.vcn.routeselection; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; import static com.android.server.VcnManagementService.LOCAL_LOG; +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold; +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold; +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.isOpportunistic; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,27 +32,23 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.TelephonyNetworkSpecifier; -import android.net.vcn.VcnManager; import android.os.Handler; import android.os.HandlerExecutor; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.Slog; -import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.VcnContext; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -61,68 +58,18 @@ import java.util.TreeSet; /** * Tracks a set of Networks underpinning a VcnGatewayConnection. * - * <p>A single UnderlyingNetworkTracker is built to serve a SINGLE VCN Gateway Connection, and MUST - * be torn down with the VcnGatewayConnection in order to ensure underlying networks are allowed to - * be reaped. + * <p>A single UnderlyingNetworkController is built to serve a SINGLE VCN Gateway Connection, and + * MUST be torn down with the VcnGatewayConnection in order to ensure underlying networks are + * allowed to be reaped. * * @hide */ -public class UnderlyingNetworkTracker { - @NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName(); - - /** - * Minimum signal strength for a WiFi network to be eligible for switching to - * - * <p>A network that satisfies this is eligible to become the selected underlying network with - * no additional conditions - */ - @VisibleForTesting(visibility = Visibility.PRIVATE) - static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70; - - /** - * Minimum signal strength to continue using a WiFi network - * - * <p>A network that satisfies the conditions may ONLY continue to be used if it is already - * selected as the underlying network. A WiFi network satisfying this condition, but NOT the - * prospective-network RSSI threshold CANNOT be switched to. - */ - @VisibleForTesting(visibility = Visibility.PRIVATE) - static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74; - - /** Priority for any cellular network for which the subscription is listed as opportunistic */ - @VisibleForTesting(visibility = Visibility.PRIVATE) - static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0; - - /** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */ - @VisibleForTesting(visibility = Visibility.PRIVATE) - static final int PRIORITY_WIFI_IN_USE = 1; - - /** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */ - @VisibleForTesting(visibility = Visibility.PRIVATE) - static final int PRIORITY_WIFI_PROSPECTIVE = 2; - - /** Priority for any standard macro cellular network */ - @VisibleForTesting(visibility = Visibility.PRIVATE) - static final int PRIORITY_MACRO_CELLULAR = 3; - - /** Priority for any other networks (including unvalidated, etc) */ - @VisibleForTesting(visibility = Visibility.PRIVATE) - static final int PRIORITY_ANY = Integer.MAX_VALUE; - - private static final SparseArray<String> PRIORITY_TO_STRING_MAP = new SparseArray<>(); - - static { - PRIORITY_TO_STRING_MAP.put( - PRIORITY_OPPORTUNISTIC_CELLULAR, "PRIORITY_OPPORTUNISTIC_CELLULAR"); - PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_IN_USE, "PRIORITY_WIFI_IN_USE"); - PRIORITY_TO_STRING_MAP.put(PRIORITY_WIFI_PROSPECTIVE, "PRIORITY_WIFI_PROSPECTIVE"); - PRIORITY_TO_STRING_MAP.put(PRIORITY_MACRO_CELLULAR, "PRIORITY_MACRO_CELLULAR"); - PRIORITY_TO_STRING_MAP.put(PRIORITY_ANY, "PRIORITY_ANY"); - } +public class UnderlyingNetworkController { + @NonNull private static final String TAG = UnderlyingNetworkController.class.getSimpleName(); @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; - @NonNull private final UnderlyingNetworkTrackerCallback mCb; + @NonNull private final UnderlyingNetworkControllerCallback mCb; @NonNull private final Dependencies mDeps; @NonNull private final Handler mHandler; @NonNull private final ConnectivityManager mConnectivityManager; @@ -142,11 +89,11 @@ public class UnderlyingNetworkTracker { @Nullable private UnderlyingNetworkRecord mCurrentRecord; @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress; - public UnderlyingNetworkTracker( + public UnderlyingNetworkController( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, - @NonNull UnderlyingNetworkTrackerCallback cb) { + @NonNull UnderlyingNetworkControllerCallback cb) { this( vcnContext, subscriptionGroup, @@ -155,11 +102,11 @@ public class UnderlyingNetworkTracker { new Dependencies()); } - private UnderlyingNetworkTracker( + private UnderlyingNetworkController( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, - @NonNull UnderlyingNetworkTrackerCallback cb, + @NonNull UnderlyingNetworkControllerCallback cb, @NonNull Dependencies deps) { mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); @@ -271,8 +218,8 @@ public class UnderlyingNetworkTracker { * subscription group, while the VCN networks are excluded by virtue of not having subIds set on * the VCN-exposed networks. * - * <p>If the VCN that this UnderlyingNetworkTracker belongs to is in test-mode, this will return - * a NetworkRequest that only matches Test Networks. + * <p>If the VCN that this UnderlyingNetworkController belongs to is in test-mode, this will + * return a NetworkRequest that only matches Test Networks. */ private NetworkRequest getRouteSelectionRequest() { if (mVcnContext.isInTestMode()) { @@ -373,9 +320,9 @@ public class UnderlyingNetworkTracker { } /** - * Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot. + * Update this UnderlyingNetworkController's TelephonySubscriptionSnapshot. * - * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkTracker to + * <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkController to * reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered * or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change. */ @@ -410,7 +357,7 @@ public class UnderlyingNetworkTracker { private void reevaluateNetworks() { if (mIsQuitting || mRouteSelectionCallback == null) { - return; // UnderlyingNetworkTracker has quit. + return; // UnderlyingNetworkController has quit. } TreeSet<UnderlyingNetworkRecord> sorted = @@ -424,22 +371,6 @@ public class UnderlyingNetworkTracker { mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); } - private static boolean isOpportunistic( - @NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) { - if (snapshot == null) { - logWtf("Got null snapshot"); - return false; - } - - for (int subId : subIds) { - if (snapshot.isOpportunistic(subId)) { - return true; - } - } - - return false; - } - /** * NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped. * @@ -544,230 +475,6 @@ public class UnderlyingNetworkTracker { } } - private static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) { - if (carrierConfig != null) { - return carrierConfig.getInt( - VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, - WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT); - } - - return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT; - } - - private static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) { - if (carrierConfig != null) { - return carrierConfig.getInt( - VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, - WIFI_EXIT_RSSI_THRESHOLD_DEFAULT); - } - - return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT; - } - - /** A record of a single underlying network, caching relevant fields. */ - public static class UnderlyingNetworkRecord { - @NonNull public final Network network; - @NonNull public final NetworkCapabilities networkCapabilities; - @NonNull public final LinkProperties linkProperties; - public final boolean isBlocked; - - @VisibleForTesting(visibility = Visibility.PRIVATE) - UnderlyingNetworkRecord( - @NonNull Network network, - @NonNull NetworkCapabilities networkCapabilities, - @NonNull LinkProperties linkProperties, - boolean isBlocked) { - this.network = network; - this.networkCapabilities = networkCapabilities; - this.linkProperties = linkProperties; - this.isBlocked = isBlocked; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof UnderlyingNetworkRecord)) return false; - final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o; - - return network.equals(that.network) - && networkCapabilities.equals(that.networkCapabilities) - && linkProperties.equals(that.linkProperties) - && isBlocked == that.isBlocked; - } - - @Override - public int hashCode() { - return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); - } - - /** - * Gives networks a priority class, based on the following priorities: - * - * <ol> - * <li>Opportunistic cellular - * <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT - * <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT - * <li>Macro cellular - * <li>Any others - * </ol> - */ - private int calculatePriorityClass( - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { - final NetworkCapabilities caps = networkCapabilities; - - // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED - - if (isBlocked) { - logWtf("Network blocked for System Server: " + network); - return PRIORITY_ANY; - } - - if (caps.hasTransport(TRANSPORT_CELLULAR) - && isOpportunistic(snapshot, caps.getSubscriptionIds())) { - // If this carrier is the active data provider, ensure that opportunistic is only - // ever prioritized if it is also the active data subscription. This ensures that - // if an opportunistic subscription is still in the process of being switched to, - // or switched away from, the VCN does not attempt to continue using it against the - // decision made at the telephony layer. Failure to do so may result in the modem - // switching back and forth. - // - // Allow the following two cases: - // 1. Active subId is NOT in the group that this VCN is supporting - // 2. This opportunistic subscription is for the active subId - if (!snapshot.getAllSubIdsInGroup(subscriptionGroup) - .contains(SubscriptionManager.getActiveDataSubscriptionId()) - || caps.getSubscriptionIds() - .contains(SubscriptionManager.getActiveDataSubscriptionId())) { - return PRIORITY_OPPORTUNISTIC_CELLULAR; - } - } - - if (caps.hasTransport(TRANSPORT_WIFI)) { - if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig) - && currentlySelected != null - && network.equals(currentlySelected.network)) { - return PRIORITY_WIFI_IN_USE; - } - - if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) { - return PRIORITY_WIFI_PROSPECTIVE; - } - } - - // Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might - // be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be - // the case if the Default Data SubId does not support certain services (eg voice - // calling) - if (caps.hasTransport(TRANSPORT_CELLULAR) - && !isOpportunistic(snapshot, caps.getSubscriptionIds())) { - return PRIORITY_MACRO_CELLULAR; - } - - return PRIORITY_ANY; - } - - private static Comparator<UnderlyingNetworkRecord> getComparator( - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { - return (left, right) -> { - return Integer.compare( - left.calculatePriorityClass( - subscriptionGroup, snapshot, currentlySelected, carrierConfig), - right.calculatePriorityClass( - subscriptionGroup, snapshot, currentlySelected, carrierConfig)); - }; - } - - /** Dumps the state of this record for logging and debugging purposes. */ - private void dump( - IndentingPrintWriter pw, - ParcelUuid subscriptionGroup, - TelephonySubscriptionSnapshot snapshot, - UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { - pw.println("UnderlyingNetworkRecord:"); - pw.increaseIndent(); - - final int priorityClass = - calculatePriorityClass( - subscriptionGroup, snapshot, currentlySelected, carrierConfig); - pw.println( - "Priority class: " + PRIORITY_TO_STRING_MAP.get(priorityClass) + " (" - + priorityClass + ")"); - pw.println("mNetwork: " + network); - pw.println("mNetworkCapabilities: " + networkCapabilities); - pw.println("mLinkProperties: " + linkProperties); - - pw.decreaseIndent(); - } - - /** Builder to incrementally construct an UnderlyingNetworkRecord. */ - private static class Builder { - @NonNull private final Network mNetwork; - - @Nullable private NetworkCapabilities mNetworkCapabilities; - @Nullable private LinkProperties mLinkProperties; - boolean mIsBlocked; - boolean mWasIsBlockedSet; - - @Nullable private UnderlyingNetworkRecord mCached; - - private Builder(@NonNull Network network) { - mNetwork = network; - } - - @NonNull - private Network getNetwork() { - return mNetwork; - } - - private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) { - mNetworkCapabilities = networkCapabilities; - mCached = null; - } - - @Nullable - private NetworkCapabilities getNetworkCapabilities() { - return mNetworkCapabilities; - } - - private void setLinkProperties(@NonNull LinkProperties linkProperties) { - mLinkProperties = linkProperties; - mCached = null; - } - - private void setIsBlocked(boolean isBlocked) { - mIsBlocked = isBlocked; - mWasIsBlockedSet = true; - mCached = null; - } - - private boolean isValid() { - return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet; - } - - private UnderlyingNetworkRecord build() { - if (!isValid()) { - throw new IllegalArgumentException( - "Called build before UnderlyingNetworkRecord was valid"); - } - - if (mCached == null) { - mCached = - new UnderlyingNetworkRecord( - mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked); - } - - return mCached; - } - } - } - private static void logWtf(String msg) { Slog.wtf(TAG, msg); LOCAL_LOG.log(TAG + " WTF: " + msg); @@ -780,7 +487,7 @@ public class UnderlyingNetworkTracker { /** Dumps the state of this record for logging and debugging purposes. */ public void dump(IndentingPrintWriter pw) { - pw.println("UnderlyingNetworkTracker:"); + pw.println("UnderlyingNetworkController:"); pw.increaseIndent(); pw.println("Carrier WiFi Entry Threshold: " + getWifiEntryRssiThreshold(mCarrierConfig)); @@ -811,7 +518,7 @@ public class UnderlyingNetworkTracker { } /** Callbacks for being notified of the changes in, or to the selected underlying network. */ - public interface UnderlyingNetworkTrackerCallback { + public interface UnderlyingNetworkControllerCallback { /** * Fired when a new underlying network is selected, or properties have changed. * diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java new file mode 100644 index 000000000000..65c69dedcb28 --- /dev/null +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -0,0 +1,175 @@ +/* + * 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.vcn.routeselection; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.os.ParcelUuid; +import android.os.PersistableBundle; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; + +import java.util.Comparator; +import java.util.Objects; + +/** + * A record of a single underlying network, caching relevant fields. + * + * @hide + */ +public class UnderlyingNetworkRecord { + @NonNull public final Network network; + @NonNull public final NetworkCapabilities networkCapabilities; + @NonNull public final LinkProperties linkProperties; + public final boolean isBlocked; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public UnderlyingNetworkRecord( + @NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties, + boolean isBlocked) { + this.network = network; + this.networkCapabilities = networkCapabilities; + this.linkProperties = linkProperties; + this.isBlocked = isBlocked; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UnderlyingNetworkRecord)) return false; + final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o; + + return network.equals(that.network) + && networkCapabilities.equals(that.networkCapabilities) + && linkProperties.equals(that.linkProperties) + && isBlocked == that.isBlocked; + } + + @Override + public int hashCode() { + return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); + } + + static Comparator<UnderlyingNetworkRecord> getComparator( + ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, + UnderlyingNetworkRecord currentlySelected, + PersistableBundle carrierConfig) { + return (left, right) -> { + return Integer.compare( + NetworkPriorityClassifier.calculatePriorityClass( + left, subscriptionGroup, snapshot, currentlySelected, carrierConfig), + NetworkPriorityClassifier.calculatePriorityClass( + right, subscriptionGroup, snapshot, currentlySelected, carrierConfig)); + }; + } + + /** Dumps the state of this record for logging and debugging purposes. */ + void dump( + IndentingPrintWriter pw, + ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, + UnderlyingNetworkRecord currentlySelected, + PersistableBundle carrierConfig) { + pw.println("UnderlyingNetworkRecord:"); + pw.increaseIndent(); + + final int priorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + this, subscriptionGroup, snapshot, currentlySelected, carrierConfig); + pw.println( + "Priority class: " + + NetworkPriorityClassifier.priorityClassToString(priorityClass) + + " (" + + priorityClass + + ")"); + pw.println("mNetwork: " + network); + pw.println("mNetworkCapabilities: " + networkCapabilities); + pw.println("mLinkProperties: " + linkProperties); + + pw.decreaseIndent(); + } + + /** Builder to incrementally construct an UnderlyingNetworkRecord. */ + static class Builder { + @NonNull private final Network mNetwork; + + @Nullable private NetworkCapabilities mNetworkCapabilities; + @Nullable private LinkProperties mLinkProperties; + boolean mIsBlocked; + boolean mWasIsBlockedSet; + + @Nullable private UnderlyingNetworkRecord mCached; + + Builder(@NonNull Network network) { + mNetwork = network; + } + + @NonNull + Network getNetwork() { + return mNetwork; + } + + void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) { + mNetworkCapabilities = networkCapabilities; + mCached = null; + } + + @Nullable + NetworkCapabilities getNetworkCapabilities() { + return mNetworkCapabilities; + } + + void setLinkProperties(@NonNull LinkProperties linkProperties) { + mLinkProperties = linkProperties; + mCached = null; + } + + void setIsBlocked(boolean isBlocked) { + mIsBlocked = isBlocked; + mWasIsBlockedSet = true; + mCached = null; + } + + boolean isValid() { + return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet; + } + + UnderlyingNetworkRecord build() { + if (!isValid()) { + throw new IllegalArgumentException( + "Called build before UnderlyingNetworkRecord was valid"); + } + + if (mCached == null) { + mCached = + new UnderlyingNetworkRecord( + mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked); + } + + return mCached; + } + } +} diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java index 5c1b5ffb2209..1c675c228554 100644 --- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java +++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java @@ -46,6 +46,7 @@ public class PersistableBundleUtils { private static final String PARCEL_UUID_KEY = "PARCEL_UUID"; private static final String BYTE_ARRAY_KEY = "BYTE_ARRAY_KEY"; private static final String INTEGER_KEY = "INTEGER_KEY"; + private static final String STRING_KEY = "STRING_KEY"; /** * Functional interface to convert an object of the specified type to a PersistableBundle. @@ -91,6 +92,21 @@ public class PersistableBundleUtils { return bundle.getInt(INTEGER_KEY); }; + /** Serializer to convert s String to a PersistableBundle. */ + public static final Serializer<String> STRING_SERIALIZER = + (i) -> { + final PersistableBundle result = new PersistableBundle(); + result.putString(STRING_KEY, i); + return result; + }; + + /** Deserializer to convert a PersistableBundle to a String. */ + public static final Deserializer<String> STRING_DESERIALIZER = + (bundle) -> { + Objects.requireNonNull(bundle, "PersistableBundle is null"); + return bundle.getString(STRING_KEY); + }; + /** * Converts a ParcelUuid to a PersistableBundle. * diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index bbea094ba2e3..d1f21778177d 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -140,6 +140,7 @@ import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING; import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START; import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN; import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING; +import static com.android.server.wm.ActivityRecordProto.MIN_ASPECT_RATIO; import static com.android.server.wm.ActivityRecordProto.NAME; import static com.android.server.wm.ActivityRecordProto.NUM_DRAWN_WINDOWS; import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS; @@ -656,6 +657,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mLastImeShown; /** + * When set to true, the IME insets will be frozen until the next app becomes IME input target. + * @see InsetsPolicy#adjustVisibilityForIme + */ + boolean mImeInsetsFrozenUntilStartInput; + + /** * A flag to determine if this AR is in the process of closing or entering PIP. This is needed * to help AR know that the app is in the process of closing but hasn't yet started closing on * the WM side. @@ -1357,6 +1364,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (newTask != null && isState(RESUMED)) { newTask.setResumedActivity(this, "onParentChanged"); + mImeInsetsFrozenUntilStartInput = false; } if (rootTask != null && rootTask.topRunningActivity() == this) { @@ -4767,6 +4775,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && imeInputTarget.getWindow().mActivityRecord == this && mDisplayContent.mInputMethodWindow != null && mDisplayContent.mInputMethodWindow.isVisible(); + mImeInsetsFrozenUntilStartInput = true; } final DisplayContent displayContent = getDisplayContent(); @@ -5885,6 +5894,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // closing activity having to wait until idle timeout to be stopped or destroyed if the // next activity won't report idle (e.g. repeated view animation). mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); + + // If the activity is visible, but no windows are eligible to start input, unfreeze + // to avoid permanently frozen IME insets. + if (mImeInsetsFrozenUntilStartInput && getWindow( + win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags)) + == null) { + mImeInsetsFrozenUntilStartInput = false; + } } } @@ -7800,6 +7817,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + @Override + void onResize() { + // Reset freezing IME insets flag when the activity resized. + mImeInsetsFrozenUntilStartInput = false; + super.onResize(); + } + /** Returns true if the configuration is compatible with this activity. */ boolean isConfigurationCompatible(Configuration config) { final int orientation = getRequestedOrientation(); @@ -8649,6 +8673,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled()); proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode()); + proto.write(MIN_ASPECT_RATIO, info.getMinAspectRatio()); } @Override diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index dbc1116ad389..9335846e7805 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1165,10 +1165,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - WindowToken removeWindowToken(IBinder binder) { + WindowToken removeWindowToken(IBinder binder, boolean animateExit) { final WindowToken token = mTokenMap.remove(binder); if (token != null && token.asActivityRecord() == null) { - token.setExiting(); + token.setExiting(animateExit); } return token; } @@ -1252,7 +1252,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void removeAppToken(IBinder binder) { - final WindowToken token = removeWindowToken(binder); + final WindowToken token = removeWindowToken(binder, true /* animateExit */); if (token == null) { Slog.w(TAG_WM, "removeAppToken: Attempted to remove non-existing token: " + binder); return; @@ -3971,6 +3971,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void updateImeInputAndControlTarget(WindowState target) { if (mImeInputTarget != target) { ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target); + if (target != null && target.mActivityRecord != null) { + target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false; + } setImeInputTarget(target); updateImeControlTarget(); } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index f2f192686ad5..a8e1c1cda72b 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -211,7 +211,7 @@ class InsetsPolicy { InsetsState getInsetsForWindow(WindowState target) { final InsetsState originalState = mStateController.getInsetsForWindow(target); final InsetsState state = adjustVisibilityForTransientTypes(originalState); - return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state; + return adjustVisibilityForIme(target, state, state == originalState); } /** @@ -241,16 +241,37 @@ class InsetsPolicy { return state; } - // Navigation bar insets is always visible to IME. - private static InsetsState adjustVisibilityForIme(InsetsState originalState, + private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState, boolean copyState) { - final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); - if (originalNavSource != null && !originalNavSource.isVisible()) { - final InsetsState state = copyState ? new InsetsState(originalState) : originalState; - final InsetsSource navSource = new InsetsSource(originalNavSource); - navSource.setVisible(true); - state.addSource(navSource); - return state; + if (w.mIsImWindow) { + // Navigation bar insets is always visible to IME. + final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR); + if (originalNavSource != null && !originalNavSource.isVisible()) { + final InsetsState state = copyState ? new InsetsState(originalState) + : originalState; + final InsetsSource navSource = new InsetsSource(originalNavSource); + navSource.setVisible(true); + state.addSource(navSource); + return state; + } + } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) { + // During switching tasks with gestural navigation, if the IME is attached to + // one app window on that time, even the next app window is behind the IME window, + // conceptually the window should not receive the IME insets if the next window is + // not eligible IME requester and ready to show IME on top of it. + final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp(); + final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME); + + if (shouldImeAttachedToApp && originalImeSource != null) { + final boolean imeVisibility = + w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME); + final InsetsState state = copyState ? new InsetsState(originalState) + : originalState; + final InsetsSource imeSource = new InsetsSource(originalImeSource); + imeSource.setVisible(imeVisibility); + state.addSource(imeSource); + return state; + } } return originalState; } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index fe1020c86041..a7216da9bed5 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -190,8 +190,7 @@ class KeyguardController { if (keyguardChanged) { // Irrelevant to AOD. - dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */, - false /* turningScreenOn */); + dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */); mKeyguardGoingAway = false; if (keyguardShowing) { mDismissalRequested = false; @@ -385,6 +384,8 @@ class KeyguardController { mService.continueWindowLayout(); } } + dismissMultiWindowModeForTaskIfNeeded(topActivity != null + ? topActivity.getRootTask() : null); } /** @@ -410,21 +411,6 @@ class KeyguardController { } } - /** - * Called when somebody wants to turn screen on. - */ - private void handleTurnScreenOn(int displayId) { - if (displayId != DEFAULT_DISPLAY) { - return; - } - - mTaskSupervisor.wakeUp("handleTurnScreenOn"); - if (mKeyguardShowing && canDismissKeyguard()) { - mWindowManager.dismissKeyguard(null /* callback */, null /* message */); - mDismissalRequested = true; - } - } - boolean isDisplayOccluded(int displayId) { return getDisplayState(displayId).mOccluded; } @@ -438,11 +424,9 @@ class KeyguardController { } private void dismissMultiWindowModeForTaskIfNeeded( - @Nullable Task currentTaskControllingOcclusion, boolean turningScreenOn) { - // If turningScreenOn is true, it means that the visibility state has changed from - // currentTaskControllingOcclusion and we should update windowing mode. + @Nullable Task currentTaskControllingOcclusion) { // TODO(b/113840485): Handle docked stack for individual display. - if (!turningScreenOn && (!mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY))) { + if (!mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) { return; } @@ -581,26 +565,17 @@ class KeyguardController { && controller.mWindowManager.isKeyguardSecure( controller.mService.getCurrentUserId()); - boolean occludingChange = false; - boolean turningScreenOn = false; if (mTopTurnScreenOnActivity != lastTurnScreenOnActivity && mTopTurnScreenOnActivity != null && !mService.mWindowManager.mPowerManager.isInteractive() - && (mRequestDismissKeyguard || occludedByActivity - || controller.canDismissKeyguard())) { - turningScreenOn = true; - controller.handleTurnScreenOn(mDisplayId); + && (mRequestDismissKeyguard || occludedByActivity)) { + controller.mTaskSupervisor.wakeUp("handleTurnScreenOn"); mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false); } if (lastOccluded != mOccluded) { - occludingChange = true; controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity); } - - if (occludingChange || turningScreenOn) { - controller.dismissMultiWindowModeForTaskIfNeeded(task, turningScreenOn); - } } /** diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index e74371036619..a518222c3bde 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -118,6 +118,11 @@ class TaskSnapshotController { */ private final boolean mIsRunningOnWear; + /** + * Flag indicating if device configuration has disabled app snapshots. + */ + private final boolean mConfigDisableTaskSnapshots; + TaskSnapshotController(WindowManagerService service) { mService = service; mPersister = new TaskSnapshotPersister(mService, Environment::getDataSystemCeDirectory); @@ -131,6 +136,8 @@ class TaskSnapshotController { PackageManager.FEATURE_WATCH); mHighResTaskSnapshotScale = mService.mContext.getResources().getFloat( com.android.internal.R.dimen.config_highResTaskSnapshotScale); + mConfigDisableTaskSnapshots = mService.mContext.getResources().getBoolean( + com.android.internal.R.bool.config_disableTaskSnapshots); } void systemReady() { @@ -488,7 +495,8 @@ class TaskSnapshotController { } boolean shouldDisableSnapshots() { - return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT; + return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT + || mConfigDisableTaskSnapshots; } /** diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 194f48f57cc4..b54e8b7a7b4e 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -66,8 +66,8 @@ class WallpaperWindowToken extends WindowToken { } @Override - void setExiting() { - super.setExiting(); + void setExiting(boolean animateExit) { + super.setExiting(animateExit); mDisplayContent.mWallpaperController.removeWallpaperToken(this); } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 47087cfbd147..4fac05c349c5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -445,8 +445,21 @@ public abstract class WindowManagerInternal { * @param removeWindows Whether to also remove the windows associated with the token. * @param displayId The display to remove the token from. */ + public final void removeWindowToken(android.os.IBinder token, boolean removeWindows, + int displayId) { + removeWindowToken(token, removeWindows, true /* animateExit */, displayId); + } + + /** + * Removes a window token. + * + * @param token The toke to remove. + * @param removeWindows Whether to also remove the windows associated with the token. + * @param animateExit Whether to play the windows exit animation after the token removal. + * @param displayId The display to remove the token from. + */ public abstract void removeWindowToken(android.os.IBinder token, boolean removeWindows, - int displayId); + boolean animateExit, int displayId); /** * Registers a listener to be notified about app transition events. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1ec9187d7a76..3421b28454f0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -167,8 +167,10 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; -import android.hardware.configstore.V1_0.ISurfaceFlingerConfigs; import android.hardware.configstore.V1_0.OptionalBool; +import android.hardware.configstore.V1_1.DisplayOrientation; +import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs; +import android.hardware.configstore.V1_1.OptionalDisplayOrientation; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.input.InputManager; @@ -220,6 +222,7 @@ import android.util.TypedValue; import android.util.proto.ProtoOutputStream; import android.view.Choreographer; import android.view.Display; +import android.view.DisplayAddress; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; @@ -465,6 +468,8 @@ public class WindowManagerService extends IWindowManager.Stub */ static final boolean ENABLE_FIXED_ROTATION_TRANSFORM = SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true); + private @Surface.Rotation int mPrimaryDisplayOrientation = Surface.ROTATION_0; + private DisplayAddress mPrimaryDisplayPhysicalAddress; // Enums for animation scale update types. @Retention(RetentionPolicy.SOURCE) @@ -2461,16 +2466,21 @@ public class WindowManagerService extends IWindowManager.Stub configChanged = displayContent.updateOrientation(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - final DisplayInfo rotatedDisplayInfo = - win.mToken.getFixedRotationTransformDisplayInfo(); - if (rotatedDisplayInfo != null) { - outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation); - } else { - // We have to update the transform hint of display here, but we need to get if from - // SurfaceFlinger, so set it as rotation of display for most cases, then - // SurfaceFlinger would still update the transform hint of display in next frame. - outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation); - } + final DisplayInfo displayInfo = win.getDisplayInfo(); + int transformHint = displayInfo.rotation; + // If the window is on the primary display, use the panel orientation to adjust the + // transform hint + final boolean isPrimaryDisplay = displayInfo.address != null && + displayInfo.address.equals(mPrimaryDisplayPhysicalAddress); + if (isPrimaryDisplay) { + transformHint = (transformHint + mPrimaryDisplayOrientation) % 4; + } + outSurfaceControl.setTransformHint(transformHint); + ProtoLog.v(WM_DEBUG_ORIENTATION, + "Passing transform hint %d for window %s%s", + transformHint, win, + isPrimaryDisplay ? " on primary display with orientation " + + mPrimaryDisplayOrientation : ""); if (toBeDisplayed && win.mIsWallpaper) { displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */); @@ -2816,6 +2826,31 @@ public class WindowManagerService extends IWindowManager.Stub } + void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit, + int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + + if (dc == null) { + ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" + + " for non-exiting displayId=%d", binder, displayId); + return; + } + final WindowToken token = dc.removeWindowToken(binder, animateExit); + if (token == null) { + ProtoLog.w(WM_ERROR, + "removeWindowToken: Attempted to remove non-existing token: %s", + binder); + return; + } + + if (removeWindows) { + token.removeAllWindowsIfPossible(); + } + dc.getInputMonitor().updateInputWindowsLw(true /* force */); + } + } + @Override public void removeWindowToken(IBinder binder, int displayId) { if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) { @@ -2823,23 +2858,7 @@ public class WindowManagerService extends IWindowManager.Stub } final long origId = Binder.clearCallingIdentity(); try { - synchronized (mGlobalLock) { - final DisplayContent dc = mRoot.getDisplayContent(displayId); - - if (dc == null) { - ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" - + " for non-exiting displayId=%d", binder, displayId); - return; - } - final WindowToken token = dc.removeWindowToken(binder); - if (token == null) { - ProtoLog.w(WM_ERROR, - "removeWindowToken: Attempted to remove non-existing token: %s", - binder); - return; - } - dc.getInputMonitor().updateInputWindowsLw(true /*force*/); - } + removeWindowToken(binder, false /* removeWindows */, true /* animateExit */, displayId); } finally { Binder.restoreCallingIdentity(origId); } @@ -4859,6 +4878,9 @@ public class WindowManagerService extends IWindowManager.Stub mTaskSnapshotController.systemReady(); mHasWideColorGamutSupport = queryWideColorGamutSupport(); mHasHdrSupport = queryHdrSupport(); + mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation(); + mPrimaryDisplayPhysicalAddress = + DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId()); UiThread.getHandler().post(mSettingsObserver::loadSettings); IVrManager vrManager = IVrManager.Stub.asInterface( ServiceManager.getService(Context.VR_SERVICE)); @@ -4878,6 +4900,9 @@ public class WindowManagerService extends IWindowManager.Stub } } + + // Keep logic in sync with SurfaceFlingerProperties.cpp + // Consider exposing properties via ISurfaceComposer instead. private static boolean queryWideColorGamutSupport() { boolean defaultValue = false; Optional<Boolean> hasWideColorProp = SurfaceFlingerProperties.has_wide_color_display(); @@ -4918,6 +4943,39 @@ public class WindowManagerService extends IWindowManager.Stub return false; } + private static @Surface.Rotation int queryPrimaryDisplayOrientation() { + Optional<SurfaceFlingerProperties.primary_display_orientation_values> prop = + SurfaceFlingerProperties.primary_display_orientation(); + if (prop.isPresent()) { + switch (prop.get()) { + case ORIENTATION_90: return Surface.ROTATION_90; + case ORIENTATION_180: return Surface.ROTATION_180; + case ORIENTATION_270: return Surface.ROTATION_270; + case ORIENTATION_0: + default: + return Surface.ROTATION_0; + } + } + try { + ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); + OptionalDisplayOrientation primaryDisplayOrientation = + surfaceFlinger.primaryDisplayOrientation(); + if (primaryDisplayOrientation != null && primaryDisplayOrientation.specified) { + switch (primaryDisplayOrientation.value) { + case DisplayOrientation.ORIENTATION_90: return Surface.ROTATION_90; + case DisplayOrientation.ORIENTATION_180: return Surface.ROTATION_180; + case DisplayOrientation.ORIENTATION_270: return Surface.ROTATION_270; + case DisplayOrientation.ORIENTATION_0: + default: + return Surface.ROTATION_0; + } + } + } catch (Exception e) { + // Use default value if we can't talk to config store. + } + return Surface.ROTATION_0; + } + void reportFocusChanged(IBinder oldToken, IBinder newToken) { WindowState lastFocus; WindowState newFocus; @@ -7536,28 +7594,10 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void removeWindowToken(IBinder binder, boolean removeWindows, int displayId) { - synchronized (mGlobalLock) { - if (removeWindows) { - final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null) { - ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s" - + " for non-exiting displayId=%d", binder, displayId); - return; - } - - final WindowToken token = dc.removeWindowToken(binder); - if (token == null) { - ProtoLog.w(WM_ERROR, - "removeWindowToken: Attempted to remove non-existing token: %s", - binder); - return; - } - - token.removeAllWindowsIfPossible(); - } - WindowManagerService.this.removeWindowToken(binder, displayId); - } + public void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit, + int displayId) { + WindowManagerService.this.removeWindowToken(binder, removeWindows, animateExit, + displayId); } @Override diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c3fc99554bcc..5e042efa2f11 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2180,11 +2180,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - boolean onSetAppExiting() { + boolean onSetAppExiting(boolean animateExit) { final DisplayContent displayContent = getDisplayContent(); boolean changed = false; - if (isVisibleNow()) { + if (!animateExit) { + // Hide the window permanently if no window exist animation is performed, so we can + // avoid the window surface becoming visible again unexpectedly during the next + // relayout. + mPermanentlyHidden = true; + hide(false /* doAnimation */, false /* requestAnim */); + } + if (isVisibleNow() && animateExit) { mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); if (mWmService.mAccessibilityController != null) { mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT); @@ -2197,7 +2204,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowState c = mChildren.get(i); - changed |= c.onSetAppExiting(); + changed |= c.onSetAppExiting(animateExit); } return changed; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index fbfa400ba852..3cbc67c004cd 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -24,6 +24,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; @@ -232,7 +233,7 @@ class WindowToken extends WindowContainer<WindowState> { } } - void setExiting() { + void setExiting(boolean animateExit) { if (isEmpty()) { super.removeImmediately(); return; @@ -247,11 +248,12 @@ class WindowToken extends WindowContainer<WindowState> { final int count = mChildren.size(); boolean changed = false; - final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN); + final boolean delayed = isAnimating(TRANSITION | PARENTS) + || (isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION) && animateExit); for (int i = 0; i < count; i++) { final WindowState win = mChildren.get(i); - changed |= win.onSetAppExiting(); + changed |= win.onSetAppExiting(animateExit); } final ActivityRecord app = asActivityRecord(); @@ -353,7 +355,7 @@ class WindowToken extends WindowContainer<WindowState> { @Override void removeImmediately() { if (mDisplayContent != null) { - mDisplayContent.removeWindowToken(token); + mDisplayContent.removeWindowToken(token, true /* animateExit */); } // Needs to occur after the token is removed from the display above to avoid attempt at // duplicate removal of this window container from it's parent. diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 710a304545af..e7005daf5626 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -85,6 +85,7 @@ cc_library_static { header_libs: [ "bionic_libc_platform_headers", + "bpf_connectivity_headers", ], } diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp index 10b248a70e7e..5178132e4a2e 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp +++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp @@ -38,9 +38,6 @@ using android::bpf::bpfGetIfaceStats; namespace android { -static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt"; -static const char* QTAGUID_UID_STATS = "/proc/net/xt_qtaguid/stats"; - // NOTE: keep these in sync with TrafficStats.java static const uint64_t UNKNOWN = -1; @@ -72,102 +69,17 @@ static uint64_t getStatsType(Stats* stats, StatsType type) { } } -static int parseIfaceStats(const char* iface, Stats* stats) { - FILE *fp = fopen(QTAGUID_IFACE_STATS, "r"); - if (fp == NULL) { - return -1; - } - - char buffer[384]; - char cur_iface[32]; - bool foundTcp = false; - uint64_t rxBytes, rxPackets, txBytes, txPackets, tcpRxPackets, tcpTxPackets; - - while (fgets(buffer, sizeof(buffer), fp) != NULL) { - int matched = sscanf(buffer, "%31s %" SCNu64 " %" SCNu64 " %" SCNu64 - " %" SCNu64 " " "%*u %" SCNu64 " %*u %*u %*u %*u " - "%*u %" SCNu64 " %*u %*u %*u %*u", cur_iface, &rxBytes, - &rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets); - if (matched >= 5) { - if (matched == 7) { - foundTcp = true; - } - if (!iface || !strcmp(iface, cur_iface)) { - stats->rxBytes += rxBytes; - stats->rxPackets += rxPackets; - stats->txBytes += txBytes; - stats->txPackets += txPackets; - if (matched == 7) { - stats->tcpRxPackets += tcpRxPackets; - stats->tcpTxPackets += tcpTxPackets; - } - } - } - } - - if (!foundTcp) { - stats->tcpRxPackets = UNKNOWN; - stats->tcpTxPackets = UNKNOWN; - } - - if (fclose(fp) != 0) { - return -1; - } - return 0; -} - -static int parseUidStats(const uint32_t uid, Stats* stats) { - FILE *fp = fopen(QTAGUID_UID_STATS, "r"); - if (fp == NULL) { - return -1; - } - - char buffer[384]; - char iface[32]; - uint32_t idx, cur_uid, set; - uint64_t tag, rxBytes, rxPackets, txBytes, txPackets; - - while (fgets(buffer, sizeof(buffer), fp) != NULL) { - if (sscanf(buffer, - "%" SCNu32 " %31s 0x%" SCNx64 " %u %u %" SCNu64 " %" SCNu64 - " %" SCNu64 " %" SCNu64 "", - &idx, iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets, - &txBytes, &txPackets) == 9) { - if (uid == cur_uid && tag == 0L) { - stats->rxBytes += rxBytes; - stats->rxPackets += rxPackets; - stats->txBytes += txBytes; - stats->txPackets += txPackets; - } - } - } - - if (fclose(fp) != 0) { - return -1; - } - return 0; -} - -static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) { +static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) { Stats stats = {}; - if (useBpfStats) { - if (bpfGetIfaceStats(NULL, &stats) == 0) { - return getStatsType(&stats, (StatsType) type); - } else { - return UNKNOWN; - } - } - - if (parseIfaceStats(NULL, &stats) == 0) { + if (bpfGetIfaceStats(NULL, &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } -static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type, - jboolean useBpfStats) { +static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) { ScopedUtfChars iface8(env, iface); if (iface8.c_str() == NULL) { return UNKNOWN; @@ -175,33 +87,17 @@ static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type, Stats stats = {}; - if (useBpfStats) { - if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) { - return getStatsType(&stats, (StatsType) type); - } else { - return UNKNOWN; - } - } - - if (parseIfaceStats(iface8.c_str(), &stats) == 0) { + if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } -static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) { +static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) { Stats stats = {}; - if (useBpfStats) { - if (bpfGetUidStats(uid, &stats) == 0) { - return getStatsType(&stats, (StatsType) type); - } else { - return UNKNOWN; - } - } - - if (parseUidStats(uid, &stats) == 0) { + if (bpfGetUidStats(uid, &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; @@ -209,9 +105,9 @@ static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean } static const JNINativeMethod gMethods[] = { - {"nativeGetTotalStat", "(IZ)J", (void*) getTotalStat}, - {"nativeGetIfaceStat", "(Ljava/lang/String;IZ)J", (void*) getIfaceStat}, - {"nativeGetUidStat", "(IIZ)J", (void*) getUidStat}, + {"nativeGetTotalStat", "(I)J", (void*)getTotalStat}, + {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat}, + {"nativeGetUidStat", "(II)J", (void*)getUidStat}, }; int register_android_server_net_NetworkStatsService(JNIEnv* env) { diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java index 3a9b2dca3921..cb52e5f72d5f 100644 --- a/services/java/com/android/server/SystemConfigService.java +++ b/services/java/com/android/server/SystemConfigService.java @@ -19,6 +19,7 @@ import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; import android.Manifest; +import android.content.ComponentName; import android.content.Context; import android.os.ISystemConfig; import android.util.ArrayMap; @@ -87,14 +88,14 @@ public class SystemConfigService extends SystemService { } @Override - public List<String> getEnabledComponentOverrides(String packageName) { + public List<ComponentName> getEnabledComponentOverrides(String packageName) { ArrayMap<String, Boolean> systemComponents = SystemConfig.getInstance() .getComponentsEnabledStates(packageName); - List<String> enabledComponent = new ArrayList<>(); + List<ComponentName> enabledComponent = new ArrayList<>(); if (systemComponents != null) { for (Map.Entry<String, Boolean> entry : systemComponents.entrySet()) { if (Boolean.TRUE.equals(entry.getValue())) { - enabledComponent.add(entry.getKey()); + enabledComponent.add(new ComponentName(packageName, entry.getKey())); } } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index aca7cc9070b1..1f96c6674a12 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -264,6 +264,8 @@ public final class SystemServer implements Dumpable { "com.android.server.stats.StatsCompanion$Lifecycle"; private static final String STATS_PULL_ATOM_SERVICE_CLASS = "com.android.server.stats.pull.StatsPullAtomService"; + private static final String STATS_BOOTSTRAP_ATOM_SERVICE_LIFECYCLE_CLASS = + "com.android.server.stats.bootstrap.StatsBootstrapAtomService$Lifecycle"; private static final String USB_SERVICE_CLASS = "com.android.server.usb.UsbService$Lifecycle"; private static final String MIDI_SERVICE_CLASS = @@ -364,6 +366,8 @@ public final class SystemServer implements Dumpable { "com.android.server.blob.BlobStoreManagerService"; private static final String APP_SEARCH_MANAGER_SERVICE_CLASS = "com.android.server.appsearch.AppSearchManagerService"; + private static final String ISOLATED_COMPILATION_SERVICE_CLASS = + "com.android.server.compos.IsolatedCompilationService"; private static final String ROLLBACK_MANAGER_SERVICE_CLASS = "com.android.server.rollback.RollbackManagerService"; private static final String ALARM_MANAGER_SERVICE_CLASS = @@ -2487,6 +2491,11 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS); t.traceEnd(); + // Log atoms to statsd from bootstrap processes. + t.traceBegin("StatsBootstrapAtomService"); + mSystemServiceManager.startService(STATS_BOOTSTRAP_ATOM_SERVICE_LIFECYCLE_CLASS); + t.traceEnd(); + // Incidentd and dumpstated helper t.traceBegin("StartIncidentCompanionService"); mSystemServiceManager.startService(IncidentCompanionService.class); @@ -2651,6 +2660,12 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS); t.traceEnd(); + if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) { + t.traceBegin("IsolatedCompilationService"); + mSystemServiceManager.startService(ISOLATED_COMPILATION_SERVICE_CLASS); + t.traceEnd(); + } + t.traceBegin("StartMediaCommunicationService"); mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS); t.traceEnd(); diff --git a/services/net/OWNERS b/services/net/OWNERS index d3836d4c6c57..62c5737a2e8e 100644 --- a/services/net/OWNERS +++ b/services/net/OWNERS @@ -1,8 +1,2 @@ set noparent - -codewiz@google.com -jchalard@google.com -junyulai@google.com -lorenzo@google.com -reminv@google.com -satk@google.com +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java index 45e0aac24ca7..ff901af3defa 100644 --- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java +++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java @@ -93,6 +93,9 @@ class CallLogQueryHelper { hasResults = true; } } + } catch (SecurityException ex) { + Slog.e(TAG, "Query call log failed: " + ex); + return false; } return hasResults; } diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index fdf23d3836ac..62a16f7f24fd 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -23,7 +23,6 @@ import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.ResolveInfo; import android.os.Handler; import android.os.IBinder.DeathRecipient; import android.os.Looper; @@ -32,12 +31,11 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UpdateEngine; import android.os.UpdateEngineCallback; -import android.os.UserHandle; -import android.os.UserManager; import android.provider.DeviceConfig; import android.util.Log; import com.android.internal.R; +import com.android.internal.os.BackgroundThread; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -45,9 +43,6 @@ import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; import com.android.server.wm.ActivityTaskManagerInternal; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -96,10 +91,12 @@ public final class ProfcollectForwardingService extends SystemService { if (mIProfcollect == null) { return; } - if (serviceHasSupportedTraceProvider()) { - registerObservers(); - } - ProfcollectBGJobService.schedule(getContext()); + BackgroundThread.get().getThreadHandler().post(() -> { + if (serviceHasSupportedTraceProvider()) { + registerObservers(); + ProfcollectBGJobService.schedule(getContext()); + } + }); } } @@ -152,7 +149,7 @@ public final class ProfcollectForwardingService extends SystemService { connectNativeService(); break; default: - throw new AssertionError("Unknown message: " + message.toString()); + throw new AssertionError("Unknown message: " + message); } } } @@ -196,11 +193,14 @@ public final class ProfcollectForwardingService extends SystemService { Log.d(LOG_TAG, "Starting background process job"); } - try { - sSelfService.mIProfcollect.process(false); - } catch (RemoteException e) { - Log.e(LOG_TAG, e.getMessage()); - } + BackgroundThread.get().getThreadHandler().post( + () -> { + try { + sSelfService.mIProfcollect.process(); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage()); + } + }); return true; } @@ -236,14 +236,16 @@ public final class ProfcollectForwardingService extends SystemService { "applaunch_trace_freq", 2); int randomNum = ThreadLocalRandom.current().nextInt(100); if (randomNum < traceFrequency) { - try { - if (DEBUG) { - Log.d(LOG_TAG, "Tracing on app launch event: " + packageName); - } - mIProfcollect.trace_once("applaunch"); - } catch (RemoteException e) { - Log.e(LOG_TAG, e.getMessage()); + if (DEBUG) { + Log.d(LOG_TAG, "Tracing on app launch event: " + packageName); } + BackgroundThread.get().getThreadHandler().post(() -> { + try { + mIProfcollect.trace_once("applaunch"); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage()); + } + }); } } @@ -306,79 +308,27 @@ public final class ProfcollectForwardingService extends SystemService { return; } - if (!getUploaderEnabledConfig(getContext())) { - return; - } - - new Thread(() -> { + Context context = getContext(); + BackgroundThread.get().getThreadHandler().post(() -> { try { - Context context = getContext(); - final String uploaderPkg = getUploaderPackageName(context); - final String uploaderAction = getUploaderActionName(context); - String reportUuid = mIProfcollect.report(); - - final int profileId = getBBProfileId(); - String reportDir = "/data/user/" + profileId - + "/com.google.android.apps.internal.betterbug/cache/"; - String reportPath = reportDir + reportUuid + ".zip"; - - if (!Files.exists(Paths.get(reportDir))) { - Log.i(LOG_TAG, "Destination directory does not exist, abort upload."); - return; - } + // Prepare profile report + String reportName = mIProfcollect.report() + ".zip"; - Intent uploadIntent = - new Intent(uploaderAction) - .setPackage(uploaderPkg) - .putExtra("EXTRA_DESTINATION", "PROFCOLLECT") - .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName()) - .putExtra("EXTRA_PROFILE_PATH", reportPath) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - - List<ResolveInfo> receivers = - context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0); - if (receivers == null || receivers.isEmpty()) { - Log.i(LOG_TAG, "No one to receive upload intent, abort upload."); + if (!context.getResources().getBoolean( + R.bool.config_profcollectReportUploaderEnabled)) { + Log.i(LOG_TAG, "Upload is not enabled."); return; } - mIProfcollect.copy_report_to_bb(profileId, reportUuid); - context.sendBroadcast(uploadIntent); - mIProfcollect.delete_report(reportUuid); + + // Upload the report + Intent intent = new Intent() + .setPackage("com.android.shell") + .setAction("com.android.shell.action.PROFCOLLECT_UPLOAD") + .putExtra("filename", reportName); + context.sendBroadcast(intent); } 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; - } - - private boolean getUploaderEnabledConfig(Context context) { - return context.getResources().getBoolean( - R.bool.config_profcollectReportUploaderEnabled); - } - - private String getUploaderPackageName(Context context) { - return context.getResources().getString( - R.string.config_defaultProfcollectReportUploaderApp); - } - - private String getUploaderActionName(Context context) { - return context.getResources().getString( - R.string.config_defaultProfcollectReportUploaderAction); + }); } } diff --git a/services/proguard.flags b/services/proguard.flags new file mode 100644 index 000000000000..30dd6cf545b9 --- /dev/null +++ b/services/proguard.flags @@ -0,0 +1,11 @@ +# TODO(b/196084106): Refine and optimize this configuration. Note that this +# configuration is only used when `SOONG_CONFIG_ANDROID_SYSTEM_OPTIMIZE_JAVA=true`. +-keep,allowoptimization,allowaccessmodification class ** { + *; +} + +# Various classes subclassed in ethernet-service (avoid marking final). +-keep public class android.net.** { *; } + +# Referenced via CarServiceHelperService in car-frameworks-service (avoid removing). +-keep public class com.android.server.utils.Slogf { *; }
\ No newline at end of file diff --git a/services/tests/mockingservicestests/OWNERS b/services/tests/mockingservicestests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/services/tests/mockingservicestests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/services/tests/servicestests/OWNERS b/services/tests/servicestests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/services/tests/servicestests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java deleted file mode 100644 index a2ecbc30ec64..000000000000 --- a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import static junit.framework.Assert.*; - -import static org.mockito.Mockito.*; - -import android.hardware.health.V2_0.IHealth; -import android.hidl.manager.V1_0.IServiceManager; -import android.hidl.manager.V1_0.IServiceNotification; -import android.test.AndroidTestCase; - -import androidx.test.filters.SmallTest; - -import org.mockito.ArgumentMatcher; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; - -import java.util.Arrays; -import java.util.Collection; -import java.util.NoSuchElementException; - -public class BatteryServiceTest extends AndroidTestCase { - - @Mock IServiceManager mMockedManager; - @Mock IHealth mMockedHal; - @Mock IHealth mMockedHal2; - - @Mock BatteryService.HealthServiceWrapper.Callback mCallback; - @Mock BatteryService.HealthServiceWrapper.IServiceManagerSupplier mManagerSupplier; - @Mock BatteryService.HealthServiceWrapper.IHealthSupplier mHealthServiceSupplier; - BatteryService.HealthServiceWrapper mWrapper; - - private static final String VENDOR = BatteryService.HealthServiceWrapper.INSTANCE_VENDOR; - - @Override - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Override - public void tearDown() { - if (mWrapper != null) - mWrapper.getHandlerThread().quitSafely(); - } - - public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) { - return new ArgumentMatcher<T>() { - @Override public boolean matches(T e) { - return collection.contains(e); - } - @Override public String toString() { - return collection.toString(); - } - }; - } - - private void initForInstances(String... instanceNamesArr) throws Exception { - final Collection<String> instanceNames = Arrays.asList(instanceNamesArr); - doAnswer((invocation) -> { - // technically, preexisting is ignored by - // BatteryService.HealthServiceWrapper.Notification, but still call it correctly. - sendNotification(invocation, true); - sendNotification(invocation, true); - sendNotification(invocation, false); - return null; - }).when(mMockedManager).registerForNotifications( - eq(IHealth.kInterfaceName), - argThat(isOneOf(instanceNames)), - any(IServiceNotification.class)); - - doReturn(mMockedManager).when(mManagerSupplier).get(); - doReturn(mMockedHal) // init calls this - .doReturn(mMockedHal) // notification 1 - .doReturn(mMockedHal) // notification 2 - .doReturn(mMockedHal2) // notification 3 - .doThrow(new RuntimeException("Should not call getService for more than 4 times")) - .when(mHealthServiceSupplier).get(argThat(isOneOf(instanceNames))); - - mWrapper = new BatteryService.HealthServiceWrapper(); - } - - private void waitHandlerThreadFinish() throws Exception { - for (int i = 0; i < 5; i++) { - if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) { - return; - } - Thread.sleep(300); - } - assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()); - } - - private static void sendNotification(InvocationOnMock invocation, boolean preexisting) - throws Exception { - ((IServiceNotification)invocation.getArguments()[2]).onRegistration( - IHealth.kInterfaceName, - (String)invocation.getArguments()[1], - preexisting); - } - - @SmallTest - public void testWrapPreferVendor() throws Exception { - initForInstances(VENDOR); - mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier); - waitHandlerThreadFinish(); - verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR)); - verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString()); - verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR)); - } - - @SmallTest - public void testNoService() throws Exception { - initForInstances("unrelated"); - try { - mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier); - fail("Expect NoSuchElementException"); - } catch (NoSuchElementException ex) { - // expected - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java index 3ace3f4c79dc..a1d4c203de18 100644 --- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java +++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java @@ -66,7 +66,7 @@ public class BluetoothAirplaneModeListenerTest { when(mHelper.isBluetoothOn()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isAirplaneModeOn()).thenReturn(true); @@ -83,7 +83,7 @@ public class BluetoothAirplaneModeListenerTest { public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() { mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; when(mHelper.isBluetoothOn()).thenReturn(true); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); @@ -97,7 +97,7 @@ public class BluetoothAirplaneModeListenerTest { public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() { mBluetoothAirplaneModeListener.mToastCount = 0; when(mHelper.isBluetoothOn()).thenReturn(true); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 5c53d43fa1df..9e1445cf589d 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -26,11 +26,11 @@ import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -98,16 +98,12 @@ public class AudioDeviceBrokerTest { Log.i(TAG, "starting testPostA2dpDeviceConnectionChange"); Assert.assertNotNull("invalid null BT device", mFakeBtDevice); - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); - verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState( - any(BluetoothDevice.class), - ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/, - ArgumentMatchers.eq(BluetoothProfile.A2DP) /*profile*/, - ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/, - ArgumentMatchers.eq(1) /*a2dpVolume*/ + verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice( + any(AudioDeviceBroker.BtDeviceInfo.class) ); // verify the connection was reported to AudioSystem @@ -210,30 +206,29 @@ public class AudioDeviceBrokerTest { ((NoOpAudioSystemAdapter) mSpyAudioSystem).configureIsStreamActive(mockMediaPlayback); // first connection: ensure the device is connected as a starting condition for the test - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // disconnection - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice, + BtProfileConnectionInfo.a2dpInfo(false, -1), "testSource")); if (delayAfterDisconnection > 0) { Thread.sleep(delayAfterDisconnection); } // reconnection - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 2), "testSource")); Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS); // Verify disconnection has been cancelled and we're seeing two connections attempts, // with the device connected at the end of the test - verify(mSpyDevInventory, times(2)).onSetA2dpSinkConnectionState( - any(BtHelper.BluetoothA2dpDeviceInfo.class), - ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED)); + verify(mSpyDevInventory, times(2)).onSetBtActiveDevice( + any(AudioDeviceBroker.BtDeviceInfo.class), anyInt()); Assert.assertTrue("Mock device not connected", mSpyDevInventory.isA2dpDeviceConnected(mFakeBtDevice)); diff --git a/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java new file mode 100644 index 000000000000..16d97a454050 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.health; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.fail; + +import static org.mockito.AdditionalMatchers.not; +import static org.mockito.Mockito.*; + +import android.hidl.manager.V1_0.IServiceManager; +import android.hidl.manager.V1_0.IServiceNotification; +import android.os.IServiceCallback; +import android.os.RemoteException; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; + +import java.util.Arrays; +import java.util.Collection; +import java.util.NoSuchElementException; + +@RunWith(AndroidJUnit4.class) +public class HealthServiceWrapperTest { + @Mock IServiceManager mMockedManager; + @Mock android.hardware.health.V2_0.IHealth mMockedHal; + @Mock android.hardware.health.V2_0.IHealth mMockedHal2; + + @Mock HealthServiceWrapperHidl.Callback mCallback; + @Mock HealthServiceWrapperHidl.IServiceManagerSupplier mManagerSupplier; + @Mock HealthServiceWrapperHidl.IHealthSupplier mHealthServiceSupplier; + + @Mock android.hardware.health.IHealth.Stub mMockedAidlHal; + @Mock android.hardware.health.IHealth.Stub mMockedAidlHal2; + @Mock HealthServiceWrapperAidl.ServiceManagerStub mMockedAidlManager; + @Mock HealthRegCallbackAidl mRegCallbackAidl; + + HealthServiceWrapper mWrapper; + + private static final String VENDOR = HealthServiceWrapperHidl.INSTANCE_VENDOR; + private static final String AIDL_SERVICE_NAME = HealthServiceWrapperAidl.SERVICE_NAME; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + // Mocks the conversion between IHealth and IBinder. + when(mMockedAidlHal.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal + when(mMockedAidlHal2.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal2 + when(mMockedAidlHal.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR)) + .thenReturn(mMockedAidlHal); + when(mMockedAidlHal2.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR)) + .thenReturn(mMockedAidlHal2); + } + + @After + public void tearDown() { + validateMockitoUsage(); + if (mWrapper != null) mWrapper.getHandlerThread().quitSafely(); + } + + public static <T> ArgumentMatcher<T> isOneOf(T[] collection) { + return isOneOf(Arrays.asList(collection)); + } + + public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) { + return new ArgumentMatcher<T>() { + @Override + public boolean matches(T e) { + return collection.contains(e); + } + + @Override + public String toString() { + return "is one of " + collection.toString(); + } + }; + } + + /** + * Set up mock objects to pretend that the given AIDL and HIDL instances exists. + * + * <p>Also, when registering service notifications, the mocked service managers immediately + * sends 3 registration notifications, including 2 referring to the original HAL and 1 referring + * to the new HAL. + * + * @param aidlInstances e.g. {"android.hardware.health.IHealth/default"} + * @param hidlInstances e.g. {"default", "backup"} + * @throws Exception + */ + private void initForInstances(String[] aidlInstances, String[] hidlInstances) throws Exception { + doAnswer( + (invocation) -> { + sendAidlRegCallback(invocation, mMockedAidlHal); + sendAidlRegCallback(invocation, mMockedAidlHal); + sendAidlRegCallback(invocation, mMockedAidlHal2); + return null; + }) + .when(mMockedAidlManager) + .registerForNotifications( + argThat(isOneOf(aidlInstances)), any(IServiceCallback.class)); + when(mMockedAidlManager.waitForDeclaredService(argThat(isOneOf(aidlInstances)))) + .thenReturn(mMockedAidlHal) + .thenThrow(new RuntimeException("waitForDeclaredService called more than once")); + when(mMockedAidlManager.waitForDeclaredService(not(argThat(isOneOf(aidlInstances))))) + .thenReturn(null); + + doAnswer( + (invocation) -> { + // technically, preexisting is ignored by + // HealthServiceWrapperHidl.Notification, but still call it correctly. + sendNotification(invocation, true); + sendNotification(invocation, true); + sendNotification(invocation, false); + return null; + }) + .when(mMockedManager) + .registerForNotifications( + eq(android.hardware.health.V2_0.IHealth.kInterfaceName), + argThat(isOneOf(hidlInstances)), + any(IServiceNotification.class)); + + doReturn(mMockedManager).when(mManagerSupplier).get(); + doReturn(mMockedHal) // init calls this + .doReturn(mMockedHal) // notification 1 + .doReturn(mMockedHal) // notification 2 + .doReturn(mMockedHal2) // notification 3 + .doThrow(new RuntimeException("Should not call getService for more than 4 times")) + .when(mHealthServiceSupplier) + .get(argThat(isOneOf(hidlInstances))); + } + + private void waitHandlerThreadFinish() throws Exception { + for (int i = 0; i < 5; i++) { + if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) { + return; + } + Thread.sleep(300); + } + assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()); + } + + private static void sendNotification(InvocationOnMock invocation, boolean preexisting) + throws Exception { + ((IServiceNotification) invocation.getArguments()[2]) + .onRegistration( + android.hardware.health.V2_0.IHealth.kInterfaceName, + (String) invocation.getArguments()[1], + preexisting); + } + + private static void sendAidlRegCallback( + InvocationOnMock invocation, android.hardware.health.IHealth service) throws Exception { + ((IServiceCallback) invocation.getArguments()[1]) + .onRegistration((String) invocation.getArguments()[0], service.asBinder()); + } + + private void createWrapper() throws RemoteException { + mWrapper = + HealthServiceWrapper.create( + mRegCallbackAidl, + mMockedAidlManager, + mCallback, + mManagerSupplier, + mHealthServiceSupplier); + } + + @SmallTest + @Test + public void testWrapAidlOnly() throws Exception { + initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[0]); + createWrapper(); + waitHandlerThreadFinish(); + verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal)); + verify(mRegCallbackAidl, never()) + .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal)); + verify(mRegCallbackAidl, times(1)) + .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2)); + verify(mCallback, never()).onRegistration(any(), any(), anyString()); + } + + @SmallTest + @Test + public void testWrapPreferAidl() throws Exception { + initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[] {VENDOR}); + createWrapper(); + waitHandlerThreadFinish(); + verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal)); + verify(mRegCallbackAidl, never()) + .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal)); + verify(mRegCallbackAidl, times(1)) + .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2)); + verify(mCallback, never()).onRegistration(any(), any(), anyString()); + } + + @SmallTest + @Test + public void testWrapFallbackHidl() throws Exception { + initForInstances(new String[0], new String[] {VENDOR}); + createWrapper(); + waitHandlerThreadFinish(); + verify(mRegCallbackAidl, never()).onRegistration(any(), any()); + verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR)); + verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString()); + verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR)); + } + + @SmallTest + @Test + public void testNoService() throws Exception { + initForInstances(new String[0], new String[] {"unrelated"}); + try { + createWrapper(); + fail("Expect NoSuchElementException"); + } catch (NoSuchElementException ex) { + // expected + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/health/OWNERS b/services/tests/servicestests/src/com/android/server/health/OWNERS new file mode 100644 index 000000000000..81522fcaa09f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/health/OWNERS @@ -0,0 +1 @@ +file:platform/hardware/interfaces:/health/aidl/OWNERS diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java index b4fbf5fe40eb..184ea521e828 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java @@ -110,18 +110,20 @@ public class ActivityOptionsTest { @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { - t.setVisibility(leash, true /* visible */).apply(); + t.show(leash).apply(); } int cookieIndex = -1; if (trampoline.equals(taskInfo.baseActivity)) { cookieIndex = 0; } else if (main.equals(taskInfo.baseActivity)) { cookieIndex = 1; - mainLatch.countDown(); } if (cookieIndex >= 0) { appearedCookies[cookieIndex] = taskInfo.launchCookies.isEmpty() ? null : taskInfo.launchCookies.get(0); + if (cookieIndex == 1) { + mainLatch.countDown(); + } } } }; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 293e862a6b74..6f04f176afd8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -39,6 +39,7 @@ import static android.os.Process.NOBODY_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -2816,6 +2817,73 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); } + @Test + public void testImeInsetsFrozenFlag_resetWhenReparented() { + final ActivityRecord activity = createActivityWithTask(); + final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app"); + final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + final Task newTask = new TaskBuilder(mSupervisor).build(); + makeWindowVisible(app, imeWindow); + mDisplayContent.mInputMethodWindow = imeWindow; + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity is reparent to the new task. + activity.setState(RESUMED, "test"); + activity.reparent(newTask, 0 /* top */, "test"); + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + + @UseTestDisplay(addWindows = W_INPUT_METHOD) + @Test + public void testImeInsetsFrozenFlag_resetWhenResized() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + makeWindowVisibleAndDrawn(app, mImeWindow); + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity is reparent to the new task. + app.mActivityRecord.onResize(); + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + + @UseTestDisplay(addWindows = W_INPUT_METHOD) + @Test + public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + makeWindowVisibleAndDrawn(app, mImeWindow); + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + + // Simulate app is closing and expect the last IME is shown and IME insets is frozen. + app.mActivityRecord.commitVisibility(false, false); + app.mActivityRecord.onWindowsGone(); + + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Expect IME insets frozen state will reset when the activity has no IME focusable window. + app.mActivityRecord.forAllWindowsUnchecked(w -> { + w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; + return true; + }, true); + + app.mActivityRecord.commitVisibility(true, false); + app.mActivityRecord.onWindowsVisible(); + + assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + } + private void assertHasStartingWindow(ActivityRecord atoken) { assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingData); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index a8e17534e6ed..8e7ba4bc3293 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -258,6 +258,7 @@ public class SystemServicesTestRule implements TestRule { final ActivityManagerInternal amInternal = mAmService.mInternal; spyOn(amInternal); doNothing().when(amInternal).trimApplications(); + doNothing().when(amInternal).scheduleAppGcs(); doNothing().when(amInternal).updateCpuStats(); doNothing().when(amInternal).updateOomAdj(); doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 92b670ed9699..d88ac256be5c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -891,6 +891,40 @@ public class WindowStateTests extends WindowTestsBase { assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); } + @Test + public void testAdjustImeInsetsVisibilityWhenSwitchingApps() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); + final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + spyOn(imeWindow); + doReturn(true).when(imeWindow).isVisible(); + mDisplayContent.mInputMethodWindow = imeWindow; + + final InsetsStateController controller = mDisplayContent.getInsetsStateController(); + controller.getImeSourceProvider().setWindow(imeWindow, null, null); + + // Simulate app requests IME with updating all windows Insets State when IME is above app. + mDisplayContent.setImeLayeringTarget(app); + mDisplayContent.setImeInputTarget(app); + assertTrue(mDisplayContent.shouldImeAttachedToApp()); + controller.getImeSourceProvider().scheduleShowImePostLayout(app); + controller.getImeSourceProvider().getSource().setVisible(true); + controller.updateAboveInsetsState(imeWindow, false); + + // Expect all app windows behind IME can receive IME insets visible. + assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); + assertTrue(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); + + // Simulate app plays closing transition to app2. + app.mActivityRecord.commitVisibility(false, false); + assertTrue(app.mActivityRecord.mLastImeShown); + assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Verify the IME insets is visible on app, but not for app2 during app task switching. + assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); + assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); + } + @UseTestDisplay(addWindows = { W_ACTIVITY }) @Test public void testUpdateImeControlTargetWhenLeavingMultiWindow() { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index d048f1842aa3..589f9134f227 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -44,6 +45,7 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import java.util.function.BiFunction; @@ -126,7 +128,7 @@ public class WindowTokenTests extends WindowTestsBase { final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1"); final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2"); - mDisplayContent.removeWindowToken(token.token); + mDisplayContent.removeWindowToken(token.token, true /* animateExit */); // Verify that the token is no longer mapped on the display assertNull(mDisplayContent.getWindowToken(token.token)); // Verify that the token is still attached to its parent @@ -261,4 +263,29 @@ public class WindowTokenTests extends WindowTestsBase { assertNotNull(app.getFrozenInsetsState()); assertNull(mDisplayContent.mInputMethodWindow.getFrozenInsetsState()); } + + @Test + public void testRemoveWindowToken_noAnimateExitWhenSet() { + final TestWindowToken token = createTestWindowToken(0, mDisplayContent); + final WindowState win = createWindow(null, TYPE_APPLICATION, token, "win"); + makeWindowVisible(win); + assertTrue(win.isOnScreen()); + spyOn(win); + spyOn(win.mWinAnimator); + spyOn(win.mToken); + + // Invoking removeWindowToken with setting no window exit animation and not remove window + // immediately. verify the window will hide without applying exit animation. + mWm.removeWindowToken(win.mToken.token, false /* removeWindows */, false /* animateExit */, + mDisplayContent.mDisplayId); + verify(win).onSetAppExiting(Mockito.eq(false) /* animateExit */); + verify(win).hide(false /* doAnimation */, false /* requestAnim */); + assertFalse(win.isOnScreen()); + verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false); + assertTrue(win.mToken.hasChild()); + + // Even though the window is being removed afterwards, it won't apply exit animation. + win.removeIfPossible(); + verify(win.mWinAnimator, Mockito.never()).applyAnimationLocked(TRANSIT_EXIT, false); + } } diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index cac716efe78f..0ddd52dfc76d 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -69,7 +69,14 @@ import java.util.List; * them know that the app has crashed and that their call was continued using the pre-loaded dialer * app. * <p> - * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call. + * The pre-loaded dialer will ALWAYS be used when the user places an emergency call, even if your + * app fills the {@link android.app.role.RoleManager#ROLE_DIALER} role. To ensure an optimal + * experience when placing an emergency call, the default dialer should ALWAYS use + * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls (including + * emergency calls). This ensures that the platform is able to verify that the request came from + * the default dialer. If a non-preloaded dialer app uses {@link Intent#ACTION_CALL} to place an + * emergency call, it will be raised to the preloaded dialer app using {@link Intent#ACTION_DIAL} + * for confirmation; this is a suboptimal user experience. * <p> * Below is an example manifest registration for an {@code InCallService}. The meta-data * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular diff --git a/telephony/OWNERS b/telephony/OWNERS index 4df8a4bc6413..4016ba7de379 100644 --- a/telephony/OWNERS +++ b/telephony/OWNERS @@ -1,6 +1,5 @@ set noparent -amitmahajan@google.com breadley@google.com fionaxu@google.com jackyu@google.com @@ -8,10 +7,10 @@ rgreenwalt@google.com tgunn@google.com jminjie@google.com shuoq@google.com -nazaninb@google.com sarahchin@google.com xiaotonj@google.com huiwang@google.com jayachandranc@google.com chinmayd@google.com amruthr@google.com +sasindran@google.com diff --git a/telephony/common/Android.bp b/telephony/common/Android.bp index 1cacc0365fe1..b0a812bf57a3 100644 --- a/telephony/common/Android.bp +++ b/telephony/common/Android.bp @@ -21,7 +21,10 @@ filegroup { filegroup { name: "framework-mms-shared-srcs", - visibility: ["//packages/apps/Bluetooth"], + visibility: [ + "//packages/apps/Bluetooth", + "//packages/modules/Bluetooth/android/app", + ], srcs: [ "com/google/android/mms/**/*.java", ], diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 4d81b5e54470..ce4363f66d1c 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -107,6 +107,26 @@ public final class TelephonyPermissions { } } + + /** + * Check whether the caller (or self, if not processing an IPC) has non dangerous + * read phone state permission. + * @param context app context + * @param message detail message + * @return true if permission is granted, else false + */ + public static boolean checkCallingOrSelfReadNonDangerousPhoneStateNoThrow( + Context context, String message) { + try { + context.enforcePermission( + Manifest.permission.READ_BASIC_PHONE_STATE, + Binder.getCallingPid(), Binder.getCallingUid(), message); + return true; + } catch (SecurityException se) { + return false; + } + } + /** * Check whether the app with the given pid/uid can read phone state. * diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java index ae597e02f33c..2b355ae216e3 100644 --- a/telephony/java/android/telephony/AvailableNetworkInfo.java +++ b/telephony/java/android/telephony/AvailableNetworkInfo.java @@ -16,11 +16,14 @@ package android.telephony; +import android.annotation.IntDef; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.telephony.RadioAccessSpecifier; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -32,7 +35,6 @@ import java.util.Objects; * Network Service when passed through {@link TelephonyManager#updateAvailableNetworks} */ public final class AvailableNetworkInfo implements Parcelable { - /* * Defines number of priority level high. */ @@ -48,6 +50,14 @@ public final class AvailableNetworkInfo implements Parcelable { */ public static final int PRIORITY_LOW = 3; + /** @hide */ + @IntDef({ + PRIORITY_HIGH, + PRIORITY_MED, + PRIORITY_LOW, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AvailableNetworkInfoPriority {} /** * subscription Id of the available network. This value must be one of the entry retrieved from * {@link SubscriptionManager#getOpportunisticSubscriptions} @@ -62,7 +72,7 @@ public final class AvailableNetworkInfo implements Parcelable { * for network selection. If there are more than one subId with highest priority then the * network with highest RSRP is chosen. */ - private int mPriority; + private @AvailableNetworkInfoPriority int mPriority; /** * Describes the List of PLMN ids (MCC-MNC) associated with mSubId. @@ -77,8 +87,7 @@ public final class AvailableNetworkInfo implements Parcelable { * Opportunistic network service will use these bands to scan. * * When no specific bands are specified (empty array or null) CBRS band - * {@link AccessNetworkConstants.EutranBand.BAND_48 - * } will be used for network scan. + * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan. * * See {@link AccessNetworkConstants} for details. * @@ -94,7 +103,7 @@ public final class AvailableNetworkInfo implements Parcelable { * If this entry is left empty, {@link RadioAcccessSpecifier}s with {@link AccessNetworkType}s * of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed - * by Opportunistic network service. + * by Opportunistic network service for a network scan. */ private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers; @@ -117,6 +126,7 @@ public final class AvailableNetworkInfo implements Parcelable { * network with highest RSRP is chosen. * @return priority level */ + @AvailableNetworkInfoPriority public int getPriority() { return mPriority; } @@ -149,15 +159,9 @@ public final class AvailableNetworkInfo implements Parcelable { * Returns a list of {@link RadioAccessSpecifier} associated with the available network. * Opportunistic network service will use this to determine which bands to scan for. * - * the returned value is one of {@link AccessNetworkConstants.AccessNetworkType}. When no - * specific access network type is specified, {@link RadioAccessSpecifier}s with {@link - * AccessNetworkType}s of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link - * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed - * by Opportunistic network service. * @return the access network type associated with the available network. - * @hide */ - public List<RadioAccessSpecifier> getRadioAccessSpecifiers() { + public @NonNull List<RadioAccessSpecifier> getRadioAccessSpecifiers() { return (List<RadioAccessSpecifier>) mRadioAccessSpecifiers.clone(); } @@ -193,9 +197,9 @@ public final class AvailableNetworkInfo implements Parcelable { } /** @hide */ - private AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs, - @NonNull List<Integer> bands, @NonNull List<RadioAccessSpecifier> - radioAccessSpecifiers) { + private AvailableNetworkInfo(int subId, @AvailableNetworkInfoPriority int priority, + @NonNull List<String> mccMncs, @NonNull List<Integer> bands, + @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) { mSubId = subId; mPriority = priority; mMccMncs = new ArrayList<String>(mccMncs); @@ -261,27 +265,39 @@ public final class AvailableNetworkInfo implements Parcelable { * * <pre><code> * - * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder() - * .setSubId(1) + * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder(subId) * .setPriority(AvailableNetworkInfo.PRIORITY_MED) + * .setRadioAccessSpecifiers(radioAccessSpecifiers) + * .setMccMncs(mccMncs) * .build(); * </code></pre> - * - * @hide */ public static final class Builder { private int mSubId = Integer.MIN_VALUE; - private int mPriority = AvailableNetworkInfo.PRIORITY_LOW; + private @AvailableNetworkInfoPriority int mPriority = AvailableNetworkInfo.PRIORITY_LOW; private ArrayList<String> mMccMncs = new ArrayList<>(); - private ArrayList<Integer> mBands = new ArrayList<>(); private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers = new ArrayList<>(); - public @NonNull Builder setSubId(int subId) { + /** + * + */ + /** + * Creates an AvailableNetworkInfo Builder with specified subscription id. + * + * @param subId of the availableNetwork. + */ + public Builder(int subId) { mSubId = subId; - return this; } - public @NonNull Builder setPriority(int priority) { + /** + * Sets the priority for the subscription id. + * + * @param priority of the subscription id. See {@link AvailableNetworkInfo#getPriority} for + * more details + * @return the original Builder object. + */ + public @NonNull Builder setPriority(@AvailableNetworkInfoPriority int priority) { if (priority > AvailableNetworkInfo.PRIORITY_LOW || priority < AvailableNetworkInfo.PRIORITY_HIGH) { throw new IllegalArgumentException("A valid priority must be set"); @@ -290,30 +306,48 @@ public final class AvailableNetworkInfo implements Parcelable { return this; } - public @NonNull Builder setMccMncs(@NonNull ArrayList<String> mccMncs) { - Objects.requireNonNull(mccMncs, "A non-null ArrayList of mccmncs must be set. An empty " - + "list is still accepted. Please read documentation in " - + "AvailableNetworkService to see consequences of an empty Arraylist."); - mMccMncs = mccMncs; + /** + * Sets the list of mccmncs associated with the subscription id. + * + * @param mccMncs nonull list of mccmncs. An empty List is still accepted. Please read + * documentation in {@link AvailableNetworkInfo} to see consequences of an empty List. + * @return the original Builder object. + */ + public @NonNull Builder setMccMncs(@NonNull List<String> mccMncs) { + Objects.requireNonNull(mccMncs, "A non-null List of mccmncs must be set. An empty " + + "List is still accepted. Please read documentation in " + + "AvailableNetworkInfo to see consequences of an empty List."); + mMccMncs = new ArrayList<>(mccMncs); return this; } + /** + * Sets the list of mccmncs associated with the subscription id. + * + * @param radioAccessSpecifiers nonull list of radioAccessSpecifiers. An empty List is still + * accepted. Please read documentation in {@link AvailableNetworkInfo} to see + * consequences of an empty List. + * @return the original Builder object. + */ public @NonNull Builder setRadioAccessSpecifiers( - @NonNull ArrayList<RadioAccessSpecifier> radioAccessSpecifiers) { - Objects.requireNonNull(radioAccessSpecifiers, "A non-null ArrayList of " - + "RadioAccessSpecifiers must be set. An empty list is still accepted. Please " - + "read documentation in AvailableNetworkService to see consequences of an " - + "empty Arraylist."); - mRadioAccessSpecifiers = radioAccessSpecifiers; + @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) { + Objects.requireNonNull(radioAccessSpecifiers, "A non-null List of " + + "RadioAccessSpecifiers must be set. An empty List is still accepted. Please " + + "read documentation in AvailableNetworkInfo to see consequences of an " + + "empty List."); + mRadioAccessSpecifiers = new ArrayList<>(radioAccessSpecifiers); return this; } + /** + * @return an AvailableNetworkInfo object with all the fields previously set by the Builder. + */ public @NonNull AvailableNetworkInfo build() { if (mSubId == Integer.MIN_VALUE) { throw new IllegalArgumentException("A valid subId must be set"); } - return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, mBands, + return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, new ArrayList<>(), mRadioAccessSpecifiers); } } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d11c66702d99..5ffe45f8c489 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -3674,6 +3675,49 @@ public class CarrierConfigManager { "show_wifi_calling_icon_in_status_bar_bool"; /** + * Configuration to indicate that the carrier supports opportunistic data + * auto provisioning. Based on this flag, the device downloads and activates + * corresponding opportunistic profile. + */ + public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = + "carrier_supports_opp_data_auto_provisioning_bool"; + + /** + * SMDP+ server address for downloading opportunistic eSIM profile. + * FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the + * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 [15] excluding '$'. + */ + public static final String KEY_SMDP_SERVER_ADDRESS_STRING = + "smdp_server_address_string"; + + /** + * This timer value is used in the eSIM Exponential Backoff download retry algorithm. + * Value should be in seconds. + * <OL> + * <LI>When the first download failure occurs, retry download after BACKOFF_TIMER_VALUE + * seconds.</LI> + * + * <LI>If download fails again then, retry after either BACKOFF_TIMER_VALUE, + * 2xBACKOFF_TIMER_VALUE, or 3xBACKOFF_TIMER_VALUE seconds.</LI> + * + * <LI>In general after the cth failed attempt, retry after k * BACKOFF_TIMER_VALUE + * seconds, where k is a random integer between 1 and 2^c − 1. Max c value is + * {@link #KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT}</LI> + * </OL> + */ + public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT = + "esim_download_retry_backoff_timer_sec_int"; + + /** + * If eSIM profile download fails then, the number of retry attempts by UE + * will be based on this configuration. If download still fails even after the + * MAX attempts configured by this item then the retry is postponed until next + * device bootup. + */ + public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT = + "esim_max_download_retry_attempts_int"; + + /** * Controls RSRP threshold at which OpportunisticNetworkService will decide whether * the opportunistic network is good enough for internet data. */ @@ -3883,6 +3927,30 @@ public class CarrierConfigManager { public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL = "enabled_4g_opportunistic_network_scan_bool"; + /** + * Only relevant when the device supports opportunistic networks but does not support + * simultaneuous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network + * goes out of service before switching the 5G capability back to primary stack. The idea of + * waiting a few seconds is to minimize the calling of the expensive capability switching + * operation in the case where CBRS goes back into service shortly after going out of it. + * + * @hide + */ + public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG = + "time_to_switch_back_to_primary_if_opportunistic_oos_long"; + + /** + * Only relevant when the device supports opportunistic networks but does not support + * simultaneuous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back + * to primary stack due to opportunistic network being OOS. The idea is to minimizing the + * 'ping-ponging' effect where device is constantly witching capability back and forth between + * primary and opportunistic stack. + * + * @hide + */ + public static final String KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG + = "opportunistic_time_to_scan_after_capability_switch_to_primary_long"; + /** * Indicates zero or more emergency number prefix(es), because some carrier requires * if users dial an emergency number address with a specific prefix, the combination of the @@ -5073,16 +5141,6 @@ public class CarrierConfigManager { "call_composer_picture_server_url_string"; /** - * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values - * for IPv4 and IPv6 if both are sent. - * TODO: remove in later release - * - * @hide - */ - public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED = - "use_lower_mtu_value_if_both_received"; - - /** * Determines the default RTT mode. * * Upon first boot, when the user has not yet set a value for their preferred RTT mode, @@ -5191,6 +5249,26 @@ public class CarrierConfigManager { "display_no_data_notification_on_permanent_failure_bool"; /** + * Boolean indicating if the VoNR setting is visible in the Call Settings menu. + * If true, the VoNR setting menu will be visible. If false, the menu will be gone. + * + * Disabled by default. + * + * @hide + */ + public static final String KEY_VONR_SETTING_VISIBILITY_BOOL = "vonr_setting_visibility_bool"; + + /** + * Flag specifying whether VoNR should be enabled for carrier. + * If true, VoNr will be enabled. If false, hard disabled. + * + * Disabled by default. + * + * @hide + */ + public static final String KEY_VONR_ENABLED_BOOL = "vonr_enabled_bool"; + + /** * Determine whether unthrottle data retry when tracking area code (TAC/LAC) from cell changes * * @hide @@ -5692,6 +5770,10 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_UNMETERED_NR_SA_SUB6_BOOL, false); sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false); sDefaults.putBoolean(KEY_SHOW_WIFI_CALLING_ICON_IN_STATUS_BAR_BOOL, false); + sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL, false); + sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, ""); + sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5); + sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108); /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */ @@ -5735,6 +5817,10 @@ public class CarrierConfigManager { /* Default value is 2 seconds. */ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000); sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true); + sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000); + sDefaults.putInt( + KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG, + 120000); sDefaults.putAll(Gps.getDefaults()); sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY, new int[] { @@ -5792,7 +5878,6 @@ public class CarrierConfigManager { sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); - sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false); sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true); sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0); @@ -5806,6 +5891,8 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CARRIER_PROVISIONING_APP_STRING, ""); sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false); sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false); + sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, false); + sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false); } /** @@ -5874,12 +5961,15 @@ public class CarrierConfigManager { * any carrier specific configuration has been applied. * * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app + * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}). * * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}. * @return A {@link PersistableBundle} containing the config for the given subId, or default * values for an invalid subId. */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) @Nullable public PersistableBundle getConfigForSubId(int subId) { try { @@ -5968,10 +6058,13 @@ public class CarrierConfigManager { * called to confirm whether any carrier specific configuration has been applied. * * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app + * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}). * * @see #getConfigForSubId */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) @Nullable public PersistableBundle getConfig() { return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId()); @@ -5980,8 +6073,8 @@ public class CarrierConfigManager { /** * Determines whether a configuration {@link PersistableBundle} obtained from * {@link #getConfig()} or {@link #getConfigForSubId(int)} corresponds to an identified carrier. - * <p> - * When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} + * + * <p>When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} * broadcast which informs it that the carrier configuration has changed, it is possible * that another reload of the carrier configuration has begun since the intent was sent. * In this case, the carrier configuration the app fetches (e.g. via {@link #getConfig()}) @@ -5990,14 +6083,12 @@ public class CarrierConfigManager { * return true because it may belong to another previous identified carrier. Users should * always call {@link #getConfig()} or {@link #getConfigForSubId(int)} after receiving the * broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED}. - * </p> - * <p> - * After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always + * + * <p>After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always * use this method to confirm whether any carrier specific configuration has been applied. * Especially when an app misses the broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED} but it * still needs to get the current configuration, it must use this method to verify whether the * configuration is default or carrier overridden. - * </p> * * @param bundle the configuration bundle to be checked. * @return boolean true if any carrier specific configuration bundle has been applied, false @@ -6009,19 +6100,20 @@ public class CarrierConfigManager { /** * Calling this method triggers telephony services to fetch the current carrier configuration. - * <p> - * Normally this does not need to be called because the platform reloads config on its own. + * + * <p>Normally this does not need to be called because the platform reloads config on its own. * This should be called by a carrier service app if it wants to update config at an arbitrary * moment. - * </p> - * <p>Requires that the calling app has carrier privileges. - * <p> - * This method returns before the reload has completed, and - * {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an - * arbitrary thread. - * </p> - * @see TelephonyManager#hasCarrierPrivileges + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or the calling app + * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}). + * + * <p>This method returns before the reload has completed, and {@link + * android.service.carrier.CarrierService#onLoadConfig} will be called from an arbitrary thread. */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyConfigChangedForSubId(int subId) { try { ICarrierConfigLoader loader = getICarrierConfigLoader(); @@ -6037,11 +6129,10 @@ public class CarrierConfigManager { } /** - * Request the carrier config loader to update the cofig for phoneId. - * <p> - * Depending on simState, the config may be cleared or loaded from config app. This is only used - * by SubscriptionInfoUpdater. - * </p> + * Request the carrier config loader to update the config for phoneId. + * + * <p>Depending on simState, the config may be cleared or loaded from config app. This is only + * used by SubscriptionInfoUpdater. * * @hide */ @@ -6112,13 +6203,16 @@ public class CarrierConfigManager { * Gets the configuration values for a component using its prefix. * * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app + * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}). * * @param prefix prefix of the component. * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}. * * @see #getConfigForSubId */ + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(Manifest.permission.READ_PHONE_STATE) @Nullable public PersistableBundle getConfigByComponentForSubId(@NonNull String prefix, int subId) { PersistableBundle configs = getConfigForSubId(subId); diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 42d7707c97d9..fc76f99cf074 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -119,7 +119,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsRcsManager(mContext, subscriptionId, sRcsCache); + return new ImsRcsManager(mContext, subscriptionId, sRcsCache, sTelephonyCache); } /** @@ -135,7 +135,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsMmTelManager(subscriptionId, sTelephonyCache); + return new ImsMmTelManager(mContext, subscriptionId, sTelephonyCache); } /** @@ -157,7 +157,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new SipDelegateManager(mContext, subscriptionId, sRcsCache); + return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache); } private static IImsRcsController getIImsRcsControllerInterface() { diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index d250088c2f10..95448c7807cc 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -242,6 +242,11 @@ public final class PhysicalChannelConfig implements Parcelable { } /** + * The physical cell ID which differentiates cells using the same radio channel. + * + * In GERAN, this value is the BSIC. The range is [0-63]. + * Reference: 3GPP TS 3.03 section 4.2.2. + * * In UTRAN, this value is primary scrambling code. The range is [0, 511]. * Reference: 3GPP TS 25.213 section 5.2.2. * diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java index 41e24ddb3fa6..9cb80f1814f9 100644 --- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java +++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java @@ -17,6 +17,8 @@ package android.telephony; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; @@ -66,10 +68,10 @@ public final class SignalStrengthUpdateRequest implements Parcelable { private final IBinder mLiveToken; private SignalStrengthUpdateRequest( - @NonNull List<SignalThresholdInfo> signalThresholdInfos, + @Nullable List<SignalThresholdInfo> signalThresholdInfos, boolean isReportingRequestedWhileIdle, boolean isSystemThresholdReportingRequestedWhileIdle) { - validate(signalThresholdInfos); + validate(signalThresholdInfos, isSystemThresholdReportingRequestedWhileIdle); mSignalThresholdInfos = signalThresholdInfos; mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; @@ -128,13 +130,15 @@ public final class SignalStrengthUpdateRequest implements Parcelable { /** * Set the builder object if require reporting on the system thresholds when device is idle. * - * This can only used by the system caller. + * <p>This can only used by the system caller. Requires permission + * {@link android.Manifest.permission#LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH}. * * @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the * system thresholds when device is idle * @return the builder to facilitate the chaining * @hide */ + @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle( boolean isSystemThresholdReportingRequestedWhileIdle) { mIsSystemThresholdReportingRequestedWhileIdle = @@ -265,8 +269,12 @@ public final class SignalStrengthUpdateRequest implements Parcelable { * Throw IAE if SignalThresholdInfo collection is null or empty, * or the SignalMeasurementType for the same RAN in the collection is not unique. */ - private static void validate(Collection<SignalThresholdInfo> infos) { - if (infos == null || infos.isEmpty()) { + private static void validate(Collection<SignalThresholdInfo> infos, + boolean isSystemThresholdReportingRequestedWhileIdle) { + // System app (like Bluetooth) can specify the request to report system thresholds while + // device is idle (with permission protection). In this case, the request doesn't need to + // provide a non-empty list of SignalThresholdInfo which is only asked for public apps. + if (infos == null || (infos.isEmpty() && !isSystemThresholdReportingRequestedWhileIdle)) { throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty"); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 3b44a34048b8..d5315acf60d5 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -949,6 +949,15 @@ public class SubscriptionManager { public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS; /** + * TelephonyProvider column name for NR Advanced calling + * Determines if the user has enabled VoNR settings for this subscription. + * + * @hide + */ + public static final String NR_ADVANCED_CALLING_ENABLED = + SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED; + + /** * Profile class of the subscription * @hide */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a1bed29dec70..453547c49498 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -315,6 +315,12 @@ public class TelephonyManager { */ public static final int UNINITIALIZED_CARD_ID = -2; + /** + * Default port index for the UICC Card + * @hide + */ + public static final int DEFAULT_PORT_INDEX = 0; + private final Context mContext; private final int mSubId; @UnsupportedAppUsage @@ -1889,9 +1895,10 @@ public class TelephonyManager { * the IMEI/SV for GSM phones. Return null if the software version is * not available. * <p> - * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}. */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) @Nullable public String getDeviceSoftwareVersion() { return getDeviceSoftwareVersion(getSlotIndex()); @@ -2953,7 +2960,9 @@ public class TelephonyManager { * when opportunistic network is providing cellular internet connection to the user. * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE + * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link #hasCarrierPrivileges}). * * @return the network type * @@ -2976,7 +2985,9 @@ public class TelephonyManager { * @see #NETWORK_TYPE_NR */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) public @NetworkType int getDataNetworkType() { return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } @@ -3014,10 +3025,14 @@ public class TelephonyManager { * Returns the NETWORK_TYPE_xxxx for voice * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE + * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link #hasCarrierPrivileges}). */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) public @NetworkType int getVoiceNetworkType() { return getVoiceNetworkType(getSubId()); } @@ -6581,12 +6596,7 @@ public class TelephonyManager { * @param AID Application id. See ETSI 102.221 and 101.220. * @param p2 P2 parameter (described in ISO 7816-4). * @return an IccOpenLogicalChannelResponse object. - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openLogicalChannel(byte[], byte)}. */ - @Deprecated public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) { return iccOpenLogicalChannel(getSubId(), AID, p2); } @@ -6617,12 +6627,7 @@ public class TelephonyManager { * @param p2 P2 parameter (described in ISO 7816-4). * @return an IccOpenLogicalChannelResponse object. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openLogicalChannel(byte[], byte)}. */ - @Deprecated public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) { try { ITelephony telephony = getITelephony(); @@ -6650,10 +6655,7 @@ public class TelephonyManager { * iccOpenLogicalChannel. * @return true if the channel was closed successfully. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.Channel#close()}. */ - @Deprecated @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) { @@ -6680,10 +6682,7 @@ public class TelephonyManager { * @param channel is the channel id to be closed as returned by a successful * iccOpenLogicalChannel. * @return true if the channel was closed successfully. - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.Channel#close()}. */ - @Deprecated public boolean iccCloseLogicalChannel(int channel) { return iccCloseLogicalChannel(getSubId(), channel); } @@ -6702,10 +6701,7 @@ public class TelephonyManager { * iccOpenLogicalChannel. * @return true if the channel was closed successfully. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.Channel#close()}. */ - @Deprecated public boolean iccCloseLogicalChannel(int subId, int channel) { try { ITelephony telephony = getITelephony(); @@ -6741,10 +6737,7 @@ public class TelephonyManager { * @return The APDU response from the ICC card with the status appended at the end, or null if * there is an issue connecting to the Telephony service. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi @Nullable @@ -6782,10 +6775,7 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated public String iccTransmitApduLogicalChannel(int channel, int cla, int instruction, int p1, int p2, int p3, String data) { return iccTransmitApduLogicalChannel(getSubId(), channel, cla, @@ -6814,10 +6804,7 @@ public class TelephonyManager { * @return The APDU response from the ICC card with the status appended at * the end. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated public String iccTransmitApduLogicalChannel(int subId, int channel, int cla, int instruction, int p1, int p2, int p3, String data) { try { @@ -6853,13 +6840,7 @@ public class TelephonyManager { * @return The APDU response from the ICC card with the status appended at * the end. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)}, - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi @NonNull @@ -6895,13 +6876,7 @@ public class TelephonyManager { * @param data Data to be sent with the APDU. * @return The APDU response from the ICC card with the status appended at * the end. - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)}, - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated public String iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2, int p3, String data) { return iccTransmitApduBasicChannel(getSubId(), cla, @@ -6928,13 +6903,7 @@ public class TelephonyManager { * @return The APDU response from the ICC card with the status appended at * the end. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)}, - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated public String iccTransmitApduBasicChannel(int subId, int cla, int instruction, int p1, int p2, int p3, String data) { try { @@ -6962,13 +6931,7 @@ public class TelephonyManager { * @param p3 P3 value of the APDU command. * @param filePath * @return The APDU response. - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)}, - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3, String filePath) { return iccExchangeSimIO(getSubId(), fileID, command, p1, p2, p3, filePath); @@ -6990,13 +6953,7 @@ public class TelephonyManager { * @param filePath * @return The APDU response. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)}, - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2, int p3, String filePath) { try { @@ -7022,13 +6979,7 @@ public class TelephonyManager { * @return The APDU response from the ICC card in hexadecimal format * with the last 4 bytes being the status word. If the command fails, * returns an empty string. - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)}, - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated public String sendEnvelopeWithStatus(String content) { return sendEnvelopeWithStatus(getSubId(), content); } @@ -7048,13 +6999,7 @@ public class TelephonyManager { * with the last 4 bytes being the status word. If the command fails, * returns an empty string. * @hide - * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See - * {@link android.se.omapi.SEService#getUiccReader(int)}, - * {@link android.se.omapi.Reader#openSession()}, - * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)}, - * {@link android.se.omapi.Channel#transmit(byte[])}. */ - @Deprecated public String sendEnvelopeWithStatus(int subId, String content) { try { ITelephony telephony = getITelephony(); @@ -9969,7 +9914,9 @@ public class TelephonyManager { * * <p>Requires one of the following permissions: * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}, - * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or that the calling app has carrier + * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or + * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE + * READ_BASIC_PHONE_STATE} or that the calling app has carrier * privileges (see {@link #hasCarrierPrivileges}). * * <p>Note that this does not take into account any data restrictions that may be present on the @@ -9980,7 +9927,8 @@ public class TelephonyManager { */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, - android.Manifest.permission.READ_PHONE_STATE}) + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabled() { try { return isDataEnabledForReason(DATA_ENABLED_REASON_USER); @@ -9999,14 +9947,17 @@ public class TelephonyManager { * * <p>Requires one of the following permissions: * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}, - * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app + * {@link android.Manifest.permission#READ_PHONE_STATE} or + * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE + * READ_BASIC_PHONE_STATE} or that the calling app * has carrier privileges (see {@link #hasCarrierPrivileges}). * * @return {@code true} if the data roaming is enabled on the subscription, otherwise return * {@code false}. */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, - android.Manifest.permission.READ_PHONE_STATE}) + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataRoamingEnabled() { boolean isDataRoamingEnabled = false; try { @@ -12148,6 +12099,100 @@ public class TelephonyManager { } /** + * No error. Operation succeeded. + * @hide + */ + public static final int ENABLE_VONR_SUCCESS = 0; + + /** + * Radio is not available. + * @hide + */ + public static final int ENABLE_VONR_RADIO_NOT_AVAILABLE = 2; + + /** + * Internal Radio error. + * @hide + */ + public static final int ENABLE_VONR_RADIO_ERROR = 3; + + /** + * Voice over NR enable/disable request is received when system is in invalid state. + * @hide + */ + public static final int ENABLE_VONR_RADIO_INVALID_STATE = 4; + + /** + * Voice over NR enable/disable request is not supported. + * @hide + */ + public static final int ENABLE_VONR_REQUEST_NOT_SUPPORTED = 5; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"EnableVoNrResult"}, value = { + ENABLE_VONR_SUCCESS, + ENABLE_VONR_RADIO_NOT_AVAILABLE, + ENABLE_VONR_RADIO_ERROR, + ENABLE_VONR_RADIO_INVALID_STATE, + ENABLE_VONR_REQUEST_NOT_SUPPORTED}) + public @interface EnableVoNrResult {} + + /** + * Enable or disable Voice over NR (VoNR) + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. + * + * @param enabled enable or disable VoNR. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public @EnableVoNrResult int setVoNrEnabled(boolean enabled) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.setVoNrEnabled( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setVoNrEnabled", e); + } + + return ENABLE_VONR_RADIO_INVALID_STATE; + } + + /** + * Is Voice over NR (VoNR) enabled. + * @return true if Voice over NR (VoNR) is enabled else false. Enabled state does not mean + * voice call over NR is active or voice ove NR is available. It means the device is allowed to + * register IMS over NR. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isVoNrEnabled() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.isVoNrEnabled(getSubId()); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "isVoNrEnabled RemoteException", ex); + ex.rethrowFromSystemServer(); + } + return false; + } + + /** * Carrier action to start or stop reporting default network available events. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the @@ -12311,16 +12356,21 @@ public class TelephonyManager { * <p>If this object has been created with {@link #createForSubscriptionId}, applies * to the given subId. Otherwise, applies to * {@link SubscriptionManager#getDefaultDataSubscriptionId()} - * * @param reason the reason the data enable change is taking place * @return whether data is enabled for a reason. * <p>Requires Permission: + * The calling app has carrier privileges (see {@link #hasCarrierPrivileges}) or * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or - * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} or + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE} * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, - android.Manifest.permission.READ_PHONE_STATE}) + android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.MODIFY_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE + }) public boolean isDataEnabledForReason(@DataEnabledReason int reason) { return isDataEnabledForReason(getSubId(), reason); } @@ -12425,26 +12475,6 @@ public class TelephonyManager { } /** - * Enable or disable signal strength changes from radio will always be reported in any - * condition (e.g. screen is off). This is only allowed for System caller. - * - * @param isEnabled {@code true} for enabling; {@code false} for disabling. - * @hide - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void setAlwaysReportSignalStrength(boolean isEnabled) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - telephony.setAlwaysReportSignalStrength(getSubId(), isEnabled); - } - } catch (RemoteException ex) { - Log.e(TAG, "setAlwaysReportSignalStrength RemoteException", ex); - ex.rethrowAsRuntimeException(); - } - } - - /** * Get the most recently available signal strength information. * * Get the most recent SignalStrength information reported by the modem. Due @@ -12475,14 +12505,11 @@ public class TelephonyManager { * <LI>And possibly others.</LI> * </UL> * @return {@code true} if the overall data connection is allowed; {@code false} if not. - * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or - * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} or - * android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE */ @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, - android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataConnectionAllowed() { boolean retVal = false; try { @@ -13547,15 +13574,18 @@ public class TelephonyManager { } /** - * It indicates whether modem is enabled or not per slot. - * It's the corresponding status of TelephonyManager.enableModemForSlot. + * Indicates whether or not there is a modem stack enabled for the given SIM slot. * * <p>Requires Permission: - * READ_PRIVILEGED_PHONE_STATE or - * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, + * READ_PRIVILEGED_PHONE_STATE or that the calling app has carrier privileges (see + * {@link #hasCarrierPrivileges()}). + * * @param slotIndex which slot it's checking. */ - @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) public boolean isModemEnabledForSlot(int slotIndex) { try { ITelephony telephony = getITelephony(); diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index be1502ad49f2..1ef04be8f242 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -147,7 +147,12 @@ public class ApnSetting implements Parcelable { } // Possible values for authentication types. - /** No authentication type. */ + /** + * Authentication type is unknown. + * @hide + */ + public static final int AUTH_TYPE_UNKNOWN = -1; + /** Authentication is not required. */ public static final int AUTH_TYPE_NONE = 0; /** Authentication type for PAP. */ public static final int AUTH_TYPE_PAP = 1; @@ -357,6 +362,7 @@ public class ApnSetting implements Parcelable { /** @hide */ @IntDef(prefix = { "AUTH_TYPE_" }, value = { + AUTH_TYPE_UNKNOWN, AUTH_TYPE_NONE, AUTH_TYPE_PAP, AUTH_TYPE_CHAP, @@ -498,7 +504,8 @@ public class ApnSetting implements Parcelable { private final String mOperatorNumeric; private final int mProtocol; private final int mRoamingProtocol; - private final int mMtu; + private final int mMtuV4; + private final int mMtuV6; private final boolean mCarrierEnabled; @@ -522,13 +529,25 @@ public class ApnSetting implements Parcelable { private final int mSkip464Xlat; /** - * Returns the MTU size of the mobile interface to which the APN connected. + * Returns the MTU size of the IPv4 mobile interface to which the APN connected. Note this value + * is used only if MTU size is not provided in {@link DataCallResponse}. * * @return the MTU size of the APN * @hide */ - public int getMtu() { - return mMtu; + public int getMtuV4() { + return mMtuV4; + } + + /** + * Returns the MTU size of the IPv6 mobile interface to which the APN connected. Note this value + * is used only if MTU size is not provided in {@link DataCallResponse}. + * + * @return the MTU size of the APN + * @hide + */ + public int getMtuV6() { + return mMtuV6; } /** @@ -879,13 +898,18 @@ public class ApnSetting implements Parcelable { this.mMmsProxyPort = builder.mMmsProxyPort; this.mUser = builder.mUser; this.mPassword = builder.mPassword; - this.mAuthType = builder.mAuthType; + this.mAuthType = (builder.mAuthType != AUTH_TYPE_UNKNOWN) + ? builder.mAuthType + : TextUtils.isEmpty(builder.mUser) + ? AUTH_TYPE_NONE + : AUTH_TYPE_PAP_OR_CHAP; this.mApnTypeBitmask = builder.mApnTypeBitmask; this.mId = builder.mId; this.mOperatorNumeric = builder.mOperatorNumeric; this.mProtocol = builder.mProtocol; this.mRoamingProtocol = builder.mRoamingProtocol; - this.mMtu = builder.mMtu; + this.mMtuV4 = builder.mMtuV4; + this.mMtuV6 = builder.mMtuV6; this.mCarrierEnabled = builder.mCarrierEnabled; this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask; this.mProfileId = builder.mProfileId; @@ -903,66 +927,6 @@ public class ApnSetting implements Parcelable { /** * @hide */ - public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName, - String apnName, String proxyAddress, int proxyPort, Uri mmsc, - String mmsProxyAddress, int mmsProxyPort, String user, String password, - int authType, int mApnTypeBitmask, int protocol, int roamingProtocol, - boolean carrierEnabled, int networkTypeBitmask, int profileId, - boolean modemCognitive, int maxConns, int waitTime, int maxConnsTime, int mtu, - int mvnoType, String mvnoMatchData, int apnSetId, int carrierId, int skip464xlat) { - return new Builder() - .setId(id) - .setOperatorNumeric(operatorNumeric) - .setEntryName(entryName) - .setApnName(apnName) - .setProxyAddress(proxyAddress) - .setProxyPort(proxyPort) - .setMmsc(mmsc) - .setMmsProxyAddress(mmsProxyAddress) - .setMmsProxyPort(mmsProxyPort) - .setUser(user) - .setPassword(password) - .setAuthType(authType) - .setApnTypeBitmask(mApnTypeBitmask) - .setProtocol(protocol) - .setRoamingProtocol(roamingProtocol) - .setCarrierEnabled(carrierEnabled) - .setNetworkTypeBitmask(networkTypeBitmask) - .setProfileId(profileId) - .setModemCognitive(modemCognitive) - .setMaxConns(maxConns) - .setWaitTime(waitTime) - .setMaxConnsTime(maxConnsTime) - .setMtu(mtu) - .setMvnoType(mvnoType) - .setMvnoMatchData(mvnoMatchData) - .setApnSetId(apnSetId) - .setCarrierId(carrierId) - .setSkip464Xlat(skip464xlat) - .buildWithoutCheck(); - } - - /** - * @hide - */ - public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName, - String apnName, String proxyAddress, int proxyPort, Uri mmsc, - String mmsProxyAddress, int mmsProxyPort, String user, String password, - int authType, int mApnTypeBitmask, int protocol, int roamingProtocol, - boolean carrierEnabled, int networkTypeBitmask, int profileId, boolean modemCognitive, - int maxConns, int waitTime, int maxConnsTime, int mtu, int mvnoType, - String mvnoMatchData) { - return makeApnSetting(id, operatorNumeric, entryName, apnName, proxyAddress, proxyPort, - mmsc, mmsProxyAddress, mmsProxyPort, user, password, authType, mApnTypeBitmask, - protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, profileId, - modemCognitive, maxConns, waitTime, maxConnsTime, mtu, mvnoType, mvnoMatchData, - Carriers.NO_APN_SET_ID, TelephonyManager.UNKNOWN_CARRIER_ID, - Carriers.SKIP_464XLAT_DEFAULT); - } - - /** - * @hide - */ public static ApnSetting makeApnSetting(Cursor cursor) { final int apnTypesBitmask = getApnTypesBitmaskFromString( cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE))); @@ -975,272 +939,99 @@ public class ApnSetting implements Parcelable { ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask); } - return makeApnSetting( - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN)), - cursor.getString( - cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY)), - portFromString(cursor.getString( - cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))), - UriFromString(cursor.getString( - cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))), - cursor.getString( - cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY)), - portFromString(cursor.getString( - cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT))), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)), - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)), - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)), - apnTypesBitmask, - getProtocolIntFromString( - cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL))), - getProtocolIntFromString( - cursor.getString(cursor.getColumnIndexOrThrow( - Telephony.Carriers.ROAMING_PROTOCOL))), - cursor.getInt(cursor.getColumnIndexOrThrow( - Telephony.Carriers.CARRIER_ENABLED)) == 1, - networkTypeBitmask, - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)), - cursor.getInt(cursor.getColumnIndexOrThrow( - Telephony.Carriers.MODEM_PERSIST)) == 1, - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNECTIONS)), - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME_RETRY)), - cursor.getInt(cursor.getColumnIndexOrThrow( - Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS)), - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)), - getMvnoTypeIntFromString( - cursor.getString(cursor.getColumnIndexOrThrow( - Telephony.Carriers.MVNO_TYPE))), - cursor.getString(cursor.getColumnIndexOrThrow( - Telephony.Carriers.MVNO_MATCH_DATA)), - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN_SET_ID)), - cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.CARRIER_ID)), - cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.SKIP_464XLAT))); + return new Builder() + .setId(cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID))) + .setOperatorNumeric(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.NUMERIC))) + .setEntryName(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.NAME))) + .setApnName(cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.APN))) + .setProxyAddress(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))) + .setProxyPort(portFromString(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT)))) + .setMmsc(UriFromString(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC)))) + .setMmsProxyAddress(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))) + .setMmsProxyPort(portFromString(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPORT)))) + .setUser(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.USER))) + .setPassword(cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD))) + .setAuthType(cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE))) + .setApnTypeBitmask(apnTypesBitmask) + .setProtocol(getProtocolIntFromString( + cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))) + .setRoamingProtocol(getProtocolIntFromString( + cursor.getString(cursor.getColumnIndexOrThrow( + Telephony.Carriers.ROAMING_PROTOCOL)))) + .setCarrierEnabled(cursor.getInt(cursor.getColumnIndexOrThrow( + Telephony.Carriers.CARRIER_ENABLED)) == 1) + .setNetworkTypeBitmask(networkTypeBitmask) + .setProfileId(cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID))) + .setModemCognitive(cursor.getInt(cursor.getColumnIndexOrThrow( + Telephony.Carriers.MODEM_PERSIST)) == 1) + .setMaxConns(cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.Carriers.MAX_CONNECTIONS))) + .setWaitTime(cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME_RETRY))) + .setMaxConnsTime(cursor.getInt(cursor.getColumnIndexOrThrow( + Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS))) + .setMtuV4(cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU))) + .setMtuV6(UNSET_MTU) // TODO: Add corresponding support in telephony provider + .setMvnoType(getMvnoTypeIntFromString( + cursor.getString(cursor.getColumnIndexOrThrow( + Telephony.Carriers.MVNO_TYPE)))) + .setMvnoMatchData(cursor.getString(cursor.getColumnIndexOrThrow( + Telephony.Carriers.MVNO_MATCH_DATA))) + .setApnSetId(cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.Carriers.APN_SET_ID))) + .setCarrierId(cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.Carriers.CARRIER_ID))) + .setSkip464Xlat(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.SKIP_464XLAT))) + .buildWithoutCheck(); } /** * @hide */ public static ApnSetting makeApnSetting(ApnSetting apn) { - return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName, - apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, - apn.mMmsProxyPort, apn.mUser, apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, - apn.mProtocol, apn.mRoamingProtocol, apn.mCarrierEnabled, apn.mNetworkTypeBitmask, - apn.mProfileId, apn.mPersistent, apn.mMaxConns, apn.mWaitTime, - apn.mMaxConnsTime, apn.mMtu, apn.mMvnoType, apn.mMvnoMatchData, apn.mApnSetId, - apn.mCarrierId, apn.mSkip464Xlat); - } - - /** - * Creates an ApnSetting object from a string. - * - * @param data the string to read. - * - * The string must be in one of two formats (newlines added for clarity, - * spaces are optional): - * - * v1 format: - * <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>, - * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>, - * <type>[| <type>...], - * - * v2 format: - * [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>, - * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>, - * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>, - * - * v3 format: - * [ApnSettingV3] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>, - * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>, - * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>, - * <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>, - * <mvnoType>, <mvnoMatchData> - * - * v4 format: - * [ApnSettingV4] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>, - * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>, - * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>, - * <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>, - * <mvnoType>, <mvnoMatchData>, <networkTypeBitmask> - * - * v5 format: - * [ApnSettingV5] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>, - * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>, - * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>, - * <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>, - * <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>, <apnSetId> - * - * v6 format: - * [ApnSettingV6] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>, - * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>, - * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>, - * <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>, - * <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>, <apnSetId>, <carrierId> - * - * v7 format: - * [ApnSettingV7] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>, - * <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>, - * <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>, - * <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>, - * <mvnoType>, <mvnoMatchData>, <networkTypeBitmask>, <apnSetId>, <carrierId>, <skip464xlat> - * - * Note that the strings generated by {@link #toString()} do not contain the username - * and password and thus cannot be read by this method. - * - * This method may return {@code null} if the input string is invalid. - * - * @hide - */ - public static ApnSetting fromString(String data) { - if (data == null) return null; - - int version; - // matches() operates on the whole string, so append .* to the regex. - if (data.matches(V7_FORMAT_REGEX + ".*")) { - version = 7; - data = data.replaceFirst(V7_FORMAT_REGEX, ""); - } else if (data.matches(V6_FORMAT_REGEX + ".*")) { - version = 6; - data = data.replaceFirst(V6_FORMAT_REGEX, ""); - } else if (data.matches(V5_FORMAT_REGEX + ".*")) { - version = 5; - data = data.replaceFirst(V5_FORMAT_REGEX, ""); - } else if (data.matches(V4_FORMAT_REGEX + ".*")) { - version = 4; - data = data.replaceFirst(V4_FORMAT_REGEX, ""); - } else if (data.matches(V3_FORMAT_REGEX + ".*")) { - version = 3; - data = data.replaceFirst(V3_FORMAT_REGEX, ""); - } else if (data.matches(V2_FORMAT_REGEX + ".*")) { - version = 2; - data = data.replaceFirst(V2_FORMAT_REGEX, ""); - } else { - version = 1; - } - - String[] a = data.split("\\s*,\\s*", -1); - if (a.length < 14) { - return null; - } - - int authType; - try { - authType = Integer.parseInt(a[12]); - } catch (NumberFormatException e) { - authType = 0; - } - - String[] typeArray; - String protocol, roamingProtocol; - boolean carrierEnabled; - int bearerBitmask = 0; - int networkTypeBitmask = 0; - int profileId = 0; - boolean modemCognitive = false; - int maxConns = 0; - int waitTime = 0; - int maxConnsTime = 0; - int mtu = UNSET_MTU; - String mvnoType = ""; - String mvnoMatchData = ""; - int apnSetId = Carriers.NO_APN_SET_ID; - int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID; - int skip464xlat = Carriers.SKIP_464XLAT_DEFAULT; - if (version == 1) { - typeArray = new String[a.length - 13]; - System.arraycopy(a, 13, typeArray, 0, a.length - 13); - protocol = PROTOCOL_INT_MAP.get(PROTOCOL_IP); - roamingProtocol = PROTOCOL_INT_MAP.get(PROTOCOL_IP); - carrierEnabled = true; - } else { - if (a.length < 18) { - return null; - } - typeArray = a[13].split("\\s*\\|\\s*"); - protocol = a[14]; - roamingProtocol = a[15]; - carrierEnabled = Boolean.parseBoolean(a[16]); - - bearerBitmask = ServiceState.getBitmaskFromString(a[17]); - - if (a.length > 22) { - modemCognitive = Boolean.parseBoolean(a[19]); - try { - profileId = Integer.parseInt(a[18]); - maxConns = Integer.parseInt(a[20]); - waitTime = Integer.parseInt(a[21]); - maxConnsTime = Integer.parseInt(a[22]); - } catch (NumberFormatException e) { - } - } - if (a.length > 23) { - try { - mtu = Integer.parseInt(a[23]); - } catch (NumberFormatException e) { - } - } - if (a.length > 25) { - mvnoType = a[24]; - mvnoMatchData = a[25]; - } - if (a.length > 26) { - networkTypeBitmask = ServiceState.getBitmaskFromString(a[26]); - } - if (a.length > 27) { - apnSetId = Integer.parseInt(a[27]); - } - if (a.length > 28) { - carrierId = Integer.parseInt(a[28]); - } - if (a.length > 29) { - try { - skip464xlat = Integer.parseInt(a[29]); - } catch (NumberFormatException e) { - } - } - } - - // If both bearerBitmask and networkTypeBitmask were specified, bearerBitmask would be - // ignored. - if (networkTypeBitmask == 0) { - networkTypeBitmask = - ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask); - } - return makeApnSetting(-1, a[10] + a[11], a[0], a[1], a[2], - portFromString(a[3]), UriFromString(a[7]), a[8], - portFromString(a[9]), a[4], a[5], authType, - getApnTypesBitmaskFromString(TextUtils.join(",", typeArray)), - getProtocolIntFromString(protocol), getProtocolIntFromString(roamingProtocol), - carrierEnabled, networkTypeBitmask, profileId, modemCognitive, maxConns, waitTime, - maxConnsTime, mtu, getMvnoTypeIntFromString(mvnoType), mvnoMatchData, apnSetId, - carrierId, skip464xlat); - } - - /** - * Creates an array of ApnSetting objects from a string. - * - * @param data the string to read. - * - * Builds on top of the same format used by fromString, but allows for multiple entries - * separated by ";". - * - * @hide - */ - public static List<ApnSetting> arrayFromString(String data) { - List<ApnSetting> retVal = new ArrayList<ApnSetting>(); - if (TextUtils.isEmpty(data)) { - return retVal; - } - String[] apnStrings = data.split("\\s*;\\s*"); - for (String apnString : apnStrings) { - ApnSetting apn = fromString(apnString); - if (apn != null) { - retVal.add(apn); - } - } - return retVal; + return new Builder() + .setId(apn.mId) + .setOperatorNumeric(apn.mOperatorNumeric) + .setEntryName(apn.mEntryName) + .setApnName(apn.mApnName) + .setProxyAddress(apn.mProxyAddress) + .setProxyPort(apn.mProxyPort) + .setMmsc(apn.mMmsc) + .setMmsProxyAddress(apn.mMmsProxyAddress) + .setMmsProxyPort(apn.mMmsProxyPort) + .setUser(apn.mUser) + .setPassword(apn.mPassword) + .setAuthType(apn.mAuthType) + .setApnTypeBitmask(apn.mApnTypeBitmask) + .setProtocol(apn.mProtocol) + .setRoamingProtocol(apn.mRoamingProtocol) + .setCarrierEnabled(apn.mCarrierEnabled) + .setNetworkTypeBitmask(apn.mNetworkTypeBitmask) + .setProfileId(apn.mProfileId) + .setModemCognitive(apn.mPersistent) + .setMaxConns(apn.mMaxConns) + .setWaitTime(apn.mWaitTime) + .setMaxConnsTime(apn.mMaxConnsTime) + .setMtuV4(apn.mMtuV4) + .setMtuV6(apn.mMtuV6) + .setMvnoType(apn.mMvnoType) + .setMvnoMatchData(apn.mMvnoMatchData) + .setApnSetId(apn.mApnSetId) + .setCarrierId(apn.mCarrierId) + .setSkip464Xlat(apn.mSkip464Xlat) + .buildWithoutCheck(); } /** @@ -1251,7 +1042,7 @@ public class ApnSetting implements Parcelable { */ public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("[ApnSettingV7] ") + sb.append("[ApnSetting] ") .append(mEntryName) .append(", ").append(mId) .append(", ").append(mOperatorNumeric) @@ -1272,7 +1063,8 @@ public class ApnSetting implements Parcelable { sb.append(", ").append(mMaxConns); sb.append(", ").append(mWaitTime); sb.append(", ").append(mMaxConnsTime); - sb.append(", ").append(mMtu); + sb.append(", ").append(mMtuV4); + sb.append(", ").append(mMtuV6); sb.append(", ").append(MVNO_TYPE_INT_MAP.get(mMvnoType)); sb.append(", ").append(mMvnoMatchData); sb.append(", ").append(mPermanentFailed); @@ -1343,9 +1135,9 @@ public class ApnSetting implements Parcelable { public int hashCode() { return Objects.hash(mApnName, mProxyAddress, mProxyPort, mMmsc, mMmsProxyAddress, mMmsProxyPort, mUser, mPassword, mAuthType, mApnTypeBitmask, mId, mOperatorNumeric, - mProtocol, mRoamingProtocol, mMtu, mCarrierEnabled, mNetworkTypeBitmask, mProfileId, - mPersistent, mMaxConns, mWaitTime, mMaxConnsTime, mMvnoType, mMvnoMatchData, - mApnSetId, mCarrierId, mSkip464Xlat); + mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask, + mProfileId, mPersistent, mMaxConns, mWaitTime, mMaxConnsTime, mMvnoType, + mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat); } @Override @@ -1357,33 +1149,34 @@ public class ApnSetting implements Parcelable { ApnSetting other = (ApnSetting) o; return mEntryName.equals(other.mEntryName) - && Objects.equals(mId, other.mId) - && Objects.equals(mOperatorNumeric, other.mOperatorNumeric) - && Objects.equals(mApnName, other.mApnName) - && Objects.equals(mProxyAddress, other.mProxyAddress) - && Objects.equals(mMmsc, other.mMmsc) - && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress) - && Objects.equals(mMmsProxyPort, other.mMmsProxyPort) - && Objects.equals(mProxyPort, other.mProxyPort) - && Objects.equals(mUser, other.mUser) - && Objects.equals(mPassword, other.mPassword) - && Objects.equals(mAuthType, other.mAuthType) - && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask) - && Objects.equals(mProtocol, other.mProtocol) - && Objects.equals(mRoamingProtocol, other.mRoamingProtocol) - && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) - && Objects.equals(mProfileId, other.mProfileId) - && Objects.equals(mPersistent, other.mPersistent) - && Objects.equals(mMaxConns, other.mMaxConns) - && Objects.equals(mWaitTime, other.mWaitTime) - && Objects.equals(mMaxConnsTime, other.mMaxConnsTime) - && Objects.equals(mMtu, other.mMtu) - && Objects.equals(mMvnoType, other.mMvnoType) - && Objects.equals(mMvnoMatchData, other.mMvnoMatchData) - && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask) - && Objects.equals(mApnSetId, other.mApnSetId) - && Objects.equals(mCarrierId, other.mCarrierId) - && Objects.equals(mSkip464Xlat, other.mSkip464Xlat); + && Objects.equals(mId, other.mId) + && Objects.equals(mOperatorNumeric, other.mOperatorNumeric) + && Objects.equals(mApnName, other.mApnName) + && Objects.equals(mProxyAddress, other.mProxyAddress) + && Objects.equals(mMmsc, other.mMmsc) + && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress) + && Objects.equals(mMmsProxyPort, other.mMmsProxyPort) + && Objects.equals(mProxyPort, other.mProxyPort) + && Objects.equals(mUser, other.mUser) + && Objects.equals(mPassword, other.mPassword) + && Objects.equals(mAuthType, other.mAuthType) + && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask) + && Objects.equals(mProtocol, other.mProtocol) + && Objects.equals(mRoamingProtocol, other.mRoamingProtocol) + && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) + && Objects.equals(mProfileId, other.mProfileId) + && Objects.equals(mPersistent, other.mPersistent) + && Objects.equals(mMaxConns, other.mMaxConns) + && Objects.equals(mWaitTime, other.mWaitTime) + && Objects.equals(mMaxConnsTime, other.mMaxConnsTime) + && Objects.equals(mMtuV4, other.mMtuV4) + && Objects.equals(mMtuV6, other.mMtuV6) + && Objects.equals(mMvnoType, other.mMvnoType) + && Objects.equals(mMvnoMatchData, other.mMvnoMatchData) + && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask) + && Objects.equals(mApnSetId, other.mApnSetId) + && Objects.equals(mCarrierId, other.mCarrierId) + && Objects.equals(mSkip464Xlat, other.mSkip464Xlat); } /** @@ -1406,31 +1199,32 @@ public class ApnSetting implements Parcelable { ApnSetting other = (ApnSetting) o; return mEntryName.equals(other.mEntryName) - && Objects.equals(mOperatorNumeric, other.mOperatorNumeric) - && Objects.equals(mApnName, other.mApnName) - && Objects.equals(mProxyAddress, other.mProxyAddress) - && Objects.equals(mMmsc, other.mMmsc) - && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress) - && Objects.equals(mMmsProxyPort, other.mMmsProxyPort) - && Objects.equals(mProxyPort, other.mProxyPort) - && Objects.equals(mUser, other.mUser) - && Objects.equals(mPassword, other.mPassword) - && Objects.equals(mAuthType, other.mAuthType) - && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask) - && (isDataRoaming || Objects.equals(mProtocol, other.mProtocol)) - && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol)) - && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) - && Objects.equals(mProfileId, other.mProfileId) - && Objects.equals(mPersistent, other.mPersistent) - && Objects.equals(mMaxConns, other.mMaxConns) - && Objects.equals(mWaitTime, other.mWaitTime) - && Objects.equals(mMaxConnsTime, other.mMaxConnsTime) - && Objects.equals(mMtu, other.mMtu) - && Objects.equals(mMvnoType, other.mMvnoType) - && Objects.equals(mMvnoMatchData, other.mMvnoMatchData) - && Objects.equals(mApnSetId, other.mApnSetId) - && Objects.equals(mCarrierId, other.mCarrierId) - && Objects.equals(mSkip464Xlat, other.mSkip464Xlat); + && Objects.equals(mOperatorNumeric, other.mOperatorNumeric) + && Objects.equals(mApnName, other.mApnName) + && Objects.equals(mProxyAddress, other.mProxyAddress) + && Objects.equals(mMmsc, other.mMmsc) + && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress) + && Objects.equals(mMmsProxyPort, other.mMmsProxyPort) + && Objects.equals(mProxyPort, other.mProxyPort) + && Objects.equals(mUser, other.mUser) + && Objects.equals(mPassword, other.mPassword) + && Objects.equals(mAuthType, other.mAuthType) + && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask) + && (isDataRoaming || Objects.equals(mProtocol, other.mProtocol)) + && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol)) + && Objects.equals(mCarrierEnabled, other.mCarrierEnabled) + && Objects.equals(mProfileId, other.mProfileId) + && Objects.equals(mPersistent, other.mPersistent) + && Objects.equals(mMaxConns, other.mMaxConns) + && Objects.equals(mWaitTime, other.mWaitTime) + && Objects.equals(mMaxConnsTime, other.mMaxConnsTime) + && Objects.equals(mMtuV4, other.mMtuV4) + && Objects.equals(mMtuV6, other.mMtuV6) + && Objects.equals(mMvnoType, other.mMvnoType) + && Objects.equals(mMvnoMatchData, other.mMvnoMatchData) + && Objects.equals(mApnSetId, other.mApnSetId) + && Objects.equals(mCarrierId, other.mCarrierId) + && Objects.equals(mSkip464Xlat, other.mSkip464Xlat); } /** @@ -1732,7 +1526,7 @@ public class ApnSetting implements Parcelable { dest.writeString(mApnName); dest.writeString(mProxyAddress); dest.writeInt(mProxyPort); - dest.writeValue(mMmsc); + dest.writeParcelable(mMmsc, flags); dest.writeString(mMmsProxyAddress); dest.writeInt(mMmsProxyPort); dest.writeString(mUser); @@ -1742,40 +1536,53 @@ public class ApnSetting implements Parcelable { dest.writeInt(mProtocol); dest.writeInt(mRoamingProtocol); dest.writeBoolean(mCarrierEnabled); - dest.writeInt(mMvnoType); dest.writeInt(mNetworkTypeBitmask); + dest.writeInt(mProfileId); + dest.writeBoolean(mPersistent); + dest.writeInt(mMaxConns); + dest.writeInt(mWaitTime); + dest.writeInt(mMaxConnsTime); + dest.writeInt(mMtuV4); + dest.writeInt(mMtuV6); + dest.writeInt(mMvnoType); + dest.writeString(mMvnoMatchData); dest.writeInt(mApnSetId); dest.writeInt(mCarrierId); dest.writeInt(mSkip464Xlat); } private static ApnSetting readFromParcel(Parcel in) { - final int id = in.readInt(); - final String operatorNumeric = in.readString(); - final String entryName = in.readString(); - final String apnName = in.readString(); - final String proxy = in.readString(); - final int port = in.readInt(); - final Uri mmsc = (Uri) in.readValue(Uri.class.getClassLoader()); - final String mmsProxy = in.readString(); - final int mmsPort = in.readInt(); - final String user = in.readString(); - final String password = in.readString(); - final int authType = in.readInt(); - final int apnTypesBitmask = in.readInt(); - final int protocol = in.readInt(); - final int roamingProtocol = in.readInt(); - final boolean carrierEnabled = in.readBoolean(); - final int mvnoType = in.readInt(); - final int networkTypeBitmask = in.readInt(); - final int apnSetId = in.readInt(); - final int carrierId = in.readInt(); - final int skip464xlat = in.readInt(); - - return makeApnSetting(id, operatorNumeric, entryName, apnName, - proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, apnTypesBitmask, - protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false, - 0, 0, 0, 0, mvnoType, null, apnSetId, carrierId, skip464xlat); + return new Builder() + .setId(in.readInt()) + .setOperatorNumeric(in.readString()) + .setEntryName(in.readString()) + .setApnName(in.readString()) + .setProxyAddress(in.readString()) + .setProxyPort(in.readInt()) + .setMmsc(in.readParcelable(Uri.class.getClassLoader())) + .setMmsProxyAddress(in.readString()) + .setMmsProxyPort(in.readInt()) + .setUser(in.readString()) + .setPassword(in.readString()) + .setAuthType(in.readInt()) + .setApnTypeBitmask(in.readInt()) + .setProtocol(in.readInt()) + .setRoamingProtocol(in.readInt()) + .setCarrierEnabled(in.readBoolean()) + .setNetworkTypeBitmask(in.readInt()) + .setProfileId(in.readInt()) + .setModemCognitive(in.readBoolean()) + .setMaxConns(in.readInt()) + .setWaitTime(in.readInt()) + .setMaxConnsTime(in.readInt()) + .setMtuV4(in.readInt()) + .setMtuV6(in.readInt()) + .setMvnoType(in.readInt()) + .setMvnoMatchData(in.readString()) + .setApnSetId(in.readInt()) + .setCarrierId(in.readInt()) + .setSkip464Xlat(in.readInt()) + .buildWithoutCheck(); } public static final @android.annotation.NonNull Parcelable.Creator<ApnSetting> CREATOR = @@ -1834,13 +1641,14 @@ public class ApnSetting implements Parcelable { private int mMmsProxyPort = UNSPECIFIED_INT; private String mUser; private String mPassword; - private int mAuthType; + private int mAuthType = AUTH_TYPE_UNKNOWN; private int mApnTypeBitmask; private int mId; private String mOperatorNumeric; private int mProtocol = UNSPECIFIED_INT; private int mRoamingProtocol = UNSPECIFIED_INT; - private int mMtu; + private int mMtuV4; + private int mMtuV6; private int mNetworkTypeBitmask; private boolean mCarrierEnabled; private int mProfileId; @@ -1863,20 +1671,34 @@ public class ApnSetting implements Parcelable { * Sets the unique database id for this entry. * * @param id the unique database id to set for this entry + * @hide */ - private Builder setId(int id) { + public Builder setId(int id) { this.mId = id; return this; } /** - * Set the MTU size of the mobile interface to which the APN connected. + * Set the MTU size of the IPv4 mobile interface to which the APN connected. Note this value + * is used only if MTU size is not provided in {@link DataCallResponse}. + * + * @param mtuV4 the MTU size to set for the APN + * @hide + */ + public Builder setMtuV4(int mtuV4) { + this.mMtuV4 = mtuV4; + return this; + } + + /** + * Set the MTU size of the IPv6 mobile interface to which the APN connected. Note this value + * is used only if MTU size is not provided in {@link DataCallResponse}. * - * @param mtu the MTU size to set for the APN + * @param mtuV6 the MTU size to set for the APN * @hide */ - public Builder setMtu(int mtu) { - this.mMtu = mtu; + public Builder setMtuV6(int mtuV6) { + this.mMtuV6 = mtuV6; return this; } @@ -2237,7 +2059,8 @@ public class ApnSetting implements Parcelable { | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX | TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE)) == 0 || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) { - return null; + throw new IllegalArgumentException("mApName=" + mApnName + ", mEntryName=" + + mEntryName + ", mApnTypeBitmask=" + mApnTypeBitmask); } return new ApnSetting(this); } diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java index f2a124901bc4..93903d2658cd 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -25,13 +25,11 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.Annotation.ApnType; +import android.telephony.TelephonyManager; import android.telephony.TelephonyManager.NetworkTypeBitMask; import android.telephony.data.ApnSetting.AuthType; import android.text.TextUtils; -import com.android.internal.telephony.RILConstants; -import com.android.internal.telephony.util.TelephonyUtils; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -62,152 +60,141 @@ public final class DataProfile implements Parcelable { /** 3GPP2 type data profile */ public static final int TYPE_3GPP2 = 2; - private final int mProfileId; - - private final String mApn; - - @ProtocolType - private final int mProtocolType; - - @AuthType - private final int mAuthType; - - private final String mUserName; - - private final String mPassword; - - @Type - private final int mType; - - private final int mMaxConnectionsTime; - - private final int mMaxConnections; - - private final int mWaitTime; - - private final boolean mEnabled; - - @ApnType - private final int mSupportedApnTypesBitmask; - - @ProtocolType - private final int mRoamingProtocolType; - - @NetworkTypeBitMask - private final int mBearerBitmask; + private final @Type int mType; - private final int mMtuV4; + private final @Nullable ApnSetting mApnSetting; - private final int mMtuV6; - - private final boolean mPersistent; + private final @Nullable TrafficDescriptor mTrafficDescriptor; private final boolean mPreferred; - /** @hide */ - private DataProfile(int profileId, String apn, @ProtocolType int protocolType, int authType, - String userName, String password, int type, int maxConnectionsTime, - int maxConnections, int waitTime, boolean enabled, - @ApnType int supportedApnTypesBitmask, @ProtocolType int roamingProtocolType, - @NetworkTypeBitMask int bearerBitmask, int mtuV4, int mtuV6, boolean persistent, - boolean preferred) { - this.mProfileId = profileId; - this.mApn = apn; - this.mProtocolType = protocolType; - if (authType == -1) { - authType = TextUtils.isEmpty(userName) ? RILConstants.SETUP_DATA_AUTH_NONE - : RILConstants.SETUP_DATA_AUTH_PAP_CHAP; - } - this.mAuthType = authType; - this.mUserName = userName; - this.mPassword = password; - this.mType = type; - this.mMaxConnectionsTime = maxConnectionsTime; - this.mMaxConnections = maxConnections; - this.mWaitTime = waitTime; - this.mEnabled = enabled; - this.mSupportedApnTypesBitmask = supportedApnTypesBitmask; - this.mRoamingProtocolType = roamingProtocolType; - this.mBearerBitmask = bearerBitmask; - this.mMtuV4 = mtuV4; - this.mMtuV6 = mtuV6; - this.mPersistent = persistent; - this.mPreferred = preferred; + private DataProfile(@NonNull Builder builder) { + mApnSetting = builder.mApnSetting; + mTrafficDescriptor = builder.mTrafficDescriptor; + mPreferred = builder.mPreferred; + + if (builder.mType != -1) { + mType = builder.mType; + } else if (mApnSetting != null) { + int networkTypes = mApnSetting.getNetworkTypeBitmask(); + + if (networkTypes == 0) { + mType = DataProfile.TYPE_COMMON; + } else if ((networkTypes & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2) + == networkTypes) { + mType = DataProfile.TYPE_3GPP2; + } else if ((networkTypes & TelephonyManager.NETWORK_STANDARDS_FAMILY_BITMASK_3GPP) + == networkTypes) { + mType = DataProfile.TYPE_3GPP; + } else { + mType = DataProfile.TYPE_COMMON; + } + } else { + mType = DataProfile.TYPE_COMMON; + } } private DataProfile(Parcel source) { - mProfileId = source.readInt(); - mApn = source.readString(); - mProtocolType = source.readInt(); - mAuthType = source.readInt(); - mUserName = source.readString(); - mPassword = source.readString(); mType = source.readInt(); - mMaxConnectionsTime = source.readInt(); - mMaxConnections = source.readInt(); - mWaitTime = source.readInt(); - mEnabled = source.readBoolean(); - mSupportedApnTypesBitmask = source.readInt(); - mRoamingProtocolType = source.readInt(); - mBearerBitmask = source.readInt(); - mMtuV4 = source.readInt(); - mMtuV6 = source.readInt(); - mPersistent = source.readBoolean(); + mApnSetting = source.readParcelable(ApnSetting.class.getClassLoader()); + mTrafficDescriptor = source.readParcelable(TrafficDescriptor.class.getClassLoader()); mPreferred = source.readBoolean(); } /** * @return Id of the data profile. */ - public int getProfileId() { return mProfileId; } + public int getProfileId() { + if (mApnSetting != null) { + return mApnSetting.getProfileId(); + } + return 0; + } /** * @return The APN (Access Point Name) to establish data connection. This is a string * specifically defined by the carrier. */ @NonNull - public String getApn() { return mApn; } + public String getApn() { + if (mApnSetting != null) { + return TextUtils.emptyIfNull(mApnSetting.getApnName()); + } + return ""; + } /** * @return The connection protocol defined in 3GPP TS 27.007 section 10.1.1. */ - public @ProtocolType int getProtocolType() { return mProtocolType; } + public @ProtocolType int getProtocolType() { + if (mApnSetting != null) { + return mApnSetting.getProtocol(); + } + return ApnSetting.PROTOCOL_IP; + } /** * @return The authentication protocol used for this PDP context. */ - public @AuthType int getAuthType() { return mAuthType; } + public @AuthType int getAuthType() { + if (mApnSetting != null) { + return mApnSetting.getAuthType(); + } + return ApnSetting.AUTH_TYPE_NONE; + } /** * @return The username for APN. Can be null. */ @Nullable - public String getUserName() { return mUserName; } + public String getUserName() { + if (mApnSetting != null) { + return mApnSetting.getUser(); + } + return null; + } /** * @return The password for APN. Can be null. */ @Nullable - public String getPassword() { return mPassword; } + public String getPassword() { + if (mApnSetting != null) { + return mApnSetting.getPassword(); + } + return null; + } /** * @return The profile type. */ - public @Type int getType() { return mType; } + public @Type int getType() { + return mType; + } /** * @return The period in seconds to limit the maximum connections. * * @hide */ - public int getMaxConnectionsTime() { return mMaxConnectionsTime; } + public int getMaxConnectionsTime() { + if (mApnSetting != null) { + return mApnSetting.getMaxConnsTime(); + } + return 0; + } /** * @return The maximum connections allowed. * * @hide */ - public int getMaxConnections() { return mMaxConnections; } + public int getMaxConnections() { + if (mApnSetting != null) { + return mApnSetting.getMaxConns(); + } + return 0; + } /** * @return The required wait time in seconds after a successful UE initiated disconnect of a @@ -216,57 +203,117 @@ public final class DataProfile implements Parcelable { * * @hide */ - public int getWaitTime() { return mWaitTime; } + public int getWaitTime() { + if (mApnSetting != null) { + return mApnSetting.getWaitTime(); + } + return 0; + } /** * @return True if the profile is enabled. */ - public boolean isEnabled() { return mEnabled; } + public boolean isEnabled() { + if (mApnSetting != null) { + return mApnSetting.isEnabled(); + } + return false; + } /** * @return The supported APN types bitmask. */ - public @ApnType int getSupportedApnTypesBitmask() { return mSupportedApnTypesBitmask; } + public @ApnType int getSupportedApnTypesBitmask() { + if (mApnSetting != null) { + return mApnSetting.getApnTypeBitmask(); + } + return ApnSetting.TYPE_NONE; + } /** * @return The connection protocol on roaming network defined in 3GPP TS 27.007 section 10.1.1. */ - public @ProtocolType int getRoamingProtocolType() { return mRoamingProtocolType; } + public @ProtocolType int getRoamingProtocolType() { + if (mApnSetting != null) { + return mApnSetting.getRoamingProtocol(); + } + return ApnSetting.PROTOCOL_IP; + } /** * @return The bearer bitmask indicating the applicable networks for this data profile. */ - public @NetworkTypeBitMask int getBearerBitmask() { return mBearerBitmask; } + public @NetworkTypeBitMask int getBearerBitmask() { + if (mApnSetting != null) { + return mApnSetting.getNetworkTypeBitmask(); + } + return (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN; + } /** * @return The maximum transmission unit (MTU) size in bytes. * @deprecated use {@link #getMtuV4} or {@link #getMtuV6} instead. */ @Deprecated - public int getMtu() { return mMtuV4; } + public int getMtu() { + return getMtuV4(); + } /** * This replaces the deprecated method getMtu. * @return The maximum transmission unit (MTU) size in bytes, for IPv4. */ - public int getMtuV4() { return mMtuV4; } + public int getMtuV4() { + if (mApnSetting != null) { + return mApnSetting.getMtuV4(); + } + return 0; + } /** * @return The maximum transmission unit (MTU) size in bytes, for IPv6. */ - public int getMtuV6() { return mMtuV6; } + public int getMtuV6() { + if (mApnSetting != null) { + return mApnSetting.getMtuV6(); + } + return 0; + } /** * @return {@code true} if modem must persist this data profile. */ - public boolean isPersistent() { return mPersistent; } + public boolean isPersistent() { + if (mApnSetting != null) { + return mApnSetting.isPersistent(); + } + return false; + } /** * @return {@code true} if this data profile was used to bring up the last default * (i.e internet) data connection successfully, or the one chosen by the user in Settings' * APN editor. For one carrier there can be only one profiled preferred. */ - public boolean isPreferred() { return mPreferred; } + public boolean isPreferred() { + return mPreferred; + } + + /** + * @return The APN setting + * @hide TODO: Remove before T is released. + */ + public @Nullable ApnSetting getApnSetting() { + return mApnSetting; + } + + /** + * @return The traffic descriptor + * @hide TODO: Remove before T is released. + */ + public @Nullable TrafficDescriptor getTrafficDescriptor() { + return mTrafficDescriptor; + } @Override public int describeContents() { @@ -276,34 +323,15 @@ public final class DataProfile implements Parcelable { @NonNull @Override public String toString() { - return "DataProfile=" + mProfileId + "/" + mProtocolType + "/" + mAuthType - + "/" + (TelephonyUtils.IS_USER ? "***/***/***" : - (mApn + "/" + mUserName + "/" + mPassword)) + "/" + mType + "/" - + mMaxConnectionsTime + "/" + mMaxConnections + "/" - + mWaitTime + "/" + mEnabled + "/" + mSupportedApnTypesBitmask + "/" - + mRoamingProtocolType + "/" + mBearerBitmask + "/" + mMtuV4 + "/" + mMtuV6 + "/" - + mPersistent + "/" + mPreferred; + return "DataProfile=" + mApnSetting + ", " + mTrafficDescriptor + ", preferred=" + + mPreferred; } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mProfileId); - dest.writeString(mApn); - dest.writeInt(mProtocolType); - dest.writeInt(mAuthType); - dest.writeString(mUserName); - dest.writeString(mPassword); dest.writeInt(mType); - dest.writeInt(mMaxConnectionsTime); - dest.writeInt(mMaxConnections); - dest.writeInt(mWaitTime); - dest.writeBoolean(mEnabled); - dest.writeInt(mSupportedApnTypesBitmask); - dest.writeInt(mRoamingProtocolType); - dest.writeInt(mBearerBitmask); - dest.writeInt(mMtuV4); - dest.writeInt(mMtuV6); - dest.writeBoolean(mPersistent); + dest.writeParcelable(mApnSetting, flags); + dest.writeParcelable(mTrafficDescriptor, flags); dest.writeBoolean(mPreferred); } @@ -321,36 +349,18 @@ public final class DataProfile implements Parcelable { }; @Override - public boolean equals(@Nullable Object o) { + public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DataProfile that = (DataProfile) o; - return mProfileId == that.mProfileId - && mProtocolType == that.mProtocolType - && mAuthType == that.mAuthType - && mType == that.mType - && mMaxConnectionsTime == that.mMaxConnectionsTime - && mMaxConnections == that.mMaxConnections - && mWaitTime == that.mWaitTime - && mEnabled == that.mEnabled - && mSupportedApnTypesBitmask == that.mSupportedApnTypesBitmask - && mRoamingProtocolType == that.mRoamingProtocolType - && mBearerBitmask == that.mBearerBitmask - && mMtuV4 == that.mMtuV4 - && mMtuV6 == that.mMtuV6 - && mPersistent == that.mPersistent - && mPreferred == that.mPreferred - && Objects.equals(mApn, that.mApn) - && Objects.equals(mUserName, that.mUserName) - && Objects.equals(mPassword, that.mPassword); + return mType == that.mType + && Objects.equals(mApnSetting, that.mApnSetting) + && Objects.equals(mTrafficDescriptor, that.mTrafficDescriptor); } @Override public int hashCode() { - return Objects.hash(mProfileId, mApn, mProtocolType, mAuthType, mUserName, mPassword, mType, - mMaxConnectionsTime, mMaxConnections, mWaitTime, mEnabled, - mSupportedApnTypesBitmask, mRoamingProtocolType, mBearerBitmask, mMtuV4, mMtuV6, - mPersistent, mPreferred); + return Objects.hash(mType, mApnSetting, mTrafficDescriptor); } /** @@ -383,13 +393,7 @@ public final class DataProfile implements Parcelable { private String mPassword; @Type - private int mType; - - private int mMaxConnectionsTime; - - private int mMaxConnections; - - private int mWaitTime; + private int mType = -1; private boolean mEnabled; @@ -410,6 +414,10 @@ public final class DataProfile implements Parcelable { private boolean mPreferred; + private ApnSetting mApnSetting; + + private TrafficDescriptor mTrafficDescriptor; + /** * Default constructor for Builder. */ @@ -496,48 +504,6 @@ public final class DataProfile implements Parcelable { } /** - * Set the period in seconds to limit the maximum connections. - * - * @param maxConnectionsTime The profile type - * @return The same instance of the builder. - * - * @hide - */ - public @NonNull Builder setMaxConnectionsTime(int maxConnectionsTime) { - mMaxConnectionsTime = maxConnectionsTime; - return this; - } - - /** - * Set the maximum connections allowed. - * - * @param maxConnections The maximum connections allowed. - * @return The same instance of the builder. - * - * @hide - */ - public @NonNull Builder setMaxConnections(int maxConnections) { - mMaxConnections = maxConnections; - return this; - } - - /** - * Set the period in seconds to limit the maximum connections. - * - * @param waitTime The required wait time in seconds after a successful UE initiated - * disconnect of a given PDN connection before the device can send a new PDN connection - * request for that given PDN. - * - * @return The same instance of the builder. - * - * @hide - */ - public @NonNull Builder setWaitTime(int waitTime) { - mWaitTime = waitTime; - return this; - } - - /** * Enable the data profile * * @param isEnabled {@code true} to enable the data profile, otherwise disable. @@ -587,8 +553,9 @@ public final class DataProfile implements Parcelable { * * @param mtu The maximum transmission unit (MTU) size in bytes. * @return The same instance of the builder. - * @deprecated use {@link #setMtuV4} or {@link #setMtuV6} instead. + * @deprecated use {@link #setApnSetting(ApnSetting)} instead. */ + @Deprecated public @NonNull Builder setMtu(int mtu) { mMtuV4 = mMtuV6 = mtu; return this; @@ -631,7 +598,7 @@ public final class DataProfile implements Parcelable { } /** - * Set data profile as persistent/non-persistent + * Set data profile as persistent/non-persistent. * * @param isPersistent {@code true} if this data profile was used to bring up the last * default (i.e internet) data connection successfully. @@ -643,15 +610,63 @@ public final class DataProfile implements Parcelable { } /** + * Set APN setting. + * + * @param apnSetting APN setting + * @return The same instance of the builder + * + * @hide // TODO: Remove before T is released. + */ + public @NonNull Builder setApnSetting(@NonNull ApnSetting apnSetting) { + mApnSetting = apnSetting; + return this; + } + + /** + * Set traffic descriptor. + * + * @param trafficDescriptor Traffic descriptor + * @return The same instance of the builder + * + * @hide // TODO: Remove before T is released. + */ + public @NonNull Builder setTrafficDescriptor(@NonNull TrafficDescriptor trafficDescriptor) { + mTrafficDescriptor = trafficDescriptor; + return this; + } + + /** * Build the DataProfile object * * @return The data profile object */ public @NonNull DataProfile build() { - return new DataProfile(mProfileId, mApn, mProtocolType, mAuthType, mUserName, mPassword, - mType, mMaxConnectionsTime, mMaxConnections, mWaitTime, mEnabled, - mSupportedApnTypesBitmask, mRoamingProtocolType, mBearerBitmask, mMtuV4, mMtuV6, - mPersistent, mPreferred); + if (mApnSetting == null && mApn != null) { + // This is for backwards compatibility. + mApnSetting = new ApnSetting.Builder() + .setEntryName(mApn) + .setApnName(mApn) + .setApnTypeBitmask(mSupportedApnTypesBitmask) + .setAuthType(mAuthType) + .setCarrierEnabled(mEnabled) + .setModemCognitive(mPersistent) + .setMtuV4(mMtuV4) + .setMtuV6(mMtuV6) + .setNetworkTypeBitmask(mBearerBitmask) + .setProfileId(mProfileId) + .setPassword(mPassword) + .setProtocol(mProtocolType) + .setRoamingProtocol(mRoamingProtocolType) + .setUser(mUserName) + .build(); + } + + if (mApnSetting == null && mTrafficDescriptor == null) { + throw new IllegalArgumentException("APN setting and traffic descriptor can't be " + + "both null."); + } + + return new DataProfile(this); } } } diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 36082dc8180e..683bb92845ba 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.content.Context; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -45,6 +46,7 @@ import com.android.internal.telephony.ITelephony; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -214,6 +216,7 @@ public class ImsMmTelManager implements RegistrationManager { } } + private final Context mContext; private final int mSubId; private final BinderCacheManager<ITelephony> mBinderCache; @@ -255,6 +258,16 @@ public class ImsMmTelManager implements RegistrationManager { */ @VisibleForTesting public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) { + this(null, subId, binderCache); + } + + /** + * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead. + * @hide + */ + @VisibleForTesting + public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache) { + mContext = context; mSubId = subId; mBinderCache = binderCache; } @@ -1482,6 +1495,74 @@ public class ImsMmTelManager implements RegistrationManager { } } + /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_MMTEL, + callback.getCallbackBinder(), getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mBinderCache.removeRunnable(callback); + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } + + private String getOpPackageName() { + if (mContext != null) { + return mContext.getOpPackageName(); + } else { + return null; + } + } + private ITelephony getITelephony() { return mBinderCache.getBinder(); } diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 8d6fa4141b5c..1b047c77d80b 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -39,9 +39,11 @@ import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; import com.android.internal.telephony.IIntegerConsumer; +import com.android.internal.telephony.ITelephony; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -159,6 +161,7 @@ public class ImsRcsManager { private final int mSubId; private final Context mContext; private final BinderCacheManager<IImsRcsController> mBinderCache; + private final BinderCacheManager<ITelephony> mTelephonyBinderCache; private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter> mAvailabilityChangedCallbacks; @@ -167,11 +170,13 @@ public class ImsRcsManager { * @hide */ public ImsRcsManager(Context context, int subId, - BinderCacheManager<IImsRcsController> binderCache) { + BinderCacheManager<IImsRcsController> binderCache, + BinderCacheManager<ITelephony> telephonyBinderCache) { mSubId = subId; mContext = context; mBinderCache = binderCache; mAvailabilityChangedCallbacks = new HashMap<>(); + mTelephonyBinderCache = telephonyBinderCache; } /** @@ -534,6 +539,67 @@ public class ImsRcsManager { } /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_RCS, + callback.getCallbackBinder(), mContext.getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback); + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } + + /** * Add the {@link OnAvailabilityChangedListener} to collection for tracking. * @param executor The executor that will be used when the publish state is changed and the * {@link OnAvailabilityChangedListener} is called. diff --git a/telephony/java/android/telephony/ims/ImsStateCallback.java b/telephony/java/android/telephony/ims/ImsStateCallback.java new file mode 100644 index 000000000000..b9ba93ff0db6 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsStateCallback.java @@ -0,0 +1,184 @@ +/* + * 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.ims; + +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Binder; + +import com.android.internal.telephony.IImsStateCallback; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.concurrent.Executor; + +/** + * A callback class used for monitoring changes in IMS service connection states + * for a specific subscription. + * <p> + * @see ImsMmTelManager#registerImsStateCallback(Executor, ImsStateCallback) + * @see ImsRcsManager#registerImsStateCallback(Executor, ImsStateCallback) + */ +public abstract class ImsStateCallback { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "REASON_", value = { + REASON_UNKNOWN_TEMPORARY_ERROR, + REASON_UNKNOWN_PERMANENT_ERROR, + REASON_IMS_SERVICE_DISCONNECTED, + REASON_NO_IMS_SERVICE_CONFIGURED, + REASON_SUBSCRIPTION_INACTIVE, + REASON_IMS_SERVICE_NOT_READY + }) + public @interface DisconnectedReason {} + + /** + * The underlying IMS service is temporarily unavailable for the + * associated subscription. + * {@link #onAvailable} will be called when the IMS service becomes + * available again. + */ + public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; + + /** + * The underlying IMS service is permanently unavailable for the + * associated subscription and there will be no Manager available for + * this subscription. + */ + public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; + + /** + * The underlying IMS service has died, is reconfiguring, or has never + * come up yet and as a result is currently unavailable. + * {@link #onAvailable} will be called when the IMS service becomes + * available. All callbacks should be unregistered now and registered again + * if the IMS service moves back to available. + */ + public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; + + /** + * There is no IMS service configured for the subscription ID specified. + * This is a permanent error and there will be no Manager available for + * this subscription. + */ + public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; + + /** + * The subscription associated with this Manager has moved to an inactive + * state (e.g. SIM removed) and the IMS service has torn down the resources + * related to this subscription. This has caused this callback + * to be deregistered. The callback must be re-registered when this subscription + * becomes active in order to continue listening to the IMS service state. + */ + public static final int REASON_SUBSCRIPTION_INACTIVE = 5; + + /** + * The IMS service is connected, but in a NOT_READY state. Once the + * service moves to ready, {@link #onAvailable} will be called. + */ + public static final int REASON_IMS_SERVICE_NOT_READY = 6; + + private IImsStateCallbackStub mCallback; + + /** + * @hide + */ + public void init(@NonNull @CallbackExecutor Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("ImsStateCallback Executor must be non-null"); + } + mCallback = new IImsStateCallbackStub(this, executor); + } + + /** + * Using a static class and weak reference here to avoid memory leak caused by the + * IImsStateCallback.Stub callback retaining references to the outside ImsStateCallback. + */ + private static class IImsStateCallbackStub extends IImsStateCallback.Stub { + private WeakReference<ImsStateCallback> mImsStateCallbackWeakRef; + private Executor mExecutor; + + IImsStateCallbackStub(ImsStateCallback imsStateCallback, Executor executor) { + mImsStateCallbackWeakRef = new WeakReference<ImsStateCallback>(imsStateCallback); + mExecutor = executor; + } + + Executor getExecutor() { + return mExecutor; + } + + public void onAvailable() { + ImsStateCallback callback = mImsStateCallbackWeakRef.get(); + if (callback == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> callback.onAvailable())); + } + + public void onUnavailable(int reason) { + ImsStateCallback callback = mImsStateCallbackWeakRef.get(); + if (callback == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> callback.onUnavailable(reason))); + } + } + + /** + * The IMS service has disconnected or is reporting NOT_READY and is no longer + * available to users. The user should clean up all related state and + * unregister callbacks. If it is a temporary error, {@link #onAvailable} will + * be called when the IMS service becomes available again. + * + * @param reason the specified reason + */ + public abstract void onUnavailable(@DisconnectedReason int reason); + + /** + * The IMS service is connected and is ready for communication over the + * provided Manager. + */ + public abstract void onAvailable(); + + /** + * An unexpected error has occurred and the Telephony process has crashed. This + * has caused this callback to be deregistered. The callback must be + * re-registered in order to continue listening to the IMS service state. + */ + public abstract void onError(); + + /** + * The callback to notify the death of telephony process + * @hide + */ + public final void binderDied() { + if (mCallback != null) { + mCallback.getExecutor().execute(() -> onError()); + } + } + + /** + * Return the callback binder + * @hide + */ + public IImsStateCallbackStub getCallbackBinder() { + return mCallback; + } +} diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 5a8066320e99..f913df588f40 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -28,15 +28,16 @@ import android.content.pm.PackageManager; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.BinderCacheManager; -import android.telephony.CarrierConfigManager; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper; +import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.stub.DelegateConnectionMessageCallback; import android.telephony.ims.stub.DelegateConnectionStateCallback; import android.telephony.ims.stub.SipDelegate; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.ITelephony; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -282,6 +283,7 @@ public class SipDelegateManager { private final Context mContext; private final int mSubId; private final BinderCacheManager<IImsRcsController> mBinderCache; + private final BinderCacheManager<ITelephony> mTelephonyBinderCache; /** * Only visible for testing. To instantiate an instance of this class, please use @@ -290,10 +292,12 @@ public class SipDelegateManager { */ @VisibleForTesting public SipDelegateManager(Context context, int subId, - BinderCacheManager<IImsRcsController> binderCache) { + BinderCacheManager<IImsRcsController> binderCache, + BinderCacheManager<ITelephony> telephonyBinderCache) { mContext = context; mSubId = subId; mBinderCache = binderCache; + mTelephonyBinderCache = telephonyBinderCache; } /** @@ -446,4 +450,65 @@ public class SipDelegateManager { + " into this method"); } } + + /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_RCS, + callback.getCallbackBinder(), mContext.getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback); + + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } } diff --git a/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl new file mode 100644 index 000000000000..e04b01d26e47 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl @@ -0,0 +1,22 @@ +/* + * 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.internal.telephony; + +oneway interface IImsStateCallback { + void onUnavailable(int reason); + void onAvailable(); +} diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 63732b5ee444..6b33a6894365 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -67,6 +67,7 @@ import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.IBooleanConsumer; import com.android.internal.telephony.ICallForwardingInfoCallback; +import com.android.internal.telephony.IImsStateCallback; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.OperatorInfo; @@ -1002,11 +1003,6 @@ interface ITelephony { boolean isManualNetworkSelectionAllowed(int subId); /** - * Enable or disable always reporting signal strength changes from radio. - */ - void setAlwaysReportSignalStrength(int subId, boolean isEnable); - - /** * Get P-CSCF address from PCO after data connection is established or modified. * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN * @param callingPackage The package making the call. @@ -2229,6 +2225,20 @@ interface ITelephony { List<String> getEquivalentHomePlmns(int subId, String callingPackage, String callingFeatureId); /** + * Enable or disable Voice over NR (VoNR) + * @param subId the subscription ID that this action applies to. + * @param enabled enable or disable VoNR. + * @return operation result. + */ + int setVoNrEnabled(int subId, boolean enabled); + + /** + * Is voice over NR enabled + * @return true if VoNR is enabled else false + */ + boolean isVoNrEnabled(int subId); + + /** * Enable/Disable E-UTRA-NR Dual Connectivity * @return operation result. See TelephonyManager.EnableNrDualConnectivityResult for * details @@ -2488,4 +2498,15 @@ interface ITelephony { * NSSAIs (configured, allowed and rejected). */ void getSlicingConfig(in ResultReceiver callback); + + /** + * Register an IMS connection state callback + */ + void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb, + in String callingPackage); + + /** + * Unregister an IMS connection state callback + */ + void unregisterImsStateCallback(in IImsStateCallback cb); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index b73f8271da1f..866fd2c7394e 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -528,6 +528,8 @@ public interface RILConstants { int RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP = 222; int RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP = 223; int RIL_REQUEST_GET_SLICING_CONFIG = 224; + int RIL_REQUEST_ENABLE_VONR = 225; + int RIL_REQUEST_IS_VONR_ENABLED = 226; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/test-mock/Android.bp b/test-mock/Android.bp index 0bb61987a514..22320fd53631 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -27,9 +27,8 @@ package { java_sdk_library { name: "android.test.mock", - - srcs: [ - ":android-test-mock-sources", + srcs: [":android-test-mock-sources"], + api_srcs: [ // Note: Below are NOT APIs of this library. We only take APIs under // the android.test.mock package. They however provide private APIs that // android.test.mock APIs references to. We need to have the classes in @@ -44,15 +43,9 @@ java_sdk_library { "app-compat-annotations", "unsupportedappusage", ], - api_packages: [ "android.test.mock", ], - // Only include android.test.mock.* classes. Jarjar rules below removes - // classes in other packages like android.content. In order to keep the - // list up-to-date, permitted_packages ensures that the library contains - // clases under android.test.mock after the jarjar rules are applied. - jarjar_rules: "jarjar-rules.txt", permitted_packages: [ "android.test.mock", ], diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt deleted file mode 100644 index 4420a4413f5b..000000000000 --- a/test-mock/jarjar-rules.txt +++ /dev/null @@ -1,7 +0,0 @@ -zap android.accounts.** -zap android.app.** -zap android.content.** -zap android.database.** -zap android.os.** -zap android.util.** -zap android.view.** diff --git a/tests/AttestationVerificationTest/OWNERS b/tests/AttestationVerificationTest/OWNERS new file mode 100644 index 000000000000..a7a6ef156eda --- /dev/null +++ b/tests/AttestationVerificationTest/OWNERS @@ -0,0 +1 @@ +include /core/java/android/security/attestationverification/OWNERS diff --git a/tests/FlickerTests/OWNERS b/tests/FlickerTests/OWNERS index b5561010e7f9..c1221e3940d2 100644 --- a/tests/FlickerTests/OWNERS +++ b/tests/FlickerTests/OWNERS @@ -1,3 +1,4 @@ # Bug component: 909476 include /services/core/java/com/android/server/wm/OWNERS -natanieljr@google.com
\ No newline at end of file +natanieljr@google.com +pablogamito@google.com diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java index 84448333a8c6..525a78486efc 100644 --- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java +++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java @@ -207,17 +207,6 @@ public class SystemPreparer extends ExternalResource { default: device.executeShellCommand("stop"); device.executeShellCommand("start"); - ITestDevice.RecoveryMode cachedRecoveryMode = device.getRecoveryMode(); - device.setRecoveryMode(ITestDevice.RecoveryMode.ONLINE); - - if (device.isEncryptionSupported()) { - if (device.isDeviceEncrypted()) { - LogUtil.CLog.e("Device is encrypted after userspace reboot!"); - device.unlockDevice(); - } - } - - device.setRecoveryMode(cachedRecoveryMode); device.waitForDeviceAvailable(); break; } diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java new file mode 100644 index 000000000000..f7d3697029d6 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.vcn; + +import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY; +import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +public class VcnCellUnderlyingNetworkPriorityTest { + private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>(); + private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>(); + + // Package private for use in VcnGatewayConnectionConfigTest + static VcnCellUnderlyingNetworkPriority getTestNetworkPriority() { + return new VcnCellUnderlyingNetworkPriority.Builder() + .setNetworkQuality(NETWORK_QUALITY_OK) + .setAllowMetered(true /* allowMetered */) + .setAllowedPlmnIds(ALLOWED_PLMN_IDS) + .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS) + .setAllowRoaming(true /* allowRoaming */) + .setRequireOpportunistic(true /* requireOpportunistic */) + .build(); + } + + @Test + public void testBuilderAndGetters() { + final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority(); + assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality()); + assertTrue(networkPriority.allowMetered()); + assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedPlmnIds()); + assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds()); + assertTrue(networkPriority.allowRoaming()); + assertTrue(networkPriority.requireOpportunistic()); + } + + @Test + public void testBuilderAndGettersForDefaultValues() { + final VcnCellUnderlyingNetworkPriority networkPriority = + new VcnCellUnderlyingNetworkPriority.Builder().build(); + assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality()); + assertFalse(networkPriority.allowMetered()); + assertEquals(new HashSet<String>(), networkPriority.getAllowedPlmnIds()); + assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds()); + assertFalse(networkPriority.allowRoaming()); + assertFalse(networkPriority.requireOpportunistic()); + } + + @Test + public void testPersistableBundle() { + final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority(); + assertEquals( + networkPriority, + VcnUnderlyingNetworkPriority.fromPersistableBundle( + networkPriority.toPersistableBundle())); + } +} diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index dc338ae0fdc7..724c33ffb354 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -38,6 +38,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @@ -50,9 +51,17 @@ public class VcnGatewayConnectionConfigTest { }; public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN}; + private static final LinkedHashSet<VcnUnderlyingNetworkPriority> UNDERLYING_NETWORK_PRIORITIES = + new LinkedHashSet(); + static { Arrays.sort(EXPOSED_CAPS); Arrays.sort(UNDERLYING_CAPS); + + UNDERLYING_NETWORK_PRIORITIES.add( + VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority()); + UNDERLYING_NETWORK_PRIORITIES.add( + VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority()); } public static final long[] RETRY_INTERVALS_MS = @@ -82,7 +91,10 @@ public class VcnGatewayConnectionConfigTest { // Public for use in VcnGatewayConnectionTest public static VcnGatewayConnectionConfig buildTestConfig() { - return buildTestConfigWithExposedCaps(EXPOSED_CAPS); + final VcnGatewayConnectionConfig.Builder builder = + newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES); + + return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS); } private static VcnGatewayConnectionConfig.Builder newBuilder() { @@ -159,6 +171,15 @@ public class VcnGatewayConnectionConfigTest { } @Test + public void testBuilderRequiresNonNullNetworkPriorities() { + try { + newBuilder().setVcnUnderlyingNetworkPriorities(null); + fail("Expected exception due to invalid underlyingNetworkPriorities"); + } catch (NullPointerException e) { + } + } + + @Test public void testBuilderRequiresNonNullRetryInterval() { try { newBuilder().setRetryIntervalsMillis(null); @@ -195,6 +216,7 @@ public class VcnGatewayConnectionConfigTest { Arrays.sort(exposedCaps); assertArrayEquals(EXPOSED_CAPS, exposedCaps); + assertEquals(UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities()); assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams()); assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis()); diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java new file mode 100644 index 000000000000..dd272cb38596 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net.vcn; + +import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY; +import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +public class VcnWifiUnderlyingNetworkPriorityTest { + private static final String SSID = "TestWifi"; + private static final int INVALID_NETWORK_QUALITY = -1; + + // Package private for use in VcnGatewayConnectionConfigTest + static VcnWifiUnderlyingNetworkPriority getTestNetworkPriority() { + return new VcnWifiUnderlyingNetworkPriority.Builder() + .setNetworkQuality(NETWORK_QUALITY_OK) + .setAllowMetered(true /* allowMetered */) + .setSsid(SSID) + .build(); + } + + @Test + public void testBuilderAndGetters() { + final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority(); + assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality()); + assertTrue(networkPriority.allowMetered()); + assertEquals(SSID, networkPriority.getSsid()); + } + + @Test + public void testBuilderAndGettersForDefaultValues() { + final VcnWifiUnderlyingNetworkPriority networkPriority = + new VcnWifiUnderlyingNetworkPriority.Builder().build(); + assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality()); + assertFalse(networkPriority.allowMetered()); + assertNull(SSID, networkPriority.getSsid()); + } + + @Test + public void testBuildWithInvalidNetworkQuality() { + try { + new VcnWifiUnderlyingNetworkPriority.Builder() + .setNetworkQuality(INVALID_NETWORK_QUALITY); + fail("Expected to fail due to the invalid network quality"); + } catch (Exception expected) { + } + } + + @Test + public void testPersistableBundle() { + final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority(); + assertEquals( + networkPriority, + VcnUnderlyingNetworkPriority.fromPersistableBundle( + networkPriority.toPersistableBundle())); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index c9a8947ab5ef..e547400fff73 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -118,8 +118,10 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection @Test public void testNullNetworkDoesNotTriggerDisconnect() throws Exception { + doReturn(false).when(mDeps).isAirplaneModeOn(any()); + mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(null); mTestLooper.dispatchAll(); @@ -129,9 +131,22 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection } @Test + public void testNullNetworkAirplaneModeDisconnects() throws Exception { + doReturn(true).when(mDeps).isAirplaneModeOn(any()); + + mGatewayConnection + .getUnderlyingNetworkControllerCallback() + .onSelectedUnderlyingNetworkChanged(null); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).kill(); + } + + @Test public void testNewNetworkTriggersMigration() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); mTestLooper.dispatchAll(); @@ -143,7 +158,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection @Test public void testSameNetworkDoesNotTriggerMigration() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); mTestLooper.dispatchAll(); @@ -203,7 +218,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection triggerChildOpened(); mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); getChildSessionCallback() .onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform()); @@ -297,8 +312,6 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection any(), any()); verify(mNetworkAgent).register(); - verify(mNetworkAgent) - .setUnderlyingNetworks(eq(singletonList(TEST_UNDERLYING_NETWORK_RECORD_1.network))); verify(mNetworkAgent).markConnected(); verify(mIpSecSvc) @@ -313,6 +326,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection final NetworkCapabilities nc = ncCaptor.getValue(); assertTrue(nc.hasTransport(TRANSPORT_CELLULAR)); assertFalse(nc.hasTransport(TRANSPORT_WIFI)); + assertEquals(List.of(TEST_UNDERLYING_NETWORK_RECORD_1.network), nc.getUnderlyingNetworks()); for (int cap : mConfig.getAllExposedCapabilities()) { assertTrue(nc.hasCapability(cap)); } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java index d1f3a210d870..3c70759a2fa6 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java @@ -64,7 +64,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio @Test public void testNullNetworkTriggersDisconnect() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(null); mTestLooper.dispatchAll(); @@ -76,7 +76,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio @Test public void testNewNetworkTriggersReconnect() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); mTestLooper.dispatchAll(); @@ -89,7 +89,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio @Test public void testSameNetworkDoesNotTriggerReconnect() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); mTestLooper.dispatchAll(); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java index 2056eea42ce6..f3eb82f46de7 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java @@ -78,7 +78,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect @Test public void testNetworkChangesTriggerStateTransitions() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); mTestLooper.dispatchAll(); @@ -89,7 +89,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect @Test public void testNullNetworkDoesNotTriggerStateTransition() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(null); mTestLooper.dispatchAll(); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java index 1c859790a2fe..6568cdd44377 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java @@ -58,7 +58,7 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect @Test public void testNewNetworkTriggerRetry() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); mTestLooper.dispatchAll(); @@ -72,7 +72,7 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect @Test public void testSameNetworkDoesNotTriggerRetry() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); mTestLooper.dispatchAll(); @@ -86,7 +86,7 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect @Test public void testNullNetworkTriggersDisconnect() throws Exception { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(null); mTestLooper.dispatchAll(); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java index a7001713533c..b9dfda38a01c 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java @@ -16,6 +16,7 @@ package com.android.server.vcn; +import static android.net.IpSecManager.IpSecTunnelInterface; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; @@ -24,6 +25,8 @@ 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 com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR; +import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; @@ -36,8 +39,11 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.CALLS_REAL_METHODS; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.net.IpSecManager; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; @@ -53,14 +59,17 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; +import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.net.InetAddress; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -70,6 +79,8 @@ import java.util.UUID; public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { private static final int TEST_UID = Process.myUid() + 1; + private static final String LOOPBACK_IFACE = "lo"; + private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID()); private static final int TEST_SIM_SLOT_INDEX = 1; private static final int TEST_SUBSCRIPTION_ID_1 = 2; @@ -77,6 +88,12 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { private static final int TEST_SUBSCRIPTION_ID_2 = 3; private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class); private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP; + private static final String TEST_TCP_BUFFER_SIZES = "1,2,3,4,5,6"; + private static final int TEST_MTU = 1300; + private static final int TEST_MTU_DELTA = 64; + private static final List<LinkAddress> TEST_INTERNAL_ADDRESSES = + Arrays.asList(new LinkAddress(DUMMY_ADDR, 16)); + private static final List<InetAddress> TEST_DNS_ADDRESSES = Arrays.asList(DUMMY_ADDR); private static final int TEST_UPSTREAM_BANDWIDTH = 1234; private static final int TEST_DOWNSTREAM_BANDWIDTH = 2345; @@ -116,8 +133,9 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { capBuilder.setLinkUpstreamBandwidthKbps(TEST_UPSTREAM_BANDWIDTH); capBuilder.setLinkDownstreamBandwidthKbps(TEST_DOWNSTREAM_BANDWIDTH); capBuilder.setAdministratorUids(new int[] {TEST_UID}); + final Network underlyingNetwork = mock(Network.class, CALLS_REAL_METHODS); UnderlyingNetworkRecord record = new UnderlyingNetworkRecord( - mock(Network.class, CALLS_REAL_METHODS), + underlyingNetwork, capBuilder.build(), new LinkProperties(), false); final NetworkCapabilities vcnCaps = VcnGatewayConnection.buildNetworkCapabilities( @@ -128,6 +146,7 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR)); assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED)); assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(vcnCaps.getUnderlyingNetworks().equals(List.of(underlyingNetwork))); for (int cap : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) { if (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN) { @@ -166,14 +185,67 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { } @Test - public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() { + public void testBuildLinkProperties() throws Exception { + final IpSecTunnelInterface tunnelIface = + mContext.getSystemService(IpSecManager.class) + .createIpSecTunnelInterface( + DUMMY_ADDR, DUMMY_ADDR, TEST_UNDERLYING_NETWORK_RECORD_1.network); + + final LinkProperties underlyingLp = new LinkProperties(); + underlyingLp.setInterfaceName(LOOPBACK_IFACE); + underlyingLp.setTcpBufferSizes(TEST_TCP_BUFFER_SIZES); + doReturn(TEST_MTU).when(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE); + + final VcnChildSessionConfiguration childSessionConfig = + mock(VcnChildSessionConfiguration.class); + doReturn(TEST_INTERNAL_ADDRESSES).when(childSessionConfig).getInternalAddresses(); + doReturn(TEST_DNS_ADDRESSES).when(childSessionConfig).getInternalDnsServers(); + + UnderlyingNetworkRecord record = + new UnderlyingNetworkRecord( + mock(Network.class, CALLS_REAL_METHODS), + new NetworkCapabilities.Builder().build(), + underlyingLp, + false); + + final LinkProperties vcnLp1 = + mGatewayConnection.buildConnectedLinkProperties( + VcnGatewayConnectionConfigTest.buildTestConfig(), + tunnelIface, + childSessionConfig, + record); + + verify(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE); + + // Instead of having to recalculate the final MTU (after accounting for IPsec overhead), + // calculate another set of Link Properties with a lower MTU, and calculate the delta. + doReturn(TEST_MTU - TEST_MTU_DELTA).when(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE); + + final LinkProperties vcnLp2 = + mGatewayConnection.buildConnectedLinkProperties( + VcnGatewayConnectionConfigTest.buildTestConfig(), + tunnelIface, + childSessionConfig, + record); + + verify(mDeps, times(2)).getUnderlyingIfaceMtu(LOOPBACK_IFACE); + + assertEquals(tunnelIface.getInterfaceName(), vcnLp1.getInterfaceName()); + assertEquals(TEST_INTERNAL_ADDRESSES, vcnLp1.getLinkAddresses()); + assertEquals(TEST_DNS_ADDRESSES, vcnLp1.getDnsServers()); + assertEquals(TEST_TCP_BUFFER_SIZES, vcnLp1.getTcpBufferSizes()); + assertEquals(TEST_MTU_DELTA, vcnLp1.getMtu() - vcnLp2.getMtu()); + } + + @Test + public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkController() { verifyWakeLockSetUp(); final TelephonySubscriptionSnapshot updatedSnapshot = mock(TelephonySubscriptionSnapshot.class); mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot); - verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot)); + verify(mUnderlyingNetworkController).updateSubscriptionSnapshot(eq(updatedSnapshot)); verifyWakeLockAcquired(); mTestLooper.dispatchAll(); @@ -184,13 +256,13 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { @Test public void testNonNullUnderlyingNetworkRecordUpdateCancelsAlarm() { mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(null); verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */); mGatewayConnection - .getUnderlyingNetworkTrackerCallback() + .getUnderlyingNetworkControllerCallback() .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); verify(mDisconnectRequestAlarm).cancel(); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 64d0bca15ce9..8a0af2dff8c8 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -16,7 +16,6 @@ package com.android.server.vcn; -import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; import static com.android.server.vcn.VcnTestUtils.setupIpSecManager; @@ -62,6 +61,8 @@ import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscription import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock; +import com.android.server.vcn.routeselection.UnderlyingNetworkController; +import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; import org.junit.Before; import org.mockito.ArgumentCaptor; @@ -137,7 +138,7 @@ public class VcnGatewayConnectionTestBase { @NonNull protected final VcnGatewayConnectionConfig mConfig; @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback; @NonNull protected final VcnGatewayConnection.Dependencies mDeps; - @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker; + @NonNull protected final UnderlyingNetworkController mUnderlyingNetworkController; @NonNull protected final VcnWakeLock mWakeLock; @NonNull protected final WakeupMessage mTeardownTimeoutAlarm; @NonNull protected final WakeupMessage mDisconnectRequestAlarm; @@ -158,7 +159,7 @@ public class VcnGatewayConnectionTestBase { mConfig = VcnGatewayConnectionConfigTest.buildTestConfig(); mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class); mDeps = mock(VcnGatewayConnection.Dependencies.class); - mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class); + mUnderlyingNetworkController = mock(UnderlyingNetworkController.class); mWakeLock = mock(VcnWakeLock.class); mTeardownTimeoutAlarm = mock(WakeupMessage.class); mDisconnectRequestAlarm = mock(WakeupMessage.class); @@ -176,9 +177,9 @@ public class VcnGatewayConnectionTestBase { doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); - doReturn(mUnderlyingNetworkTracker) + doReturn(mUnderlyingNetworkController) .when(mDeps) - .newUnderlyingNetworkTracker(any(), any(), any(), any()); + .newUnderlyingNetworkController(any(), any(), any(), any()); doReturn(mWakeLock) .when(mDeps) .newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any()); diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java index 5af69b5d1bf2..c954cb84df7f 100644 --- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java @@ -14,9 +14,11 @@ * limitations under the License. */ -package com.android.server.vcn; +package com.android.server.vcn.routeselection; import static com.android.server.vcn.VcnTestUtils.setupSystemService; +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT; +import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -48,10 +50,11 @@ import android.telephony.TelephonyManager; import android.util.ArraySet; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; -import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback; -import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkListener; -import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; -import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; +import com.android.server.vcn.VcnContext; +import com.android.server.vcn.VcnNetworkProvider; +import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback; +import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback; +import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener; import org.junit.Before; import org.junit.Test; @@ -64,7 +67,7 @@ import java.util.Arrays; import java.util.Set; import java.util.UUID; -public class UnderlyingNetworkTrackerTest { +public class UnderlyingNetworkControllerTest { private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); private static final int INITIAL_SUB_ID_1 = 1; private static final int INITIAL_SUB_ID_2 = 2; @@ -102,14 +105,14 @@ public class UnderlyingNetworkTrackerTest { @Mock private TelephonyManager mTelephonyManager; @Mock private CarrierConfigManager mCarrierConfigManager; @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot; - @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb; + @Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb; @Mock private Network mNetwork; @Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor; private TestLooper mTestLooper; private VcnContext mVcnContext; - private UnderlyingNetworkTracker mUnderlyingNetworkTracker; + private UnderlyingNetworkController mUnderlyingNetworkController; @Before public void setUp() { @@ -140,12 +143,9 @@ public class UnderlyingNetworkTrackerTest { when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS); - mUnderlyingNetworkTracker = - new UnderlyingNetworkTracker( - mVcnContext, - SUB_GROUP, - mSubscriptionSnapshot, - mNetworkTrackerCb); + mUnderlyingNetworkController = + new UnderlyingNetworkController( + mVcnContext, SUB_GROUP, mSubscriptionSnapshot, mNetworkControllerCb); } private void resetVcnContext() { @@ -181,11 +181,8 @@ public class UnderlyingNetworkTrackerTest { mVcnNetworkProvider, true /* isInTestMode */); - new UnderlyingNetworkTracker( - vcnContext, - SUB_GROUP, - mSubscriptionSnapshot, - mNetworkTrackerCb); + new UnderlyingNetworkController( + vcnContext, SUB_GROUP, mSubscriptionSnapshot, mNetworkControllerCb); verify(cm) .registerNetworkCallback( @@ -233,7 +230,7 @@ public class UnderlyingNetworkTrackerTest { mock(TelephonySubscriptionSnapshot.class); when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS); - mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate); + mUnderlyingNetworkController.updateSubscriptionSnapshot(subscriptionUpdate); // verify that initially-filed bringup requests are unregistered (cell + wifi) verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 3)) @@ -255,7 +252,7 @@ public class UnderlyingNetworkTrackerTest { return getExpectedRequestBase() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .setSubscriptionIds(netCapsSubIds) - .setSignalStrength(UnderlyingNetworkTracker.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT) + .setSignalStrength(WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT) .build(); } @@ -264,7 +261,7 @@ public class UnderlyingNetworkTrackerTest { return getExpectedRequestBase() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .setSubscriptionIds(netCapsSubIds) - .setSignalStrength(UnderlyingNetworkTracker.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT) + .setSignalStrength(WIFI_EXIT_RSSI_THRESHOLD_DEFAULT) .build(); } @@ -304,7 +301,7 @@ public class UnderlyingNetworkTrackerTest { @Test public void testTeardown() { - mUnderlyingNetworkTracker.teardown(); + mUnderlyingNetworkController.teardown(); // Expect 5 NetworkBringupCallbacks to be unregistered: 1 for WiFi, 2 for Cellular (1x for // each subId), and 1 for each of the Wifi signal strength thresholds @@ -368,7 +365,7 @@ public class UnderlyingNetworkTrackerTest { networkCapabilities, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); return cb; } @@ -384,7 +381,7 @@ public class UnderlyingNetworkTrackerTest { UPDATED_NETWORK_CAPABILITIES, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); } @Test @@ -399,7 +396,7 @@ public class UnderlyingNetworkTrackerTest { INITIAL_NETWORK_CAPABILITIES, UPDATED_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); } @Test @@ -414,11 +411,13 @@ public class UnderlyingNetworkTrackerTest { SUSPENDED_NETWORK_CAPABILITIES, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verify(mNetworkControllerCb, 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)); + verify(mNetworkControllerCb, times(1)) + .onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); } @Test @@ -434,11 +433,13 @@ public class UnderlyingNetworkTrackerTest { INITIAL_NETWORK_CAPABILITIES, INITIAL_LINK_PROPERTIES, false /* isBlocked */); - verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verify(mNetworkControllerCb, 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)); + verify(mNetworkControllerCb, times(1)) + .onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); } @Test @@ -453,7 +454,7 @@ public class UnderlyingNetworkTrackerTest { INITIAL_NETWORK_CAPABILITIES, INITIAL_LINK_PROPERTIES, true /* isBlocked */); - verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord)); } @Test @@ -462,7 +463,7 @@ public class UnderlyingNetworkTrackerTest { cb.onLost(mNetwork); - verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(null); + verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(null); } @Test @@ -471,20 +472,20 @@ public class UnderlyingNetworkTrackerTest { cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES); - // Verify no more calls to the UnderlyingNetworkTrackerCallback when the + // Verify no more calls to the UnderlyingNetworkControllerCallback when the // UnderlyingNetworkRecord does not actually change - verifyNoMoreInteractions(mNetworkTrackerCb); + verifyNoMoreInteractions(mNetworkControllerCb); } @Test public void testRecordTrackerCallbackNotifiedAfterTeardown() { UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback(); - mUnderlyingNetworkTracker.teardown(); + mUnderlyingNetworkController.teardown(); cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES); // Verify that the only call was during onAvailable() - verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(any()); + verify(mNetworkControllerCb, times(1)).onSelectedUnderlyingNetworkChanged(any()); } // TODO (b/187991063): Add tests for network prioritization diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS index 69dfcc98340d..4f655e54a7c2 100644 --- a/tools/aapt2/OWNERS +++ b/tools/aapt2/OWNERS @@ -1,4 +1,4 @@ set noparent toddke@google.com -rtmitchell@google.com +zyy@google.com patb@google.com
\ No newline at end of file diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 5d57de6a9fb1..be09545abb45 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -154,7 +154,7 @@ StringPiece GetFilename(const StringPiece& path) { const char* end = path.end(); const char* last_dir_sep = path.begin(); for (const char* c = path.begin(); c != end; ++c) { - if (*c == sDirSep) { + if (*c == sDirSep || *c == sInvariantDirSep) { last_dir_sep = c + 1; } } diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h index 481a4cdb6ad0..e50cb505bf66 100644 --- a/tools/aapt2/util/Files.h +++ b/tools/aapt2/util/Files.h @@ -41,6 +41,8 @@ constexpr const char sDirSep = '/'; constexpr const char sPathSep = ':'; #endif +constexpr const char sInvariantDirSep = '/'; + enum class FileType { kUnknown = 0, kNonexistant, diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh index ac23c3d9cb4a..36bea57b710f 100755 --- a/tools/aosp/aosp_sha.sh +++ b/tools/aosp/aosp_sha.sh @@ -1,7 +1,7 @@ #!/bin/bash LOCAL_DIR="$( dirname "${BASH_SOURCE}" )" -if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then +if git branch -vv | grep -q -E "^\*[^\[]+\[aosp/"; then # Change appears to be in AOSP exit 0 elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then |