diff options
57 files changed, 1836 insertions, 1112 deletions
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 3d129d8548eb..20ce13322fd2 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -135,6 +135,10 @@ java_sdk_library { ":updatable-media-srcs", ], + api_lint: { + enabled: false, + }, + libs: [ "framework_media_annotation", ], diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt index 6158e2ece55f..1d912ebc71fa 100644 --- a/apex/media/framework/api/system-current.txt +++ b/apex/media/framework/api/system-current.txt @@ -3,38 +3,19 @@ package android.media { public final class MediaTranscodeManager { method @Nullable public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener); - field public static final int PRIORITY_REALTIME = 1; // 0x1 - field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1 } @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener { method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession); } - public static final class MediaTranscodeManager.TranscodingRequest { + public abstract static class MediaTranscodeManager.TranscodingRequest { method public int getClientPid(); method public int getClientUid(); method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor(); method @NonNull public android.net.Uri getDestinationUri(); - method public int getPriority(); method @Nullable public android.os.ParcelFileDescriptor getSourceFileDescriptor(); method @NonNull public android.net.Uri getSourceUri(); - method public int getType(); - method @Nullable public android.media.MediaFormat getVideoTrackFormat(); - } - - public static final class MediaTranscodeManager.TranscodingRequest.Builder { - ctor public MediaTranscodeManager.TranscodingRequest.Builder(); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build(); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat); } public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver { @@ -71,5 +52,18 @@ package android.media { method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int); } + public static final class MediaTranscodeManager.VideoTranscodingRequest extends android.media.MediaTranscodeManager.TranscodingRequest { + method @NonNull public android.media.MediaFormat getVideoTrackFormat(); + } + + public static final class MediaTranscodeManager.VideoTranscodingRequest.Builder { + ctor public MediaTranscodeManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest build(); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientPid(int); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientUid(int); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(android.os.ParcelFileDescriptor); + method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(android.os.ParcelFileDescriptor); + } + } diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java index 84332e5bbcf0..79e0d58cf495 100644 --- a/apex/media/framework/java/android/media/MediaTranscodeManager.java +++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java @@ -50,27 +50,23 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - MediaTranscodeManager provides an interface to the system's media transcoding service and can be - used to transcode media files, e.g. transcoding a video from HEVC to AVC. + Android 12 introduces Compatible media transcoding feature. See + <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding"> + Compatible media transcoding</a>. MediaTranscodeManager provides an interface to the system's media + transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to + AVC. <h3>Transcoding Types</h3> <h4>Video Transcoding</h4> - When transcoding a video file, the video file could be of any of the following types: - <ul> - <li> Video file with single video track. </li> - <li> Video file with multiple video track. </li> - <li> Video file with multiple video tracks and audio tracks. </li> - <li> Video file with video/audio tracks and metadata track. Note that metadata track will be passed - through only if it could be recognized by {@link MediaExtractor}. - TODO(hkuang): Finalize the metadata track behavior. </li> - </ul> + When transcoding a video file, the video track will be transcoded based on the desired track format + and the audio track will be pass through without any modification. <p class=note> - Note that currently only support transcoding video file in mp4 format. + Note that currently only support transcoding video file in mp4 format and with single video track. <h3>Transcoding Request</h3> <p> To transcode a media file, first create a {@link TranscodingRequest} through its builder class - {@link TranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through + {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through {@link MediaTranscodeManager#enqueueRequest( TranscodingRequest, Executor, OnTranscodingFinishedListener)} TranscodeRequest are processed based on client process's priority and request priority. When a @@ -82,23 +78,9 @@ import java.util.concurrent.Executors; Here is an example where <code>Builder</code> is used to specify all parameters <pre class=prettyprint> - TranscodingRequest request = - new TranscodingRequest.Builder() - .setSourceUri(srcUri) - .setDestinationUri(dstUri) - .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO) - .setPriority(REALTIME) - .setVideoTrackFormat(videoFormat) - .build(); + VideoTranscodingRequest request = + new VideoTranscodingRequest.Builder(srcUri, dstUri, videoFormat).build(); }</pre> - - TODO(hkuang): Add architecture diagram showing the transcoding service and api. - TODO(hkuang): Add sample code when API is settled. - TODO(hkuang): Clarify whether multiple video tracks is supported or not. - TODO(hkuang): Clarify whether image/audio transcoding is supported or not. - TODO(hkuang): Clarify what will happen if there is unrecognized track in the source. - TODO(hkuang): Clarify whether supports scaling. - TODO(hkuang): Clarify whether supports framerate conversion. @hide */ @MinSdk(Build.VERSION_CODES.S) @@ -116,68 +98,6 @@ public final class MediaTranscodeManager { private static final float BPP = 0.25f; /** - * Default transcoding type. - * @hide - */ - public static final int TRANSCODING_TYPE_UNKNOWN = 0; - - /** - * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video file. - * <p>Note that currently only support transcoding video file in mp4 format. - */ - public static final int TRANSCODING_TYPE_VIDEO = 1; - - /** - * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image file. - * @hide - */ - public static final int TRANSCODING_TYPE_IMAGE = 2; - - /** @hide */ - @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = { - TRANSCODING_TYPE_UNKNOWN, - TRANSCODING_TYPE_VIDEO, - TRANSCODING_TYPE_IMAGE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TranscodingType {} - - /** - * Default value. - * @hide - */ - public static final int PRIORITY_UNKNOWN = 0; - /** - * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the - * client wants the transcoding result as soon as possible. - * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve - * performance penalty due to resource reallocation to prioritize the sessions with higher - * priority. - * TODO(hkuang): Add more description of this when priority is finalized. - */ - public static final int PRIORITY_REALTIME = 1; - - /** - * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not need - * the transcoding result as soon as possible. - * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set to - * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept - * delay of the transcoding result. - * @hide - * TODO(hkuang): Add more description of this when priority is finalized. - */ - public static final int PRIORITY_OFFLINE = 2; - - /** @hide */ - @IntDef(prefix = {"PRIORITY_"}, value = { - PRIORITY_UNKNOWN, - PRIORITY_REALTIME, - PRIORITY_OFFLINE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TranscodingPriority {} - - /** * Listener that gets notified when a transcoding operation has finished. * This listener gets notified regardless of how the operation finished. It is up to the * listener implementation to check the result and take appropriate action. @@ -503,7 +423,79 @@ public final class MediaTranscodeManager { } } - public static final class TranscodingRequest { + /** + * Abstract base class for all the TranscodingRequest. + * <p> TranscodingRequest encapsulates the desired configuration for the transcoding. + */ + public abstract static class TranscodingRequest { + /** + * + * Default transcoding type. + * @hide + */ + public static final int TRANSCODING_TYPE_UNKNOWN = 0; + + /** + * TRANSCODING_TYPE_VIDEO indicates that client wants to perform transcoding on a video. + * <p>Note that currently only support transcoding video file in mp4 format. + * @hide + */ + public static final int TRANSCODING_TYPE_VIDEO = 1; + + /** + * TRANSCODING_TYPE_IMAGE indicates that client wants to perform transcoding on an image. + * @hide + */ + public static final int TRANSCODING_TYPE_IMAGE = 2; + + /** @hide */ + @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = { + TRANSCODING_TYPE_UNKNOWN, + TRANSCODING_TYPE_VIDEO, + TRANSCODING_TYPE_IMAGE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TranscodingType {} + + /** + * Default value. + * + * @hide + */ + public static final int PRIORITY_UNKNOWN = 0; + /** + * PRIORITY_REALTIME indicates that the transcoding request is time-critical and that the + * client wants the transcoding result as soon as possible. + * <p> Set PRIORITY_REALTIME only if the transcoding is time-critical as it will involve + * performance penalty due to resource reallocation to prioritize the sessions with higher + * priority. + * + * @hide + */ + public static final int PRIORITY_REALTIME = 1; + + /** + * PRIORITY_OFFLINE indicates the transcoding is not time-critical and the client does not + * need the transcoding result as soon as possible. + * <p>Sessions with PRIORITY_OFFLINE will be scheduled behind PRIORITY_REALTIME. Always set + * to + * PRIORITY_OFFLINE if client does not need the result as soon as possible and could accept + * delay of the transcoding result. + * + * @hide + * + */ + public static final int PRIORITY_OFFLINE = 2; + + /** @hide */ + @IntDef(prefix = {"PRIORITY_"}, value = { + PRIORITY_UNKNOWN, + PRIORITY_REALTIME, + PRIORITY_OFFLINE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TranscodingPriority {} + /** Uri of the source media file. */ private @NonNull Uri mSourceUri; @@ -537,22 +529,6 @@ public final class MediaTranscodeManager { private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN; /** - * Desired output video format of the destination file. - * <p> If this is null, source file's video track will be passed through and copied to the - * destination file. - * <p> - */ - private @Nullable MediaFormat mVideoTrackFormat = null; - - /** - * Desired output audio format of the destination file. - * <p> If this is null, source file's audio track will be passed through and copied to the - * destination file. - * @hide - */ - private @Nullable MediaFormat mAudioTrackFormat = null; - - /** * Desired image format for the destination file. * <p> If this is null, source file's image track will be passed through and copied to the * destination file. @@ -563,6 +539,12 @@ public final class MediaTranscodeManager { @VisibleForTesting private TranscodingTestConfig mTestConfig = null; + /** + * Prevent public constructor access. + */ + /* package private */ TranscodingRequest() { + } + private TranscodingRequest(Builder b) { mSourceUri = b.mSourceUri; mSourceFileDescriptor = b.mSourceFileDescriptor; @@ -572,13 +554,13 @@ public final class MediaTranscodeManager { mClientPid = b.mClientPid; mPriority = b.mPriority; mType = b.mType; - mVideoTrackFormat = b.mVideoTrackFormat; - mAudioTrackFormat = b.mAudioTrackFormat; - mImageFormat = b.mImageFormat; mTestConfig = b.mTestConfig; } - /** Return the type of the transcoding. */ + /** + * Return the type of the transcoding. + * @hide + */ @TranscodingType public int getType() { return mType; @@ -624,22 +606,16 @@ public final class MediaTranscodeManager { return mDestinationFileDescriptor; } - /** Return priority of the transcoding. */ + /** + * Return priority of the transcoding. + * @hide + */ @TranscodingPriority public int getPriority() { return mPriority; } /** - * Return the video track format of the transcoding. - * This will be null is the transcoding is not for video transcoding. - */ - @Nullable - public MediaFormat getVideoTrackFormat() { - return mVideoTrackFormat; - } - - /** * Return TestConfig of the transcoding. * @hide */ @@ -648,6 +624,8 @@ public final class MediaTranscodeManager { return mTestConfig; } + abstract void writeFormatToParcel(TranscodingRequestParcel parcel); + /* Writes the TranscodingRequest to a parcel. */ private TranscodingRequestParcel writeToParcel(@NonNull Context context) { TranscodingRequestParcel parcel = new TranscodingRequestParcel(); @@ -671,7 +649,7 @@ public final class MediaTranscodeManager { } parcel.clientPackageName = packageName; } - parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat); + writeFormatToParcel(parcel); if (mTestConfig != null) { parcel.isForTesting = true; parcel.testConfig = mTestConfig; @@ -679,71 +657,12 @@ public final class MediaTranscodeManager { return parcel; } - /* Converts the MediaFormat to TranscodingVideoTrackFormat. */ - private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) { - if (format == null) { - throw new IllegalArgumentException("Invalid MediaFormat"); - } - - TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat(); - - if (format.containsKey(MediaFormat.KEY_MIME)) { - String mime = format.getString(MediaFormat.KEY_MIME); - if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) { - trackFormat.codecType = TranscodingVideoCodecType.kAvc; - } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) { - trackFormat.codecType = TranscodingVideoCodecType.kHevc; - } else { - throw new UnsupportedOperationException("Only support transcode to avc/hevc"); - } - } - - if (format.containsKey(MediaFormat.KEY_BIT_RATE)) { - int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE); - if (bitrateBps <= 0) { - throw new IllegalArgumentException("Bitrate must be larger than 0"); - } - trackFormat.bitrateBps = bitrateBps; - } - - if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey( - MediaFormat.KEY_HEIGHT)) { - int width = format.getInteger(MediaFormat.KEY_WIDTH); - int height = format.getInteger(MediaFormat.KEY_HEIGHT); - if (width <= 0 || height <= 0) { - throw new IllegalArgumentException("Width and height must be larger than 0"); - } - // TODO(hkuang): Validate the aspect ratio after adding scaling. - trackFormat.width = width; - trackFormat.height = height; - } - - if (format.containsKey(MediaFormat.KEY_PROFILE)) { - int profile = format.getInteger(MediaFormat.KEY_PROFILE); - if (profile <= 0) { - throw new IllegalArgumentException("Invalid codec profile"); - } - // TODO(hkuang): Validate the profile according to codec type. - trackFormat.profile = profile; - } - - if (format.containsKey(MediaFormat.KEY_LEVEL)) { - int level = format.getInteger(MediaFormat.KEY_LEVEL); - if (level <= 0) { - throw new IllegalArgumentException("Invalid codec level"); - } - // TODO(hkuang): Validate the level according to codec type. - trackFormat.level = level; - } - - return trackFormat; - } - /** - * Builder class for {@link TranscodingRequest} objects. - * Use this class to configure and create a <code>TranscodingRequest</code> instance. + * Builder to build a {@link TranscodingRequest} object. + * + * @param <T> The subclass to be built. */ - public static final class Builder { + abstract static class Builder<T extends Builder<T>> { private @NonNull Uri mSourceUri; private @NonNull Uri mDestinationUri; private @Nullable ParcelFileDescriptor mSourceFileDescriptor = null; @@ -752,80 +671,68 @@ public final class MediaTranscodeManager { private int mClientPid = -1; private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN; private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN; - private @Nullable MediaFormat mVideoTrackFormat; - private @Nullable MediaFormat mAudioTrackFormat; - private @Nullable MediaFormat mImageFormat; private TranscodingTestConfig mTestConfig; + abstract T self(); + /** - * Specifies the uri of source media file. + * Creates a builder for building {@link TranscodingRequest}s. * * Client must set the source Uri. If client also provides the source fileDescriptor * through is provided by {@link #setSourceFileDescriptor(ParcelFileDescriptor)}, * TranscodingSession will use the fd instead of calling back to the client to open the * sourceUri. + * + * + * @param type The transcoding type. * @param sourceUri Content uri for the source media file. - * @return The same builder instance. - * @throws IllegalArgumentException if Uri is null or empty. + * @param destinationUri Content uri for the destination media file. + * */ - @NonNull - public Builder setSourceUri(@NonNull Uri sourceUri) { + private Builder(@TranscodingType int type, @NonNull Uri sourceUri, + @NonNull Uri destinationUri) { + mType = type; + if (sourceUri == null || Uri.EMPTY.equals(sourceUri)) { throw new IllegalArgumentException( "You must specify a non-empty source Uri."); } mSourceUri = sourceUri; - return this; + + if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) { + throw new IllegalArgumentException( + "You must specify a non-empty destination Uri."); + } + mDestinationUri = destinationUri; } /** * Specifies the fileDescriptor opened from the source media file. * * This call is optional. If the source fileDescriptor is provided, TranscodingSession - * will use it directly instead of opening the uri from {@link #setSourceUri(Uri)}. It - * is client's responsibility to make sure the fileDescriptor is opened from the source - * uri. + * will use it directly instead of opening the uri from {@link #Builder(int, Uri, Uri)}. + * It is client's responsibility to make sure the fileDescriptor is opened from the + * source uri. * @param fileDescriptor a {@link ParcelFileDescriptor} opened from source media file. * @return The same builder instance. * @throws IllegalArgumentException if fileDescriptor is invalid. */ @NonNull - public Builder setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) { + public T setSourceFileDescriptor(@NonNull ParcelFileDescriptor fileDescriptor) { if (fileDescriptor == null || fileDescriptor.getFd() < 0) { throw new IllegalArgumentException( "Invalid source descriptor."); } mSourceFileDescriptor = fileDescriptor; - return this; - } - - /** - * Specifies the uri of the destination media file. - * - * Client must set the destination Uri. If client also provides the destination - * fileDescriptor through {@link #setDestinationFileDescriptor(ParcelFileDescriptor)}, - * TranscodingSession will use the fd instead of calling back to the client to open the - * destinationUri. - * @param destinationUri Content uri for the destination media file. - * @return The same builder instance. - * @throws IllegalArgumentException if Uri is null or empty. - */ - @NonNull - public Builder setDestinationUri(@NonNull Uri destinationUri) { - if (destinationUri == null || Uri.EMPTY.equals(destinationUri)) { - throw new IllegalArgumentException( - "You must specify a non-empty destination Uri."); - } - mDestinationUri = destinationUri; - return this; + return self(); } /** * Specifies the fileDescriptor opened from the destination media file. * * This call is optional. If the destination fileDescriptor is provided, - * TranscodingSession will use it directly instead of opening the uri from - * {@link #setDestinationUri(Uri)} upon transcoding starts. It is client's + * TranscodingSession will use it directly instead of opening the source uri from + * {@link #Builder(int, Uri, Uri)} upon transcoding starts. It is client's * responsibility to make sure the fileDescriptor is opened from the destination uri. * @param fileDescriptor a {@link ParcelFileDescriptor} opened from destination media * file. @@ -833,46 +740,54 @@ public final class MediaTranscodeManager { * @throws IllegalArgumentException if fileDescriptor is invalid. */ @NonNull - public Builder setDestinationFileDescriptor( + public T setDestinationFileDescriptor( @NonNull ParcelFileDescriptor fileDescriptor) { if (fileDescriptor == null || fileDescriptor.getFd() < 0) { throw new IllegalArgumentException( "Invalid destination descriptor."); } mDestinationFileDescriptor = fileDescriptor; - return this; + return self(); } /** * Specify the UID of the client that this request is for. + * <p> + * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the + * pid. Note that the permission check happens on the service side upon starting the + * transcoding. If the client does not have the permission, the transcoding will fail. + * * @param uid client Uid. * @return The same builder instance. * @throws IllegalArgumentException if uid is invalid. - * TODO(hkuang): Check the permission if it is allowed. */ @NonNull - public Builder setClientUid(int uid) { + public T setClientUid(int uid) { if (uid < 0) { throw new IllegalArgumentException("Invalid Uid"); } mClientUid = uid; - return this; + return self(); } /** - * Specify the PID of the client that this request is for. + * Specify the pid of the client that this request is for. + * <p> + * Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could forward the + * pid. Note that the permission check happens on the service side upon starting the + * transcoding. If the client does not have the permission, the transcoding will fail. + * * @param pid client Pid. * @return The same builder instance. * @throws IllegalArgumentException if pid is invalid. - * TODO(hkuang): Check the permission if it is allowed. */ @NonNull - public Builder setClientPid(int pid) { + public T setClientPid(int pid) { if (pid < 0) { throw new IllegalArgumentException("Invalid pid"); } mClientPid = pid; - return this; + return self(); } /** @@ -881,64 +796,15 @@ public final class MediaTranscodeManager { * @param priority Must be one of the {@code PRIORITY_*} * @return The same builder instance. * @throws IllegalArgumentException if flags is invalid. + * @hide */ @NonNull - public Builder setPriority(@TranscodingPriority int priority) { + public T setPriority(@TranscodingPriority int priority) { if (priority != PRIORITY_OFFLINE && priority != PRIORITY_REALTIME) { throw new IllegalArgumentException("Invalid priority: " + priority); } mPriority = priority; - return this; - } - - /** - * Specifies the type of transcoding. - * <p> Clients must provide the source and destination that corresponds to the - * transcoding type. - * - * @param type Must be one of the {@code TRANSCODING_TYPE_*} - * @return The same builder instance. - * @throws IllegalArgumentException if flags is invalid. - */ - @NonNull - public Builder setType(@TranscodingType int type) { - if (type != TRANSCODING_TYPE_VIDEO && type != TRANSCODING_TYPE_IMAGE) { - throw new IllegalArgumentException("Invalid transcoding type"); - } - mType = type; - return this; - } - - /** - * Specifies the desired video track format in the destination media file. - * <p>Client could only specify the settings that matters to them, e.g. codec format or - * bitrate. And by default, transcoding will preserve the original video's - * settings(bitrate, framerate, resolution) if not provided. - * <p>Note that some settings may silently fail to apply if the device does not - * support them. - * TODO(hkuang): Add MediaTranscodeUtil to help client generate transcoding setting. - * TODO(hkuang): Add MediaTranscodeUtil to check if the setting is valid. - * - * @param videoFormat MediaFormat containing the settings that client wants override in - * the original video's video track. - * @return The same builder instance. - * @throws IllegalArgumentException if videoFormat is invalid. - */ - @NonNull - public Builder setVideoTrackFormat(@NonNull MediaFormat videoFormat) { - if (videoFormat == null) { - throw new IllegalArgumentException("videoFormat must not be null"); - } - - // Check if the MediaFormat is for video by looking at the MIME type. - String mime = videoFormat.containsKey(MediaFormat.KEY_MIME) - ? videoFormat.getString(MediaFormat.KEY_MIME) : null; - if (mime == null || !mime.startsWith("video/")) { - throw new IllegalArgumentException("Invalid video format: wrong mime type"); - } - - mVideoTrackFormat = videoFormat; - return this; + return self(); } /** @@ -949,44 +815,9 @@ public final class MediaTranscodeManager { */ @VisibleForTesting @NonNull - public Builder setTestConfig(@NonNull TranscodingTestConfig config) { + public T setTestConfig(@NonNull TranscodingTestConfig config) { mTestConfig = config; - return this; - } - - /** - * @return a new {@link TranscodingRequest} instance successfully initialized with all - * the parameters set on this <code>Builder</code>. - * @throws UnsupportedOperationException if the parameters set on the - * <code>Builder</code> were incompatible, or if they are not supported by the - * device. - */ - @NonNull - public TranscodingRequest build() { - if (mSourceUri == null) { - throw new UnsupportedOperationException("Source URI must not be null"); - } - - if (mDestinationUri == null) { - throw new UnsupportedOperationException("Destination URI must not be null"); - } - - if (mPriority == PRIORITY_UNKNOWN) { - throw new UnsupportedOperationException("Must specify transcoding priority"); - } - - // Only support video transcoding now. - if (mType != TRANSCODING_TYPE_VIDEO) { - throw new UnsupportedOperationException("Only supports video transcoding now"); - } - - // Must provide video track format for video transcoding. - if (mType == TRANSCODING_TYPE_VIDEO && mVideoTrackFormat == null) { - throw new UnsupportedOperationException( - "Must provide video track format for video transcoding"); - } - - return new TranscodingRequest(this); + return self(); } } @@ -1201,6 +1032,206 @@ public final class MediaTranscodeManager { } /** + * VideoTranscodingRequest encapsulates the configuration for transcoding a video. + */ + public static final class VideoTranscodingRequest extends TranscodingRequest { + /** + * Desired output video format of the destination file. + * <p> If this is null, source file's video track will be passed through and copied to the + * destination file. + */ + private @Nullable MediaFormat mVideoTrackFormat = null; + + /** + * Desired output audio format of the destination file. + * <p> If this is null, source file's audio track will be passed through and copied to the + * destination file. + */ + private @Nullable MediaFormat mAudioTrackFormat = null; + + private VideoTranscodingRequest(VideoTranscodingRequest.Builder builder) { + super(builder); + mVideoTrackFormat = builder.mVideoTrackFormat; + mAudioTrackFormat = builder.mAudioTrackFormat; + } + + /** + * Return the video track format of the transcoding. + * This will be null if client has not specified the video track format. + */ + @NonNull + public MediaFormat getVideoTrackFormat() { + return mVideoTrackFormat; + } + + @Override + void writeFormatToParcel(TranscodingRequestParcel parcel) { + parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat); + } + + /* Converts the MediaFormat to TranscodingVideoTrackFormat. */ + private static TranscodingVideoTrackFormat convertToVideoTrackFormat(MediaFormat format) { + if (format == null) { + throw new IllegalArgumentException("Invalid MediaFormat"); + } + + TranscodingVideoTrackFormat trackFormat = new TranscodingVideoTrackFormat(); + + if (format.containsKey(MediaFormat.KEY_MIME)) { + String mime = format.getString(MediaFormat.KEY_MIME); + if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) { + trackFormat.codecType = TranscodingVideoCodecType.kAvc; + } else if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mime)) { + trackFormat.codecType = TranscodingVideoCodecType.kHevc; + } else { + throw new UnsupportedOperationException("Only support transcode to avc/hevc"); + } + } + + if (format.containsKey(MediaFormat.KEY_BIT_RATE)) { + int bitrateBps = format.getInteger(MediaFormat.KEY_BIT_RATE); + if (bitrateBps <= 0) { + throw new IllegalArgumentException("Bitrate must be larger than 0"); + } + trackFormat.bitrateBps = bitrateBps; + } + + if (format.containsKey(MediaFormat.KEY_WIDTH) && format.containsKey( + MediaFormat.KEY_HEIGHT)) { + int width = format.getInteger(MediaFormat.KEY_WIDTH); + int height = format.getInteger(MediaFormat.KEY_HEIGHT); + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("Width and height must be larger than 0"); + } + // TODO: Validate the aspect ratio after adding scaling. + trackFormat.width = width; + trackFormat.height = height; + } + + if (format.containsKey(MediaFormat.KEY_PROFILE)) { + int profile = format.getInteger(MediaFormat.KEY_PROFILE); + if (profile <= 0) { + throw new IllegalArgumentException("Invalid codec profile"); + } + // TODO: Validate the profile according to codec type. + trackFormat.profile = profile; + } + + if (format.containsKey(MediaFormat.KEY_LEVEL)) { + int level = format.getInteger(MediaFormat.KEY_LEVEL); + if (level <= 0) { + throw new IllegalArgumentException("Invalid codec level"); + } + // TODO: Validate the level according to codec type. + trackFormat.level = level; + } + + return trackFormat; + } + + /** + * Builder class for {@link VideoTranscodingRequest}. + */ + public static final class Builder extends + TranscodingRequest.Builder<VideoTranscodingRequest.Builder> { + /** + * Desired output video format of the destination file. + * <p> If this is null, source file's video track will be passed through and + * copied to the destination file. + */ + private @Nullable MediaFormat mVideoTrackFormat = null; + + /** + * Desired output audio format of the destination file. + * <p> If this is null, source file's audio track will be passed through and copied + * to the destination file. + */ + private @Nullable MediaFormat mAudioTrackFormat = null; + + /** + * Creates a builder for building {@link VideoTranscodingRequest}s. + * + * <p> Client could only specify the settings that matters to them, e.g. codec format or + * bitrate. And by default, transcoding will preserve the original video's settings + * (bitrate, framerate, resolution) if not provided. + * <p>Note that some settings may silently fail to apply if the device does not support + * them. + * @param sourceUri Content uri for the source media file. + * @param destinationUri Content uri for the destination media file. + * @param videoFormat MediaFormat containing the settings that client wants override in + * the original video's video track. + * @throws IllegalArgumentException if videoFormat is invalid. + */ + public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri, + @NonNull MediaFormat videoFormat) { + super(TRANSCODING_TYPE_VIDEO, sourceUri, destinationUri); + setVideoTrackFormat(videoFormat); + } + + @Override + @NonNull + public Builder setClientUid(int uid) { + super.setClientUid(uid); + return self(); + } + + @Override + @NonNull + public Builder setClientPid(int pid) { + super.setClientPid(pid); + return self(); + } + + @Override + @NonNull + public Builder setSourceFileDescriptor(ParcelFileDescriptor fd) { + super.setSourceFileDescriptor(fd); + return self(); + } + + @Override + @NonNull + public Builder setDestinationFileDescriptor(ParcelFileDescriptor fd) { + super.setDestinationFileDescriptor(fd); + return self(); + } + + private void setVideoTrackFormat(@NonNull MediaFormat videoFormat) { + if (videoFormat == null) { + throw new IllegalArgumentException("videoFormat must not be null"); + } + + // Check if the MediaFormat is for video by looking at the MIME type. + String mime = videoFormat.containsKey(MediaFormat.KEY_MIME) + ? videoFormat.getString(MediaFormat.KEY_MIME) : null; + if (mime == null || !mime.startsWith("video/")) { + throw new IllegalArgumentException("Invalid video format: wrong mime type"); + } + + mVideoTrackFormat = videoFormat; + } + + /** + * @return a new {@link TranscodingRequest} instance successfully initialized + * with all the parameters set on this <code>Builder</code>. + * @throws UnsupportedOperationException if the parameters set on the + * <code>Builder</code> were incompatible, or + * if they are not supported by the + * device. + */ + @NonNull + public VideoTranscodingRequest build() { + return new VideoTranscodingRequest(this); + } + + @Override + VideoTranscodingRequest.Builder self() { + return this; + } + } + } + + /** * Handle to an enqueued transcoding operation. An instance of this class represents a single * enqueued transcoding operation. The caller can use that instance to query the status or * progress, and to get the result once the operation has completed. diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index 4e5b3bac5713..0eff83c99282 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -22,13 +22,9 @@ cc_binary { multilib: { lib32: { - // TODO(b/142944043): Remove version script when libsigchain is a DSO. - version_script: "version-script32.txt", suffix: "32", }, lib64: { - // TODO(b/142944043): Remove version script when libsigchain is a DSO. - version_script: "version-script64.txt", suffix: "64", }, }, @@ -43,6 +39,13 @@ cc_binary { "libhidlbase", "liblog", "libnativeloader", + + // Even though app_process doesn't call into libsigchain, we need to + // make sure it's in the DT list of app_process, as we want all code + // in app_process and the libraries it loads to find libsigchain + // symbols before libc symbols. + "libsigchain", + "libutils", // This is a list of libraries that need to be included in order to avoid @@ -52,8 +55,6 @@ cc_binary { "libwilhelm", ], - whole_static_libs: ["libsigchain"], - compile_multilib: "both", cflags: [ diff --git a/cmds/app_process/version-script32.txt b/cmds/app_process/version-script32.txt deleted file mode 100644 index 70810e0b7173..000000000000 --- a/cmds/app_process/version-script32.txt +++ /dev/null @@ -1,15 +0,0 @@ -{ -global: - EnsureFrontOfChain; - AddSpecialSignalHandlerFn; - RemoveSpecialSignalHandlerFn; - SkipAddSignalHandler; - bsd_signal; - sigaction; - sigaction64; - signal; - sigprocmask; - sigprocmask64; -local: - *; -}; diff --git a/cmds/app_process/version-script64.txt b/cmds/app_process/version-script64.txt deleted file mode 100644 index 7bcd76b50f87..000000000000 --- a/cmds/app_process/version-script64.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ -global: - EnsureFrontOfChain; - AddSpecialSignalHandlerFn; - RemoveSpecialSignalHandlerFn; - SkipAddSignalHandler; - sigaction; - sigaction64; - signal; - sigprocmask; - sigprocmask64; -local: - *; -}; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 54c666314f98..0eb5553e11fa 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10452,11 +10452,32 @@ package android.telecom { method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onBluetoothCallQualityReportReceived(@NonNull android.telecom.BluetoothCallQualityReport); method public abstract void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState); - method @NonNull public abstract android.telecom.DiagnosticCall onInitializeDiagnosticCall(@NonNull android.telecom.Call.Details); - method public abstract void onRemoveDiagnosticCall(@NonNull android.telecom.DiagnosticCall); + method @NonNull public abstract android.telecom.CallDiagnostics onInitializeCallDiagnostics(@NonNull android.telecom.Call.Details); + method public abstract void onRemoveCallDiagnostics(@NonNull android.telecom.CallDiagnostics); field public static final String SERVICE_INTERFACE = "android.telecom.CallDiagnosticService"; } + public abstract class CallDiagnostics { + ctor public CallDiagnostics(); + method public final void clearDiagnosticMessage(int); + method public final void displayDiagnosticMessage(int, @NonNull CharSequence); + method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details); + method @Nullable public abstract CharSequence onCallDisconnected(int, int); + method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo); + method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality); + method public abstract void onReceiveDeviceToDeviceMessage(int, int); + method public final void sendDeviceToDeviceMessage(int, int); + field public static final int BATTERY_STATE_CHARGING = 3; // 0x3 + field public static final int BATTERY_STATE_GOOD = 2; // 0x2 + field public static final int BATTERY_STATE_LOW = 1; // 0x1 + field public static final int COVERAGE_GOOD = 2; // 0x2 + field public static final int COVERAGE_POOR = 1; // 0x1 + field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2 + field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1 + field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3 + field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4 + } + public static class CallScreeningService.CallResponse.Builder { method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallViaAudioProcessing(boolean); } @@ -10517,25 +10538,8 @@ package android.telecom { method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference); } - public abstract class DiagnosticCall { - ctor public DiagnosticCall(); - method public final void clearDiagnosticMessage(int); - method public final void displayDiagnosticMessage(int, @NonNull CharSequence); - method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details); - method @Nullable public abstract CharSequence onCallDisconnected(int, int); - method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo); - method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality); - method public abstract void onReceiveDeviceToDeviceMessage(int, int); - method public final void sendDeviceToDeviceMessage(int, int); - field public static final int BATTERY_STATE_CHARGING = 3; // 0x3 - field public static final int BATTERY_STATE_GOOD = 2; // 0x2 - field public static final int BATTERY_STATE_LOW = 1; // 0x1 - field public static final int COVERAGE_GOOD = 2; // 0x2 - field public static final int COVERAGE_POOR = 1; // 0x1 - field public static final int MESSAGE_CALL_AUDIO_CODEC = 2; // 0x2 - field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1 - field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3 - field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4 + @Deprecated public abstract class DiagnosticCall extends android.telecom.CallDiagnostics { + ctor @Deprecated public DiagnosticCall(); } public abstract class InCallService extends android.app.Service { diff --git a/core/java/android/net/NetworkScore.aidl b/core/java/android/net/NetworkScore.aidl new file mode 100644 index 000000000000..af12dcf7f17a --- /dev/null +++ b/core/java/android/net/NetworkScore.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.net; + +parcelable NetworkScore; + diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java new file mode 100644 index 000000000000..f47801002296 --- /dev/null +++ b/core/java/android/net/NetworkScore.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Object representing the quality of a network as perceived by the user. + * + * A NetworkScore object represents the characteristics of a network that affects how good the + * network is considered for a particular use. + * @hide + */ +// TODO : @SystemApi when the implementation is complete +public final class NetworkScore implements Parcelable { + // This will be removed soon. Do *NOT* depend on it for any new code that is not part of + // a migration. + private final int mLegacyInt; + + /** @hide */ + NetworkScore(final int legacyInt) { + this.mLegacyInt = legacyInt; + } + + private NetworkScore(@NonNull final Parcel in) { + mLegacyInt = in.readInt(); + } + + public int getLegacyInt() { + return mLegacyInt; + } + + @Override + public String toString() { + return "Score(" + mLegacyInt + ")"; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeInt(mLegacyInt); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull public static final Creator<NetworkScore> CREATOR = new Creator<>() { + @Override + @NonNull + public NetworkScore createFromParcel(@NonNull final Parcel in) { + return new NetworkScore(in); + } + + @Override + @NonNull + public NetworkScore[] newArray(int size) { + return new NetworkScore[size]; + } + }; + + /** + * A builder for NetworkScore. + */ + public static final class Builder { + private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE; + private int mLegacyInt = INVALID_LEGACY_INT; + + /** + * Sets the legacy int for this score. + * + * Do not rely on this. It will be gone by the time S is released. + * + * @param score the legacy int + * @return this + */ + @NonNull + public Builder setLegacyInt(final int score) { + mLegacyInt = score; + return this; + } + + /** + * Builds this NetworkScore. + * @return The built NetworkScore object. + */ + @NonNull + public NetworkScore build() { + return new NetworkScore(mLegacyInt); + } + } +} diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 4c26e2f33fb2..fa6472ee4a79 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -986,13 +986,13 @@ public abstract class BatteryStats implements Parcelable { public abstract void getDeferredJobsLineLocked(StringBuilder sb, int which); /** - * Returns the battery consumption (in microcoulombs) of the screen while on and uid active, + * Returns the battery consumption (in microcoulombs) of bluetooth for this uid, * derived from on device power measurement data. * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. * * {@hide} */ - public abstract long getScreenOnMeasuredBatteryConsumptionUC(); + public abstract long getBluetoothMeasuredBatteryConsumptionUC(); /** * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from @@ -1004,6 +1004,24 @@ public abstract class BatteryStats implements Parcelable { public abstract long getCpuMeasuredBatteryConsumptionUC(); /** + * Returns the battery consumption (in microcoulombs) of the screen while on and uid active, + * derived from on device power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getScreenOnMeasuredBatteryConsumptionUC(); + + /** + * Returns the battery consumption (in microcoulombs) of wifi for this uid, + * derived from on device power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getWifiMeasuredBatteryConsumptionUC(); + + /** * Returns the battery consumption (in microcoulombs) used by this uid for each * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}). @@ -2505,11 +2523,29 @@ public abstract class BatteryStats implements Parcelable { }; /** - * Returned value if power data is unavailable + * Returned value if power data is unavailable. + * + * {@hide} + */ + public static final long POWER_DATA_UNAVAILABLE = -1L; + + /** + * Returns the battery consumption (in microcoulombs) of bluetooth, derived from on + * device power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. * * {@hide} */ - public static final long POWER_DATA_UNAVAILABLE = -1; + public abstract long getBluetoothMeasuredBatteryConsumptionUC(); + + /** + * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power + * measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getCpuMeasuredBatteryConsumptionUC(); /** * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on @@ -2530,13 +2566,13 @@ public abstract class BatteryStats implements Parcelable { public abstract long getScreenDozeMeasuredBatteryConsumptionUC(); /** - * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power - * measurement data. + * Returns the battery consumption (in microcoulombs) of wifi, derived from on + * device power measurement data. * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. * * {@hide} */ - public abstract long getCpuMeasuredBatteryConsumptionUC(); + public abstract long getWifiMeasuredBatteryConsumptionUC(); /** * Returns the battery consumption (in microcoulombs) that each diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 0d9f715c11d4..a2edc93c6e5e 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -140,7 +140,7 @@ public class Build { */ @UnsupportedAppUsage @TestApi - public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1"); + public static final boolean IS_EMULATOR = getString("ro.boot.qemu").equals("1"); /** * A hardware serial number, if available. Alphanumeric only, case-insensitive. diff --git a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java index 016cc2f3cdad..ad74a9f5c906 100644 --- a/core/java/android/os/connectivity/WifiActivityEnergyInfo.java +++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java @@ -110,7 +110,7 @@ public final class WifiActivityEnergyInfo implements Parcelable { } // Calculate energy used using PowerProfile. PowerProfile powerProfile = new PowerProfile(context); - final double rxIdleCurrent = powerProfile.getAveragePower( + final double idleCurrent = powerProfile.getAveragePower( PowerProfile.POWER_WIFI_CONTROLLER_IDLE); final double rxCurrent = powerProfile.getAveragePower( PowerProfile.POWER_WIFI_CONTROLLER_RX); @@ -121,7 +121,7 @@ public final class WifiActivityEnergyInfo implements Parcelable { return (long) ((txDurationMillis * txCurrent + rxDurationMillis * rxCurrent - + idleDurationMillis * rxIdleCurrent) + + idleDurationMillis * idleCurrent) * voltage); } diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS index 718076b49f77..64570a8ad155 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -5,6 +5,8 @@ alanv@google.com adamp@google.com aurimas@google.com siyamed@google.com +mount@google.com +njawad@google.com per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 11466f4bc042..33b55ac2f0a0 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -169,7 +169,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 194; + static final int VERSION = 195; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -1009,8 +1009,12 @@ public class BatteryStatsImpl extends BatteryStats { protected @Nullable MeasuredEnergyStats mGlobalMeasuredEnergyStats; /** Last known screen state. Needed for apportioning display energy. */ int mScreenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN; + /** Bluetooth Power calculator for attributing measured bluetooth charge consumption to uids */ + @Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null; /** Cpu Power calculator for attributing measured cpu charge consumption to uids */ @Nullable CpuPowerCalculator mCpuPowerCalculator = null; + /** Wifi Power calculator for attributing measured wifi charge consumption to uids */ + @Nullable WifiPowerCalculator mWifiPowerCalculator = null; /** * These provide time bases that discount the time the device is plugged @@ -6967,6 +6971,16 @@ public class BatteryStatsImpl extends BatteryStats { } @Override + public long getBluetoothMeasuredBatteryConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH); + } + + @Override + public long getCpuMeasuredBatteryConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); + } + + @Override public long getScreenOnMeasuredBatteryConsumptionUC() { return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON); } @@ -6977,8 +6991,8 @@ public class BatteryStatsImpl extends BatteryStats { } @Override - public long getCpuMeasuredBatteryConsumptionUC() { - return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); + public long getWifiMeasuredBatteryConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI); } /** @@ -7815,6 +7829,26 @@ public class BatteryStatsImpl extends BatteryStats { return mUidMeasuredEnergyStats.getAccumulatedCustomBucketCharges(); } + @Override + public long getBluetoothMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH); + } + + @Override + public long getCpuMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); + } + + @Override + public long getScreenOnMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON); + } + + @Override + public long getWifiMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI); + } + /** * Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time * since last marked. Also sets the mark time for both these timers. @@ -8482,16 +8516,6 @@ public class BatteryStatsImpl extends BatteryStats { } } - @Override - public long getScreenOnMeasuredBatteryConsumptionUC() { - return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON); - } - - @Override - public long getCpuMeasuredBatteryConsumptionUC() { - return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); - } - void initNetworkActivityLocked() { detachIfNotNull(mNetworkByteActivityCounters); mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; @@ -11437,7 +11461,7 @@ public class BatteryStatsImpl extends BatteryStats { * @param info The energy information from the WiFi controller. */ public void updateWifiState(@Nullable final WifiActivityEnergyInfo info, - long elapsedRealtimeMs, long uptimeMs) { + final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces)); } @@ -11459,9 +11483,21 @@ public class BatteryStatsImpl extends BatteryStats { if (delta != null) { mNetworkStatsPool.release(delta); } + if (mIgnoreNextExternalStats) { + // TODO: Strictly speaking, we should re-mark all 5 timers for each uid (and the + // global one) here like we do for display. But I'm not sure it's worth the + // complicated code for a codepath that shouldn't ever actually happen in real + // life. + } return; } + final ArrayMap<Uid, Double> uidEstimatedConsumptionMah = + (mGlobalMeasuredEnergyStats != null + && mWifiPowerCalculator != null && consumedChargeUC > 0) ? + new ArrayMap<>() : null; + double totalEstimatedConsumptionMah = 0; + SparseLongArray rxPackets = new SparseLongArray(); SparseLongArray txPackets = new SparseLongArray(); long totalTxPackets = 0; @@ -11496,6 +11532,7 @@ public class BatteryStatsImpl extends BatteryStats { mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( entry.rxPackets); + // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum? rxPackets.put(u.getUid(), entry.rxPackets); // Sum the total number of packets so that the Rx Power can @@ -11515,12 +11552,42 @@ public class BatteryStatsImpl extends BatteryStats { mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( entry.txPackets); + // TODO(b/182845426): What if u was a mapped isolated uid? Shouldn't we sum? txPackets.put(u.getUid(), entry.txPackets); // Sum the total number of packets so that the Tx Power can // be evenly distributed amongst the apps. totalTxPackets += entry.txPackets; } + + // Calculate consumed energy for this uid. Only do so if WifiReporting isn't + // enabled (if it is, we'll do it later instead using info). + if (uidEstimatedConsumptionMah != null && info == null && !mHasWifiReporting) { + final long uidRunningMs = u.mWifiRunningTimer + .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000; + if (uidRunningMs > 0) u.mWifiRunningTimer.setMark(elapsedRealtimeMs); + + final long uidScanMs = u.mWifiScanTimer + .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000; + if (uidScanMs > 0) u.mWifiScanTimer.setMark(elapsedRealtimeMs); + + long uidBatchScanMs = 0; + for (int bn = 0; bn < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bn++) { + if (u.mWifiBatchedScanTimer[bn] != null) { + long bnMs = u.mWifiBatchedScanTimer[bn] + .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000; + if (bnMs > 0) { + u.mWifiBatchedScanTimer[bn].setMark(elapsedRealtimeMs); + } + uidBatchScanMs += bnMs; + } + } + + addDoubleToUidMap(uidEstimatedConsumptionMah, u, + mWifiPowerCalculator.calcPowerWithoutControllerDataMah( + entry.rxPackets, entry.txPackets, + uidRunningMs, uidScanMs, uidBatchScanMs)); + } } mNetworkStatsPool.release(delta); delta = null; @@ -11581,15 +11648,14 @@ public class BatteryStatsImpl extends BatteryStats { for (int i = 0; i < uidStatsSize; i++) { final Uid uid = mUidStats.valueAt(i); - long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked( + final long scanTimeSinceMarkMs = uid.mWifiScanTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; + long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final + long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs; // not final if (scanTimeSinceMarkMs > 0) { // Set the new mark so that next time we get new data since this point. uid.mWifiScanTimer.setMark(elapsedRealtimeMs); - long scanRxTimeSinceMarkMs = scanTimeSinceMarkMs; - long scanTxTimeSinceMarkMs = scanTimeSinceMarkMs; - // Our total scan time is more than the reported Tx/Rx time. // This is possible because the cost of a scan is approximate. // Let's normalize the result so that we evenly blame each app @@ -11623,6 +11689,7 @@ public class BatteryStatsImpl extends BatteryStats { // Distribute evenly the power consumed while Idle to each app holding a WiFi // lock. + long myIdleTimeMs = 0; final long wifiLockTimeSinceMarkMs = uid.mFullWifiLockTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; @@ -11630,8 +11697,7 @@ public class BatteryStatsImpl extends BatteryStats { // Set the new mark so that next time we get new data since this point. uid.mFullWifiLockTimer.setMark(elapsedRealtimeMs); - final long myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs) - / totalWifiLockTimeMs; + myIdleTimeMs = (wifiLockTimeSinceMarkMs * idleTimeMs) / totalWifiLockTimeMs; if (DEBUG_ENERGY) { Slog.d(TAG, " IdleTime for UID " + uid.getUid() + ": " + myIdleTimeMs + " ms"); @@ -11639,6 +11705,12 @@ public class BatteryStatsImpl extends BatteryStats { uid.getOrCreateWifiControllerActivityLocked().getIdleTimeCounter() .addCountLocked(myIdleTimeMs); } + + if (uidEstimatedConsumptionMah != null) { + double uidEstMah = mWifiPowerCalculator.calcPowerFromControllerDataMah( + scanRxTimeSinceMarkMs, scanTxTimeSinceMarkMs, myIdleTimeMs); + addDoubleToUidMap(uidEstimatedConsumptionMah, uid, uidEstMah); + } } if (DEBUG_ENERGY) { @@ -11658,6 +11730,11 @@ public class BatteryStatsImpl extends BatteryStats { } uid.getOrCreateWifiControllerActivityLocked().getTxTimeCounters()[0] .addCountLocked(myTxTimeMs); + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, uid, + mWifiPowerCalculator.calcPowerFromControllerDataMah( + 0, myTxTimeMs, 0)); + } } // Distribute the remaining Rx power appropriately between all apps that received @@ -11672,6 +11749,11 @@ public class BatteryStatsImpl extends BatteryStats { } uid.getOrCreateWifiControllerActivityLocked().getRxTimeCounter() .addCountLocked(myRxTimeMs); + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, uid, + mWifiPowerCalculator.calcPowerFromControllerDataMah( + myRxTimeMs, 0, 0)); + } } // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper. @@ -11690,10 +11772,11 @@ public class BatteryStatsImpl extends BatteryStats { // POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. final double opVolt = mPowerProfile.getAveragePower( PowerProfile.POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; + double controllerMaMs = 0; if (opVolt != 0) { // We store the power drain as mAms. - mWifiActivity.getPowerCounter().addCountLocked( - (long) (info.getControllerEnergyUsedMicroJoules() / opVolt)); + controllerMaMs = info.getControllerEnergyUsedMicroJoules() / opVolt; + mWifiActivity.getPowerCounter().addCountLocked((long) controllerMaMs); } // Converting uWs to mAms. // Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms @@ -11705,6 +11788,29 @@ public class BatteryStatsImpl extends BatteryStats { (monitoredRailChargeConsumedMaMs / MILLISECONDS_IN_HOUR); addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); mTmpRailStats.resetWifiTotalEnergyUsed(); + + if (uidEstimatedConsumptionMah != null) { + totalEstimatedConsumptionMah = Math.max(controllerMaMs / MILLISECONDS_IN_HOUR, + mWifiPowerCalculator.calcPowerFromControllerDataMah( + rxTimeMs, txTimeMs, idleTimeMs)); + } + } + + // Update the MeasuredEnergyStats information. + if (uidEstimatedConsumptionMah != null) { + mGlobalMeasuredEnergyStats.updateStandardBucket( + MeasuredEnergyStats.POWER_BUCKET_WIFI, consumedChargeUC); + + // Now calculate the consumption for each uid, according to its proportional usage. + if (!mHasWifiReporting) { + final long globalTimeMs = mGlobalWifiRunningTimer + .getTimeSinceMarkLocked(elapsedRealtimeMs * 1000) / 1000; + mGlobalWifiRunningTimer.setMark(elapsedRealtimeMs); + totalEstimatedConsumptionMah = mWifiPowerCalculator + .calcGlobalPowerWithoutControllerDataMah(globalTimeMs); + } + distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI, + consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah); } } } @@ -11948,7 +12054,7 @@ public class BatteryStatsImpl extends BatteryStats { * @param info The energy information from the bluetooth controller. */ public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info, - long elapsedRealtimeMs, long uptimeMs) { + final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating bluetooth stats: " + info); } @@ -11980,6 +12086,11 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, " Idle Time: " + idleTimeMs + " ms"); } + final ArrayMap<Uid, Double> uidEstimatedConsumptionMah = + (mGlobalMeasuredEnergyStats != null + && mBluetoothPowerCalculator != null && consumedChargeUC > 0) ? + new ArrayMap<>() : null; + long totalScanTimeMs = 0; final int uidCount = mUidStats.size(); @@ -12038,6 +12149,12 @@ public class BatteryStatsImpl extends BatteryStats { counter.getRxTimeCounter().addCountLocked(scanTimeRxSinceMarkMs); counter.getTxTimeCounters()[0].addCountLocked(scanTimeTxSinceMarkMs); + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, u, + mBluetoothPowerCalculator.calculatePowerMah( + scanTimeRxSinceMarkMs, scanTimeTxSinceMarkMs, 0)); + } + leftOverRxTimeMs -= scanTimeRxSinceMarkMs; leftOverTxTimeMs -= scanTimeTxSinceMarkMs; } @@ -12098,6 +12215,11 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs); } counter.getRxTimeCounter().addCountLocked(timeRxMs); + + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, u, + mBluetoothPowerCalculator.calculatePowerMah(timeRxMs, 0, 0)); + } } if (totalTxBytes > 0 && txBytes > 0) { @@ -12106,6 +12228,11 @@ public class BatteryStatsImpl extends BatteryStats { Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs); } counter.getTxTimeCounters()[0].addCountLocked(timeTxMs); + + if (uidEstimatedConsumptionMah != null) { + addDoubleToUidMap(uidEstimatedConsumptionMah, u, + mBluetoothPowerCalculator.calculatePowerMah(0, timeTxMs, 0)); + } } } } @@ -12117,12 +12244,26 @@ public class BatteryStatsImpl extends BatteryStats { // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. final double opVolt = mPowerProfile.getAveragePower( PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0; + double controllerMaMs = 0; if (opVolt != 0) { + controllerMaMs = (info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy) + / opVolt; // We store the power drain as mAms. - mBluetoothActivity.getPowerCounter().addCountLocked( - (long) ((info.getControllerEnergyUsed() - mLastBluetoothActivityInfo.energy) - / opVolt)); + mBluetoothActivity.getPowerCounter().addCountLocked((long) controllerMaMs); + } + + // Update the MeasuredEnergyStats information. + if (uidEstimatedConsumptionMah != null) { + mGlobalMeasuredEnergyStats.updateStandardBucket( + MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH, consumedChargeUC); + + double totalEstimatedMah + = mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs); + totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR); + distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH, + consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah); } + mLastBluetoothActivityInfo.set(info); } @@ -12311,37 +12452,17 @@ public class BatteryStatsImpl extends BatteryStats { // If multidisplay becomes a reality, this is probably more reasonable than pooling. // On the first pass, collect total time since mark so that we can normalize power. - long totalFgTimeMs = 0L; - final ArrayMap<Uid, Long> fgTimeMsArray = new ArrayMap<>(); + final ArrayMap<Uid, Double> fgTimeUsArray = new ArrayMap<>(); final long elapsedRealtimeUs = elapsedRealtimeMs * 1000; // TODO(b/175726779): Update and optimize the algorithm (e.g. avoid iterating over ALL uids) final int uidStatsSize = mUidStats.size(); for (int i = 0; i < uidStatsSize; i++) { final Uid uid = mUidStats.valueAt(i); - final long fgTimeMs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true) / 1000; - if (fgTimeMs == 0) continue; - fgTimeMsArray.put(uid, fgTimeMs); - totalFgTimeMs += fgTimeMs; - } - long totalDisplayChargeMC = chargeUC / 1000; // not final - - // Actually assign and distribute power usage to apps based on their fg time since mark. - // TODO(b/175726326): Decide on 'energy' units and make sure algorithm won't overflow. - final long fgTimeArraySize = fgTimeMsArray.size(); - for (int i = 0; i < fgTimeArraySize; i++) { - final Uid uid = fgTimeMsArray.keyAt(i); - final long fgTimeMs = fgTimeMsArray.valueAt(i); - - // Using long division: "appEnergy = totalEnergy * appFg/totalFg + 0.5" with rounding - final long appDisplayChargeMC = - (totalDisplayChargeMC * fgTimeMs + (totalFgTimeMs / 2)) - / totalFgTimeMs; - uid.addChargeToStandardBucketLocked(appDisplayChargeMC * 1000, powerBucket); - - // To mitigate round-off errors, remove this app from numerator & denominator totals - totalDisplayChargeMC -= appDisplayChargeMC; - totalFgTimeMs -= fgTimeMs; + final long fgTimeUs = uid.markProcessForegroundTimeUs(elapsedRealtimeMs, true); + if (fgTimeUs == 0) continue; + fgTimeUsArray.put(uid, (double) fgTimeUs); } + distributeEnergyToUidsLocked(powerBucket, chargeUC, fgTimeUsArray, 0); } /** @@ -12389,6 +12510,54 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * Attributes energy (for the given bucket) to each uid according to the following formula: + * blamedEnergy[uid] = totalEnergy * ratioNumerators[uid] / ratioDenominator; + * <p>Does nothing if ratioDenominator is 0. + * + * <p>Here, ratioDenominator = max(sumOfAllRatioNumerators, minRatioDenominator), + * so if given minRatioDenominator <= 0, then sumOfAllRatioNumerators will be used implicitly. + * + * <p>Note that ratioNumerators and minRatioDenominator must use the same units, but need not + * use the same units as totalConsumedChargeUC (which must be in microcoulombs). + * + * <p>A consequence of minRatioDenominator is that the sum over all uids might be less than + * totalConsumedChargeUC. This is intentional; the remainder is purposefully unnaccounted rather + * than incorrectly blamed on uids, and implies unknown (non-uid) sources of drain. + */ + // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>. + private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket, + long totalConsumedChargeUC, ArrayMap<Uid, Double> ratioNumerators, + double minRatioDenominator) { + + // If the sum of all app usage was greater than the total, use that instead: + double sumRatioNumerators = 0; + for (int i = ratioNumerators.size() - 1; i >= 0; i--) { + sumRatioNumerators += ratioNumerators.valueAt(i); + } + final double ratioDenominator = Math.max(sumRatioNumerators, minRatioDenominator); + if (ratioDenominator <= 0) return; + + for (int i = ratioNumerators.size() - 1; i >= 0; i--) { + final Uid uid = ratioNumerators.keyAt(i); + final double ratioNumerator = ratioNumerators.valueAt(i); + final long uidActualUC + = (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5); + uid.addChargeToStandardBucketLocked(uidActualUC, bucket); + } + } + + /** Adds the summand to the value stored in uidMap for the given uid. */ + // TODO(b/182845832): Use some sort of "SparseDoubleArray" instead of ArrayMap<Uid, Double>. + private static void addDoubleToUidMap(ArrayMap<Uid, Double> uidMap, Uid uid, double summand) { + if (uidMap == null) return; + final Double oldVal = uidMap.get(uid); + if (oldVal != null) { + summand += oldVal; + } + uidMap.put(uid, summand); + } + + /** * Read and record Rail Energy data. */ public void updateRailStatsLocked() { @@ -14222,15 +14391,20 @@ public class BatteryStatsImpl extends BatteryStats { if (mGlobalMeasuredEnergyStats == null) { mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - return; } else { supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo( supportedStandardBuckets, numCustomBuckets); } + if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH]) { + mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile); + } if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU]) { mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); } + if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) { + mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile); + } } if (supportedBucketMismatch) { diff --git a/core/java/com/android/internal/os/BluetoothPowerCalculator.java b/core/java/com/android/internal/os/BluetoothPowerCalculator.java index 7d42de4486a4..db1403479f8a 100644 --- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java +++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java @@ -15,8 +15,11 @@ */ package com.android.internal.os; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; + import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryStats.ControllerActivityCounter; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Process; @@ -65,17 +68,19 @@ public class BluetoothPowerCalculator extends PowerCalculator { builder.getUidBatteryConsumerBuilders(); for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); - calculateApp(app, total); + calculateApp(app, total, query); if (app.getUid() == Process.BLUETOOTH_UID) { app.excludeFromBatteryUsageStats(); systemBatteryConsumerBuilder.addUidBatteryConsumer(app); } } - final BatteryStats.ControllerActivityCounter activityCounter = + final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ? + POWER_DATA_UNAVAILABLE : batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); + final ControllerActivityCounter activityCounter = batteryStats.getBluetoothControllerActivity(); final long systemDurationMs = calculateDuration(activityCounter); - final double systemPowerMah = calculatePower(activityCounter); + final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter); // Subtract what the apps used, but clamp to 0. final long systemComponentDurationMs = Math.max(0, systemDurationMs - total.durationMs); @@ -91,11 +96,16 @@ public class BluetoothPowerCalculator extends PowerCalculator { systemComponentPowerMah); } - private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total) { - final BatteryStats.ControllerActivityCounter activityCounter = + private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration total, + BatteryUsageStatsQuery query) { + + final long measuredChargeUC = query.shouldForceUsePowerProfileModel() ? + POWER_DATA_UNAVAILABLE : + app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC(); + final ControllerActivityCounter activityCounter = app.getBatteryStatsUid().getBluetoothControllerActivity(); final long durationMs = calculateDuration(activityCounter); - final double powerMah = calculatePower(activityCounter); + final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter); app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_BLUETOOTH, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH, powerMah); @@ -121,10 +131,11 @@ public class BluetoothPowerCalculator extends PowerCalculator { } BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0); - final BatteryStats.ControllerActivityCounter activityCounter = + final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC(); + final ControllerActivityCounter activityCounter = batteryStats.getBluetoothControllerActivity(); - final double systemPowerMah = calculatePower(activityCounter); final long systemDurationMs = calculateDuration(activityCounter); + final double systemPowerMah = calculatePowerMah(measuredChargeUC, activityCounter); // Subtract what the apps used, but clamp to 0. final double powerMah = Math.max(0, systemPowerMah - total.powerMah); @@ -152,10 +163,11 @@ public class BluetoothPowerCalculator extends PowerCalculator { private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, PowerAndDuration total) { - final BatteryStats.ControllerActivityCounter activityCounter = - u.getBluetoothControllerActivity(); + + final long measuredChargeUC = u.getBluetoothMeasuredBatteryConsumptionUC(); + final ControllerActivityCounter activityCounter = u.getBluetoothControllerActivity(); final long durationMs = calculateDuration(activityCounter); - final double powerMah = calculatePower(activityCounter); + final double powerMah = calculatePowerMah(measuredChargeUC, activityCounter); app.bluetoothRunningTimeMs = durationMs; app.bluetoothPowerMah = powerMah; @@ -166,7 +178,7 @@ public class BluetoothPowerCalculator extends PowerCalculator { total.powerMah += powerMah; } - private long calculateDuration(BatteryStats.ControllerActivityCounter counter) { + private long calculateDuration(ControllerActivityCounter counter) { if (counter == null) { return 0; } @@ -176,7 +188,11 @@ public class BluetoothPowerCalculator extends PowerCalculator { + counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); } - private double calculatePower(BatteryStats.ControllerActivityCounter counter) { + /** Returns bluetooth power usage based on the best data available. */ + private double calculatePowerMah(long measuredChargeUC, ControllerActivityCounter counter) { + if (measuredChargeUC != POWER_DATA_UNAVAILABLE) { + return uCtoMah(measuredChargeUC); + } if (counter == null) { return 0; } @@ -195,6 +211,11 @@ public class BluetoothPowerCalculator extends PowerCalculator { counter.getRxTimeCounter().getCountLocked(BatteryStats.STATS_SINCE_CHARGED); final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); + return calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs); + } + + /** Returns estimated bluetooth power usage based on usage times. */ + public double calculatePowerMah(long rxTimeMs, long txTimeMs, long idleTimeMs) { return ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) / (1000 * 60 * 60); } diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index 98f613fc1c40..b6bfde709cb8 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -15,6 +15,8 @@ */ package com.android.internal.os; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; + import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.BatteryUsageStats; @@ -79,11 +81,6 @@ public class WifiPowerCalculator extends PowerCalculator { public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, - // so always check this field. - final boolean hasWifiPowerReporting = - mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = builder.getOrCreateSystemBatteryConsumerBuilder( SystemBatteryConsumer.DRAIN_TYPE_WIFI); @@ -97,7 +94,8 @@ public class WifiPowerCalculator extends PowerCalculator { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, - hasWifiPowerReporting); + batteryStats.hasWifiActivityReporting(), + query.shouldForceUsePowerProfileModel()); totalAppDurationMs += powerDurationAndTraffic.durationMs; totalAppPowerMah += powerDurationAndTraffic.powerMah; @@ -115,7 +113,9 @@ public class WifiPowerCalculator extends PowerCalculator { calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, - hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + batteryStats.hasWifiActivityReporting(), + query.shouldForceUsePowerProfileModel(), + totalAppDurationMs, totalAppPowerMah); systemBatteryConsumerBuilder .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, @@ -135,11 +135,6 @@ public class WifiPowerCalculator extends PowerCalculator { public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, - // so always check this field. - final boolean hasWifiPowerReporting = - mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); long totalAppDurationMs = 0; @@ -149,7 +144,7 @@ public class WifiPowerCalculator extends PowerCalculator { final BatterySipper app = sippers.get(i); if (app.drainType == BatterySipper.DrainType.APP) { calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType, - hasWifiPowerReporting); + batteryStats.hasWifiActivityReporting(), /* force use power model*/ false); totalAppDurationMs += powerDurationAndTraffic.durationMs; totalAppPowerMah += powerDurationAndTraffic.powerMah; @@ -169,7 +164,8 @@ public class WifiPowerCalculator extends PowerCalculator { } calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType, - hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + batteryStats.hasWifiActivityReporting(), /* force use power model*/ false, + totalAppDurationMs, totalAppPowerMah); bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs; bs.wifiPowerMah += powerDurationAndTraffic.powerMah; @@ -180,8 +176,9 @@ public class WifiPowerCalculator extends PowerCalculator { } private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u, - long rawRealtimeUs, - int statsType, boolean hasWifiPowerReporting) { + long rawRealtimeUs, int statsType, + boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel) { + powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets( BatteryStats.NETWORK_WIFI_RX_DATA, statsType); @@ -195,7 +192,14 @@ public class WifiPowerCalculator extends PowerCalculator { BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - if (hasWifiPowerReporting) { + final long measuredChargeUC = u.getWifiMeasuredBatteryConsumptionUC(); + final boolean isMeasuredPowerAvailable + = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE; + if (isMeasuredPowerAvailable) { + powerDurationAndTraffic.powerMah = uCtoMah(measuredChargeUC); + } + + if (hasWifiActivityReporting && mHasWifiPowerController) { final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity(); if (counter != null) { final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType); @@ -203,9 +207,10 @@ public class WifiPowerCalculator extends PowerCalculator { final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime; - powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime) - + mTxPowerEstimator.calculatePower(txTime) - + mRxPowerEstimator.calculatePower(rxTime); + if (!isMeasuredPowerAvailable) { + powerDurationAndTraffic.powerMah + = calcPowerFromControllerDataMah(rxTime, txTime, idleTime); + } if (DEBUG && powerDurationAndTraffic.powerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime @@ -214,21 +219,20 @@ public class WifiPowerCalculator extends PowerCalculator { } } } else { - final double wifiPacketPower = ( - powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets) - * mWifiPowerPerPacket; final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; - final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; - long batchScanTimeMs = 0; - for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { - batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; - } - powerDurationAndTraffic.durationMs = wifiRunningTime; - powerDurationAndTraffic.powerMah = wifiPacketPower - + mPowerOnPowerEstimator.calculatePower(wifiRunningTime) - + mScanPowerEstimator.calculatePower(wifiScanTimeMs) - + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs); + + if (!isMeasuredPowerAvailable) { + final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; + long batchTimeMs = 0; + for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { + batchTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; + } + powerDurationAndTraffic.powerMah = calcPowerWithoutControllerDataMah( + powerDurationAndTraffic.wifiRxPackets, + powerDurationAndTraffic.wifiTxPackets, + wifiRunningTime, wifiScanTimeMs, batchTimeMs); + } if (DEBUG && powerDurationAndTraffic.powerMah != 0) { Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge( @@ -238,12 +242,20 @@ public class WifiPowerCalculator extends PowerCalculator { } private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic, - BatteryStats stats, long rawRealtimeUs, - int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs, - double totalAppPowerMah) { + BatteryStats stats, long rawRealtimeUs, int statsType, + boolean hasWifiActivityReporting, boolean shouldForceUsePowerProfileModel, + long totalAppDurationMs, double totalAppPowerMah) { + long totalDurationMs; - double totalPowerMah; - if (hasWifiPowerReporting) { + double totalPowerMah = 0; + + final long measuredChargeUC = stats.getWifiMeasuredBatteryConsumptionUC(); + final boolean isMeasuredPowerAvailable + = !shouldForceUsePowerProfileModel && measuredChargeUC != POWER_DATA_UNAVAILABLE; + if (isMeasuredPowerAvailable) { + totalPowerMah = uCtoMah(measuredChargeUC); + } + if (hasWifiActivityReporting && mHasWifiPowerController) { final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity(); @@ -253,17 +265,19 @@ public class WifiPowerCalculator extends PowerCalculator { totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs; - totalPowerMah = - counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60); - if (totalPowerMah == 0) { - // Some controllers do not report power drain, so we can calculate it here. - totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs) - + mTxPowerEstimator.calculatePower(txTimeMs) - + mRxPowerEstimator.calculatePower(rxTimeMs); + if (!isMeasuredPowerAvailable) { + totalPowerMah = counter.getPowerCounter().getCountLocked(statsType) + / (double) (1000 * 60 * 60); + if (totalPowerMah == 0) { + // Some controllers do not report power drain, so we can calculate it here. + totalPowerMah = calcPowerFromControllerDataMah(rxTimeMs, txTimeMs, idleTimeMs); + } } } else { totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000; - totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs); + if (!isMeasuredPowerAvailable) { + totalPowerMah = calcGlobalPowerWithoutControllerDataMah(totalDurationMs); + } } powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs); @@ -274,6 +288,29 @@ public class WifiPowerCalculator extends PowerCalculator { } } + /** Returns (global or uid) estimated wifi power used using WifiControllerActivity data. */ + public double calcPowerFromControllerDataMah(long rxTimeMs, long txTimeMs, long idleTimeMs) { + return mRxPowerEstimator.calculatePower(rxTimeMs) + + mTxPowerEstimator.calculatePower(txTimeMs) + + mIdlePowerEstimator.calculatePower(idleTimeMs); + } + + /** Returns per-uid estimated wifi power used using non-WifiControllerActivity data. */ + public double calcPowerWithoutControllerDataMah(long rxPackets, long txPackets, + long wifiRunningTimeMs, long wifiScanTimeMs, long wifiBatchScanTimeMs) { + return + (rxPackets + txPackets) * mWifiPowerPerPacket + + mPowerOnPowerEstimator.calculatePower(wifiRunningTimeMs) + + mScanPowerEstimator.calculatePower(wifiScanTimeMs) + + mBatchScanPowerEstimator.calculatePower(wifiBatchScanTimeMs); + + } + + /** Returns global estimated wifi power used using non-WifiControllerActivity data. */ + public double calcGlobalPowerWithoutControllerDataMah(long globalWifiRunningTimeMs) { + return mPowerOnPowerEstimator.calculatePower(globalWifiRunningTimeMs); + } + /** * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. */ diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index e3d5464ca413..845b3e501c08 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -52,7 +52,9 @@ public class MeasuredEnergyStats { public static final int POWER_BUCKET_SCREEN_DOZE = 1; public static final int POWER_BUCKET_SCREEN_OTHER = 2; public static final int POWER_BUCKET_CPU = 3; - public static final int NUMBER_STANDARD_POWER_BUCKETS = 4; // Buckets above this are custom. + public static final int POWER_BUCKET_WIFI = 4; + public static final int POWER_BUCKET_BLUETOOTH = 5; + public static final int NUMBER_STANDARD_POWER_BUCKETS = 6; // Buckets above this are custom. @IntDef(prefix = {"POWER_BUCKET_"}, value = { POWER_BUCKET_UNKNOWN, @@ -60,6 +62,8 @@ public class MeasuredEnergyStats { POWER_BUCKET_SCREEN_DOZE, POWER_BUCKET_SCREEN_OTHER, POWER_BUCKET_CPU, + POWER_BUCKET_WIFI, + POWER_BUCKET_BLUETOOTH, }) @Retention(RetentionPolicy.SOURCE) public @interface StandardPowerBucket { diff --git a/core/java/com/android/internal/view/inline/OWNERS b/core/java/com/android/internal/view/inline/OWNERS new file mode 100644 index 000000000000..edfb2112198a --- /dev/null +++ b/core/java/com/android/internal/view/inline/OWNERS @@ -0,0 +1 @@ +include /core/java/android/view/autofill/OWNERS diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index 8aeb761ffc4d..80ab36ec84cf 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -33,11 +33,15 @@ import android.util.SparseArray; import androidx.test.InstrumentationRegistry; +import com.android.internal.power.MeasuredEnergyStats; + import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.mockito.stubbing.Answer; +import java.util.Arrays; + public class BatteryUsageStatsRule implements TestRule { private final PowerProfile mPowerProfile; private final MockClocks mMockClocks = new MockClocks(); @@ -98,6 +102,16 @@ public class BatteryUsageStatsRule implements TestRule { return this; } + /** Call only after setting the power profile information. */ + public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(int numCustom) { + final boolean[] supportedStandardBuckets = + new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; + Arrays.fill(supportedStandardBuckets, true); + mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, numCustom); + mBatteryStats.informThatAllExternalStatsAreFlushed(); + return this; + } + public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) { mScreenOn = screenOn; return this; diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java index f6aa08bf0645..99cca0b26b1a 100644 --- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.annotation.Nullable; import android.os.BatteryConsumer; +import android.os.BatteryUsageStatsQuery; import android.os.Process; import android.os.SystemBatteryConsumer; @@ -60,7 +61,8 @@ public class BluetoothPowerCalculatorTest { BluetoothPowerCalculator calculator = new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), @@ -91,7 +93,8 @@ public class BluetoothPowerCalculatorTest { BluetoothPowerCalculator calculator = new BluetoothPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index a47c4d832083..26adbe9e7c59 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -46,6 +46,7 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { initTimersAndCounters(); setExternalStatsSyncLocked(new DummyExternalStatsSync()); + informThatAllExternalStatsAreFlushed(); final boolean[] supportedStandardBuckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java index e1005457c289..2e23dc8dbba8 100644 --- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -17,11 +17,14 @@ package com.android.internal.os; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; + import static com.google.common.truth.Truth.assertThat; import android.net.NetworkCapabilities; import android.net.NetworkStats; import android.os.BatteryConsumer; +import android.os.BatteryUsageStatsQuery; import android.os.Process; import android.os.SystemBatteryConsumer; import android.os.UidBatteryConsumer; @@ -50,10 +53,11 @@ public class WifiPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0) .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0) .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0) - .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0); + .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0) + .initMeasuredEnergyStatsLocked(0); - @Test - public void testPowerControllerBasedModel() { + /** Sets up a batterystats object with pre-populated network values. */ + private BatteryStatsImpl setupTestNetworkNumbers() { BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); batteryStats.noteNetworkInterfaceForTransports("wifi", @@ -64,13 +68,25 @@ public class WifiPowerCalculatorTest { .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); mStatsRule.setNetworkStats(networkStats); - WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000, + return batteryStats; + } + + /** Sets up an WifiActivityEnergyInfo for ActivityController-model-based tests. */ + private WifiActivityEnergyInfo setupPowerControllerBasedModelEnergyNumbersInfo() { + return new WifiActivityEnergyInfo(10000, WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000); + } + + @Test + public void testPowerControllerBasedModel_nonMeasured() { + final BatteryStatsImpl batteryStats = setupTestNetworkNumbers(); + final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo(); - batteryStats.updateWifiState(energyInfo, 1000, 1000); + batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000); WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) @@ -87,30 +103,54 @@ public class WifiPowerCalculatorTest { } @Test - public void testTimerBasedModel() { - BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + public void testPowerControllerBasedModel_measured() { + final BatteryStatsImpl batteryStats = setupTestNetworkNumbers(); + final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo(); - batteryStats.noteNetworkInterfaceForTransports("wifi", - new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000); - NetworkStats networkStats = new NetworkStats(10000, 1) - .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100) - .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); - mStatsRule.setNetworkStats(networkStats); + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1423); + /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */ + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.2214666 / (0.2214666 + 0.645200) * 1_000_000 / 3600000); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(5577); + /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */ + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.645200 / (0.2214666 + 0.645200) * 1_000_000 / 3600000); + } + + /** Sets up batterystats object with prepopulated network & timer data for Timer-model tests. */ + private BatteryStatsImpl setupTimerBasedModelTestNumbers() { + final BatteryStatsImpl batteryStats = setupTestNetworkNumbers(); batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000); batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000); batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000); batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000); batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222); batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444); + return batteryStats; + } + + @Test + public void testTimerBasedModel_nonMeasured() { + final BatteryStatsImpl batteryStats = setupTimerBasedModelTestNumbers(); // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively // on the packet counts. - batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000); + batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000); WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) @@ -125,4 +165,31 @@ public class WifiPowerCalculatorTest { assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.8759216); } + + @Test + public void testTimerBasedModel_measured() { + final BatteryStatsImpl batteryStats = setupTimerBasedModelTestNumbers(); + + // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively + // on the packet counts. + batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000); + + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1000); + /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */ + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(2222); + /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */ + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8759216 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000); + } } diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 971a53a8b2dc..e58f31fd15eb 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -126,7 +126,7 @@ bool Properties::load() { SkAndroidFrameworkTraceUtil::setEnableTracing( base::GetBoolProperty(PROPERTY_SKIA_ATRACE_ENABLED, false)); - runningInEmulator = base::GetBoolProperty(PROPERTY_QEMU_KERNEL, false); + runningInEmulator = base::GetBoolProperty(PROPERTY_IS_EMULATOR, false); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index dcb79babad24..42aa87b492e2 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -160,7 +160,7 @@ enum DebugLevel { /** * Property for whether this is running in the emulator. */ -#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu" +#define PROPERTY_IS_EMULATOR "ro.boot.qemu" /////////////////////////////////////////////////////////////////////////////// // Misc diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 49c2b39f8904..309d71cfd062 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -358,14 +358,14 @@ void StreamManager::addToActiveQueue_l(Stream *stream) { void StreamManager::run(int32_t id) { ALOGV("%s(%d) entering", __func__, id); - int64_t waitTimeNs = kWaitTimeBeforeCloseNs; + int64_t waitTimeNs = 0; // on thread start, mRestartStreams can be non-empty. std::unique_lock lock(mStreamManagerLock); while (!mQuit) { - if (mRestartStreams.empty()) { // on thread start, mRestartStreams can be non-empty. + if (waitTimeNs > 0) { mStreamManagerCondition.wait_for( lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs)); } - ALOGV("%s(%d) awake", __func__, id); + ALOGV("%s(%d) awake lock waitTimeNs:%lld", __func__, id, (long long)waitTimeNs); sanityCheckQueue_l(); diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index b9bcddba5d0a..cc95a7cc2413 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -29,6 +29,11 @@ package android.net { field public static final int TRANSPORT_TEST = 7; // 0x7 } + public class ParseException extends java.lang.RuntimeException { + ctor public ParseException(@NonNull String); + ctor public ParseException(@NonNull String, @NonNull Throwable); + } + public final class TcpRepairWindow { ctor public TcpRepairWindow(int, int, int, int, int, int); field public final int maxWindow; diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 6d6a5547930c..7bfc9702ba6a 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -18,8 +18,8 @@ package android.net; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; import static android.net.NetworkRequest.Type.LISTEN; +import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST; import static android.net.NetworkRequest.Type.REQUEST; -import static android.net.NetworkRequest.Type.TRACK_BEST; import static android.net.NetworkRequest.Type.TRACK_DEFAULT; import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT; import static android.net.QosCallback.QosCallbackRegistrationException; @@ -3213,10 +3213,6 @@ public class ConnectivityManager { } } - // TODO : remove this method. It is a stopgap measure to help sheperding a number - // of dependent changes that would conflict throughout the automerger graph. Having this - // temporarily helps with the process of going through with all these dependent changes across - // the entire tree. /** * @hide * Register a NetworkAgent with ConnectivityService. @@ -3226,20 +3222,8 @@ public class ConnectivityManager { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, int score, NetworkAgentConfig config) { - return registerNetworkAgent(na, ni, lp, nc, score, config, NetworkProvider.ID_NONE); - } - - /** - * @hide - * Register a NetworkAgent with ConnectivityService. - * @return Network corresponding to NetworkAgent. - */ - @RequiresPermission(anyOf = { - NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, - android.Manifest.permission.NETWORK_FACTORY}) - public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) { + NetworkCapabilities nc, @NonNull NetworkScore score, NetworkAgentConfig config, + int providerId) { try { return mService.registerNetworkAgent(na, ni, lp, nc, score, config, providerId); } catch (RemoteException e) { @@ -4265,7 +4249,7 @@ public class ConnectivityManager { @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { final NetworkCapabilities nc = request.networkCapabilities; final CallbackHandler cbHandler = new CallbackHandler(handler); - sendRequestForNetwork(nc, networkCallback, 0, TRACK_BEST, TYPE_NONE, cbHandler); + sendRequestForNetwork(nc, networkCallback, 0, LISTEN_FOR_BEST, TYPE_NONE, cbHandler); } /** @@ -5121,9 +5105,9 @@ public class ConnectivityManager { } // The first network ID of IPSec tunnel interface. - private static final int TUN_INTF_NETID_START = 0xFC00; + private static final int TUN_INTF_NETID_START = 0xFC00; // 0xFC00 = 64512 // The network ID range of IPSec tunnel interface. - private static final int TUN_INTF_NETID_RANGE = 0x0400; + private static final int TUN_INTF_NETID_RANGE = 0x0400; // 0x0400 = 1024 /** * Get the network ID range reserved for IPSec tunnel interfaces. diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index f9393e315b83..1bbf1a95fcca 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -30,6 +30,7 @@ import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkState; import android.net.NetworkStateSnapshot; import android.net.OemNetworkPreferences; @@ -138,7 +139,7 @@ interface IConnectivityManager void declareNetworkRequestUnfulfillable(in NetworkRequest request); Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp, - in NetworkCapabilities nc, int score, in NetworkAgentConfig config, + in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config, in int factorySerialNumber); NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType, diff --git a/packages/Connectivity/framework/src/android/net/Network.java b/packages/Connectivity/framework/src/android/net/Network.java index 46141e0d0c1e..7245db3b17db 100644 --- a/packages/Connectivity/framework/src/android/net/Network.java +++ b/packages/Connectivity/framework/src/android/net/Network.java @@ -30,10 +30,10 @@ import android.system.OsConstants; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; -import com.android.okhttp.internalandroidapi.Dns; -import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory; import libcore.io.IoUtils; +import libcore.net.http.Dns; +import libcore.net.http.HttpURLConnectionFactory; import java.io.FileDescriptor; import java.io.IOException; @@ -299,7 +299,7 @@ public class Network implements Parcelable { // Set configuration on the HttpURLConnectionFactory that will be good for all // connections created by this Network. Configuration that might vary is left // until openConnection() and passed as arguments. - HttpURLConnectionFactory urlConnectionFactory = new HttpURLConnectionFactory(); + HttpURLConnectionFactory urlConnectionFactory = HttpURLConnectionFactory.createInstance(); urlConnectionFactory.setDns(dnsLookup); // Let traffic go via dnsLookup // A private connection pool just for this Network. urlConnectionFactory.setNewConnectionPool(httpMaxConnections, diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java index 27aa15d1e1e4..b3ab0ee8bd3c 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java @@ -371,6 +371,14 @@ public abstract class NetworkAgent { return ni; } + // Temporary backward compatibility constructor + public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag, + @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, + @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) { + this(context, looper, logTag, nc, lp, + new NetworkScore.Builder().setLegacyInt(score).build(), config, provider); + } + /** * Create a new network agent. * @param context a {@link Context} to get system services from. @@ -382,10 +390,12 @@ public abstract class NetworkAgent { * @param score the initial score of this network. Update with sendNetworkScore. * @param config an immutable {@link NetworkAgentConfig} for this agent. * @param provider the {@link NetworkProvider} managing this agent. + * @hide TODO : unhide when impl is complete */ public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag, - @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, - @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) { + @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, + @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, + @Nullable NetworkProvider provider) { this(looper, context, logTag, nc, lp, score, config, provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(), getLegacyNetworkInfo(config)); @@ -395,12 +405,12 @@ public abstract class NetworkAgent { public final Context context; public final NetworkCapabilities capabilities; public final LinkProperties properties; - public final int score; + public final NetworkScore score; public final NetworkAgentConfig config; public final NetworkInfo info; InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities, - @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config, - @NonNull NetworkInfo info) { + @NonNull LinkProperties properties, @NonNull NetworkScore score, + @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) { this.context = context; this.capabilities = capabilities; this.properties = properties; @@ -412,8 +422,9 @@ public abstract class NetworkAgent { private volatile InitialConfiguration mInitialConfiguration; private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag, - @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score, - @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni) { + @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, + @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId, + @NonNull NetworkInfo ni) { mHandler = new NetworkAgentHandler(looper); LOG_TAG = logTag; mNetworkInfo = new NetworkInfo(ni); @@ -875,13 +886,22 @@ public abstract class NetworkAgent { /** * Must be called by the agent to update the score of this network. * + * @param score the new score. + * @hide TODO : unhide when impl is complete + */ + public final void sendNetworkScore(@NonNull NetworkScore score) { + Objects.requireNonNull(score); + queueOrSendMessage(reg -> reg.sendScore(score)); + } + + /** + * Must be called by the agent to update the score of this network. + * * @param score the new score, between 0 and 99. + * deprecated use sendNetworkScore(NetworkScore) TODO : remove in S. */ public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) { - if (score < 0) { - throw new IllegalArgumentException("Score must be >= 0"); - } - queueOrSendMessage(reg -> reg.sendScore(score)); + sendNetworkScore(new NetworkScore.Builder().setLegacyInt(score).build()); } /** diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index 3fd95ee58df2..dbe3ecc4d775 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -140,7 +140,7 @@ public class NetworkRequest implements Parcelable { REQUEST, BACKGROUND_REQUEST, TRACK_SYSTEM_DEFAULT, - TRACK_BEST, + LISTEN_FOR_BEST, }; /** @@ -514,6 +514,15 @@ public class NetworkRequest implements Parcelable { } /** + * Returns true iff. this NetworkRequest is of type LISTEN_FOR_BEST. + * + * @hide + */ + public boolean isListenForBest() { + return type == Type.LISTEN_FOR_BEST; + } + + /** * Returns true iff. the contained NetworkRequest is one that: * * - should be associated with at most one satisfying network diff --git a/packages/Connectivity/framework/src/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java index bcfdd7ef09cc..ca6d012dfe7c 100644 --- a/packages/Connectivity/framework/src/android/net/ParseException.java +++ b/packages/Connectivity/framework/src/android/net/ParseException.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.SystemApi; /** * Thrown when parsing failed. @@ -25,12 +26,16 @@ import android.annotation.NonNull; public class ParseException extends RuntimeException { public String response; - ParseException(@NonNull String response) { + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public ParseException(@NonNull String response) { super(response); this.response = response; } - ParseException(@NonNull String response, @NonNull Throwable cause) { + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public ParseException(@NonNull String response, @NonNull Throwable cause) { super(response, cause); this.response = response; } diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl index f0193db5c2e2..18d26a7e4baf 100644 --- a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl +++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl @@ -19,11 +19,12 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; +import android.net.NetworkScore; import android.net.QosSession; import android.telephony.data.EpsBearerQosSessionAttributes; /** - * Interface for NetworkAgents to send network network properties. + * Interface for NetworkAgents to send network properties. * @hide */ oneway interface INetworkAgentRegistry { @@ -31,7 +32,7 @@ oneway interface INetworkAgentRegistry { void sendLinkProperties(in LinkProperties lp); // TODO: consider replacing this by "markConnected()" and removing void sendNetworkInfo(in NetworkInfo info); - void sendScore(int score); + void sendScore(in NetworkScore score); void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial); void sendSocketKeepaliveEvent(int slot, int reason); void sendUnderlyingNetworks(in @nullable List<Network> networks); diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 9c0c80bff3df..bf4242f542da 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -23,7 +23,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.media.AudioManager; -import android.net.ConnectivityManager; import android.net.NetworkCapabilities; import android.net.TetheringManager; import android.net.vcn.VcnTransportInfo; @@ -37,6 +36,7 @@ import android.provider.Settings; import android.telephony.AccessNetworkConstants; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; +import android.telephony.TelephonyManager; import androidx.annotation.NonNull; import androidx.core.graphics.drawable.RoundedBitmapDrawable; @@ -439,8 +439,7 @@ public class Utils { } public static boolean isWifiOnly(Context context) { - return !context.getSystemService(ConnectivityManager.class) - .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + return !context.getSystemService(TelephonyManager.class).isDataCapable(); } /** Returns if the automatic storage management feature is turned on or not. **/ diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java index 092cbf3c7c12..60bcf37304a5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java @@ -16,7 +16,6 @@ package com.android.settingslib.net; -import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; @@ -59,7 +58,6 @@ public class DataUsageController { PERIOD_BUILDER, Locale.getDefault()); private final Context mContext; - private final ConnectivityManager mConnectivityManager; private final INetworkStatsService mStatsService; private final NetworkPolicyManager mPolicyManager; private final NetworkStatsManager mNetworkStatsManager; @@ -71,7 +69,6 @@ public class DataUsageController { public DataUsageController(Context context) { mContext = context; - mConnectivityManager = ConnectivityManager.from(context); mStatsService = INetworkStatsService.Stub.asInterface( ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); mPolicyManager = NetworkPolicyManager.from(mContext); @@ -236,7 +233,7 @@ public class DataUsageController { public boolean isMobileDataSupported() { // require both supported network and ready SIM - return mConnectivityManager.isNetworkSupported(TYPE_MOBILE) + return getTelephonyManager().isDataCapable() && getTelephonyManager().getSimState() == SIM_STATE_READY; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 6568bffddecc..268603fa8b0d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -2445,8 +2445,8 @@ class DatabaseHelper extends SQLiteOpenHelper { R.bool.def_auto_time_zone); // Sync timezone to NITZ loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, - ("1".equals(SystemProperties.get("ro.kernel.qemu")) || - res.getBoolean(R.bool.def_stay_on_while_plugged_in)) + ("1".equals(SystemProperties.get("ro.boot.qemu")) + || res.getBoolean(R.bool.def_stay_on_while_plugged_in)) ? 1 : 0); loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY, diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 688b33e0f685..f666490d13aa 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -55,6 +55,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; +import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST; import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired; import static android.os.Process.INVALID_UID; import static android.os.Process.VPN_UID; @@ -117,6 +118,7 @@ import android.net.NetworkMonitorManager; import android.net.NetworkPolicyManager; import android.net.NetworkProvider; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; @@ -1273,12 +1275,18 @@ public class ConnectivityService extends IConnectivityManager.Stub mDnsManager = new DnsManager(mContext, mDnsResolver); registerPrivateDnsSettingsCallbacks(); + // This NAI is a sentinel used to offer no service to apps that are on a multi-layer + // request that doesn't allow fallback to the default network. It should never be visible + // to apps. As such, it's not in the list of NAIs and doesn't need many of the normal + // arguments like the handler or the DnsResolver. + // TODO : remove this ; it is probably better handled with a sentinel request. mNoServiceNetwork = new NetworkAgentInfo(null, new Network(NO_SERVICE_NET_ID), new NetworkInfo(TYPE_NONE, 0, "", ""), - new LinkProperties(), new NetworkCapabilities(), 0, mContext, - null, new NetworkAgentConfig(), this, null, - null, 0, INVALID_UID, mQosCallbackTracker, mDeps); + new LinkProperties(), new NetworkCapabilities(), + new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null, + new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker, + mDeps); } private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { @@ -2949,7 +2957,7 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: { - updateNetworkScore(nai, msg.arg1); + updateNetworkScore(nai, (NetworkScore) arg.second); break; } case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: { @@ -3656,6 +3664,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("REGISTER " + nri); for (final NetworkRequest req : nri.mRequests) { mNetworkRequests.put(req, nri); + // TODO: Consider update signal strength for other types. if (req.isListen()) { for (final NetworkAgentInfo network : mNetworkAgentInfos) { if (req.networkCapabilities.hasSignalStrength() @@ -3748,18 +3757,19 @@ public class ConnectivityService extends IConnectivityManager.Stub // listen requests won't keep up a network satisfying it. If this is not a multilayer // request, return immediately. For multilayer requests, check to see if any of the // multilayer requests may have a potential satisfier. - if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) { + if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen() + || nri.mRequests.get(0).isListenForBest())) { return false; } for (final NetworkRequest req : nri.mRequests) { // This multilayer listen request is satisfied therefore no further requests need to be // evaluated deeming this network not a potential satisfier. - if (req.isListen() && nri.getActiveRequest() == req) { + if ((req.isListen() || req.isListenForBest()) && nri.getActiveRequest() == req) { return false; } // As non-multilayer listen requests have already returned, the below would only happen // for a multilayer request therefore continue to the next request if available. - if (req.isListen()) { + if (req.isListen() || req.isListenForBest()) { continue; } // If this Network is already the highest scoring Network for a request, or if @@ -3799,8 +3809,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ? mNetworkRequests.get(request) : getNriForAppRequest(request); if (nri != null) { - if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid - && nri.mUid != callingUid) { + if (Process.SYSTEM_UID != callingUid && nri.mUid != callingUid) { log(String.format("UID %d attempted to %s for unowned request %s", callingUid, requestedOperation, nri)); return null; @@ -5543,8 +5552,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // request if the app changes network state. http://b/29964605 enforceMeteredApnPolicy(networkCapabilities); break; - case TRACK_BEST: - throw new UnsupportedOperationException("Not implemented yet"); + case LISTEN_FOR_BEST: + enforceAccessPermission(); + networkCapabilities = new NetworkCapabilities(networkCapabilities); + break; default: throw new IllegalArgumentException("Unsupported request type " + reqType); } @@ -5552,11 +5563,17 @@ public class ConnectivityService extends IConnectivityManager.Stub ensureSufficientPermissionsForRequest(networkCapabilities, Binder.getCallingPid(), callingUid, callingPackageName); - // Set the UID range for this request to the single UID of the requester, or to an empty - // set of UIDs if the caller has the appropriate permission and UIDs have not been set. + // Enforce FOREGROUND if the caller does not have permission to use background network. + if (reqType == LISTEN_FOR_BEST) { + restrictBackgroundRequestForCaller(networkCapabilities); + } + + // Set the UID range for this request to the single UID of the requester, unless the + // requester has the permission to specify other UIDs. // This will overwrite any allowed UIDs in the requested capabilities. Though there // are no visible methods to set the UIDs, an app could use reflection to try and get // networks for other apps so it's essential that the UIDs are overwritten. + // Also set the requester UID and package name in the request. restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities, callingUid, callingPackageName); @@ -6083,20 +6100,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return nai == getDefaultNetwork(); } - // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent - // changes that would conflict throughout the automerger graph. Having this method temporarily - // helps with the process of going through with all these dependent changes across the entire - // tree. - /** - * Register a new agent. {@see #registerNetworkAgent} below. - */ - public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo, - LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkAgentConfig networkAgentConfig) { - return registerNetworkAgent(na, networkInfo, linkProperties, networkCapabilities, - currentScore, networkAgentConfig, NetworkProvider.ID_NONE); - } - /** * Register a new agent with ConnectivityService to handle a network. * @@ -6107,7 +6110,7 @@ public class ConnectivityService extends IConnectivityManager.Stub * later : see {@link #updateLinkProperties}. * @param networkCapabilities the initial capabilites of this network. They can be updated * later : see {@link #updateCapabilities}. - * @param currentScore the initial score of the network. See + * @param initialScore the initial score of the network. See * {@link NetworkAgentInfo#getCurrentScore}. * @param networkAgentConfig metadata about the network. This is never updated. * @param providerId the ID of the provider owning this NetworkAgent. @@ -6115,10 +6118,12 @@ public class ConnectivityService extends IConnectivityManager.Stub */ public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) { + @NonNull NetworkScore initialScore, NetworkAgentConfig networkAgentConfig, + int providerId) { Objects.requireNonNull(networkInfo, "networkInfo must not be null"); Objects.requireNonNull(linkProperties, "linkProperties must not be null"); Objects.requireNonNull(networkCapabilities, "networkCapabilities must not be null"); + Objects.requireNonNull(initialScore, "initialScore must not be null"); Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null"); if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS); @@ -6130,7 +6135,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final long token = Binder.clearCallingIdentity(); try { return registerNetworkAgentInternal(na, networkInfo, linkProperties, - networkCapabilities, currentScore, networkAgentConfig, providerId, uid); + networkCapabilities, initialScore, networkAgentConfig, providerId, uid); } finally { Binder.restoreCallingIdentity(token); } @@ -6138,7 +6143,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) { + NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId, + int uid) { if (networkCapabilities.hasTransport(TRANSPORT_TEST)) { // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never @@ -7862,7 +7868,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final int score) { + private void updateNetworkScore(@NonNull final NetworkAgentInfo nai, final NetworkScore score) { if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + score); nai.setScore(score); rematchAllNetworksAndRequests(); diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index e2086b01ec13..e74c936af02d 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -589,9 +589,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { Slog.w(TAG, "exception reading modem stats: " + e.getCause()); } - final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDelta; + final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas; if (mMeasuredEnergySnapshot == null || futureECRs == null) { - measuredEnergyDelta = null; + measuredEnergyDeltas = null; } else { final int voltageMv; synchronized (mStats) { @@ -610,7 +610,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { ecrs = null; } - measuredEnergyDelta = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv); + measuredEnergyDeltas = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv); } final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -633,10 +633,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } final long[] cpuClusterChargeUC; - if (measuredEnergyDelta == null) { + if (measuredEnergyDeltas == null) { cpuClusterChargeUC = null; } else { - cpuClusterChargeUC = measuredEnergyDelta.cpuClusterChargeUC; + cpuClusterChargeUC = measuredEnergyDeltas.cpuClusterChargeUC; } mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC); } @@ -650,9 +650,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateRpmStatsLocked(elapsedRealtimeUs); } - // Inform mStats about each applicable measured energy. - if (measuredEnergyDelta != null) { - final long displayChargeUC = measuredEnergyDelta.displayChargeUC; + // Inform mStats about each applicable measured energy (unless addressed elsewhere). + if (measuredEnergyDeltas != null) { + final long displayChargeUC = measuredEnergyDeltas.displayChargeUC; if (displayChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) { // If updating, pass in what BatteryExternalStatsWorker thinks screenState is. mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC, screenState, @@ -660,19 +660,23 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } } // Inform mStats about each applicable custom energy bucket. - if (measuredEnergyDelta != null - && measuredEnergyDelta.otherTotalChargeUC != null) { + if (measuredEnergyDeltas != null + && measuredEnergyDeltas.otherTotalChargeUC != null) { // Iterate over the custom (EnergyConsumerType.OTHER) ordinals. - for (int ord = 0; ord < measuredEnergyDelta.otherTotalChargeUC.length; ord++) { - long totalEnergy = measuredEnergyDelta.otherTotalChargeUC[ord]; - SparseLongArray uidEnergies = measuredEnergyDelta.otherUidChargesUC[ord]; + for (int ord = 0; ord < measuredEnergyDeltas.otherTotalChargeUC.length; ord++) { + long totalEnergy = measuredEnergyDeltas.otherTotalChargeUC[ord]; + SparseLongArray uidEnergies = measuredEnergyDeltas.otherUidChargesUC[ord]; mStats.updateCustomMeasuredEnergyStatsLocked(ord, totalEnergy, uidEnergies); } } if (bluetoothInfo != null) { if (bluetoothInfo.isValid()) { - mStats.updateBluetoothStateLocked(bluetoothInfo, elapsedRealtime, uptime); + final long btChargeUC = measuredEnergyDeltas != null + ? measuredEnergyDeltas.bluetoothChargeUC + : MeasuredEnergySnapshot.UNAVAILABLE; + mStats.updateBluetoothStateLocked(bluetoothInfo, + btChargeUC, elapsedRealtime, uptime); } else { Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo); } @@ -684,10 +688,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if (wifiInfo != null) { if (wifiInfo.isValid()) { - // TODO: wifiEnergyDelta = measuredEnergyDelta.consumerTypeEnergyUJ - // .get(EnergyConsumerType.WIFI, MeasuredEnergySnapshot.UNAVAILABLE) - mStats.updateWifiState(extractDeltaLocked(wifiInfo) - /*, TODO: wifiEnergyDelta */, elapsedRealtime, uptime); + final long wifiChargeUC = measuredEnergyDeltas != null ? + measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE; + mStats.updateWifiState( + extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime); } else { Slog.w(TAG, "wifi info is invalid: " + wifiInfo); } @@ -820,13 +824,19 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { for (int idx = 0; idx < size; idx++) { final EnergyConsumer consumer = idToConsumer.valueAt(idx); switch (consumer.type) { + case EnergyConsumerType.BLUETOOTH: + buckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH] = true; + break; + case EnergyConsumerType.CPU_CLUSTER: + buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true; + break; case EnergyConsumerType.DISPLAY: buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON] = true; buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE] = true; buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_OTHER] = true; break; - case EnergyConsumerType.CPU_CLUSTER: - buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true; + case EnergyConsumerType.WIFI: + buckets[MeasuredEnergyStats.POWER_BUCKET_WIFI] = true; break; } } @@ -864,13 +874,18 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } final IntArray energyConsumerIds = new IntArray(); + if ((flags & UPDATE_BT) != 0) { + addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.BLUETOOTH); + } if ((flags & UPDATE_CPU) != 0) { addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.CPU_CLUSTER); } if ((flags & UPDATE_DISPLAY) != 0) { addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.DISPLAY); } - // TODO: Wifi, Bluetooth, etc., go here + if ((flags & UPDATE_WIFI) != 0) { + addEnergyConsumerIdLocked(energyConsumerIds, EnergyConsumerType.WIFI); + } if (energyConsumerIds.size() == 0) { return null; diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 6b9fc0718879..c3f97adbd9c3 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -17,6 +17,7 @@ package com.android.server.am; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; import android.annotation.NonNull; import android.bluetooth.BluetoothActivityEnergyInfo; @@ -1927,7 +1928,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { - mStats.updateWifiState(info, elapsedRealtime, uptime); + mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime); }); } } @@ -1945,7 +1946,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.updateBluetoothStateLocked(info, elapsedRealtime, uptime); + mStats.updateBluetoothStateLocked( + info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime); } }); } diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java index 9b2ca136bdfb..4c9ab63a100b 100644 --- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java +++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java @@ -41,7 +41,7 @@ public class MeasuredEnergySnapshot { private static final int MILLIVOLTS_PER_VOLT = 1000; - public static final long UNAVAILABLE = -1L; + public static final long UNAVAILABLE = android.os.BatteryStats.POWER_DATA_UNAVAILABLE; /** Map of {@link EnergyConsumer#id} to its corresponding {@link EnergyConsumer}. */ private final SparseArray<EnergyConsumer> mEnergyConsumers; @@ -109,12 +109,18 @@ public class MeasuredEnergySnapshot { /** Class for returning the relevant data calculated from the measured energy delta */ static class MeasuredEnergyDeltaData { - /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */ - public long displayChargeUC = UNAVAILABLE; + /** The chargeUC for {@link EnergyConsumerType#BLUETOOTH}. */ + public long bluetoothChargeUC = UNAVAILABLE; /** The chargeUC for {@link EnergyConsumerType#CPU_CLUSTER}s. */ public long[] cpuClusterChargeUC = null; + /** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */ + public long displayChargeUC = UNAVAILABLE; + + /** The chargeUC for {@link EnergyConsumerType#WIFI}. */ + public long wifiChargeUC = UNAVAILABLE; + /** Map of {@link EnergyConsumerType#OTHER} ordinals to their total chargeUC. */ public @Nullable long[] otherTotalChargeUC = null; @@ -196,8 +202,8 @@ public class MeasuredEnergySnapshot { final long deltaChargeUC = calculateChargeConsumedUC(deltaUJ, avgVoltageMV); switch (type) { - case EnergyConsumerType.DISPLAY: - output.displayChargeUC = deltaChargeUC; + case EnergyConsumerType.BLUETOOTH: + output.bluetoothChargeUC = deltaChargeUC; break; case EnergyConsumerType.CPU_CLUSTER: @@ -207,6 +213,14 @@ public class MeasuredEnergySnapshot { output.cpuClusterChargeUC[ordinal] = deltaChargeUC; break; + case EnergyConsumerType.DISPLAY: + output.displayChargeUC = deltaChargeUC; + break; + + case EnergyConsumerType.WIFI: + output.wifiChargeUC = deltaChargeUC; + break; + case EnergyConsumerType.OTHER: if (output.otherTotalChargeUC == null) { output.otherTotalChargeUC = new long[getNumOtherOrdinals()]; @@ -215,6 +229,7 @@ public class MeasuredEnergySnapshot { output.otherTotalChargeUC[ordinal] = deltaChargeUC; output.otherUidChargesUC[ordinal] = otherUidCharges; break; + default: Slog.w(TAG, "Ignoring consumer " + consumer.name + " of unknown type " + type); diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 5bf15dc70ff9..71565301e3ed 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -80,6 +80,7 @@ import com.android.server.wm.WindowManagerInternal; import java.io.IOException; import java.io.RandomAccessFile; +import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -95,21 +96,20 @@ class HostClipboardMonitor implements Runnable { private static final String PIPE_NAME = "pipe:clipboard"; private static final String PIPE_DEVICE = "/dev/qemu_pipe"; + private static byte[] createOpenHandshake() { + // String.getBytes doesn't include the null terminator, + // but the QEMU pipe device requires the pipe service name + // to be null-terminated. + + final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1); + bits[PIPE_NAME.length()] = 0; + return bits; + } + private void openPipe() { try { - // String.getBytes doesn't include the null terminator, - // but the QEMU pipe device requires the pipe service name - // to be null-terminated. - byte[] b = new byte[PIPE_NAME.length() + 1]; - b[PIPE_NAME.length()] = 0; - System.arraycopy( - PIPE_NAME.getBytes(), - 0, - b, - 0, - PIPE_NAME.length()); mPipe = new RandomAccessFile(PIPE_DEVICE, "rw"); - mPipe.write(b); + mPipe.write(createOpenHandshake()); } catch (IOException e) { try { if (mPipe != null) mPipe.close(); @@ -173,7 +173,7 @@ public class ClipboardService extends SystemService { private static final String TAG = "ClipboardService"; private static final boolean IS_EMULATOR = - SystemProperties.getBoolean("ro.kernel.qemu", false); + SystemProperties.getBoolean("ro.boot.qemu", false); // DeviceConfig properties private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications"; diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 803cc9d31c35..e44dcf5975f1 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -35,6 +35,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMonitorManager; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkStateSnapshot; import android.net.QosCallbackException; import android.net.QosFilter; @@ -302,8 +303,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // validated). private boolean mInactive; - // This represents the quality of the network with no clear scale. - private int mScore; + // This represents the quality of the network. + private NetworkScore mScore; // The list of NetworkRequests being satisfied by this Network. private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>(); @@ -338,7 +339,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private final QosCallbackTracker mQosCallbackTracker; public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info, - @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context, + @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, + @NonNull NetworkScore score, Context context, Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid, QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) { @@ -603,9 +605,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } @Override - public void sendScore(int score) { - mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, score, 0, - new Pair<>(NetworkAgentInfo.this, null)).sendToTarget(); + public void sendScore(@NonNull final NetworkScore score) { + mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, + new Pair<>(NetworkAgentInfo.this, score)).sendToTarget(); } @Override @@ -717,6 +719,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { break; case LISTEN: + case LISTEN_FOR_BEST: case TRACK_DEFAULT: case TRACK_SYSTEM_DEFAULT: break; @@ -857,7 +860,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE; } - int score = mScore; + int score = mScore.getLegacyInt(); if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) { score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY; } @@ -886,7 +889,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return getCurrentScore(true); } - public void setScore(final int score) { + public void setScore(final NetworkScore score) { mScore = score; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d5a9e3c0d4f8..e58836659189 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -10635,17 +10635,17 @@ public class NotificationManagerService extends SystemService { return true; } } - String toastMessage = "Indirect activity start from " + packageName; String logcatMessage = "Indirect notification activity start (trampoline) from " + packageName; - + // Call to toast() method is posted to mHandler below to offload PM lookup from the + // activity start path if (CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid)) { - toast(toastMessage + " blocked."); + mHandler.post(() -> toast(packageName, uid, /* blocked */ true)); Slog.e(TAG, logcatMessage + " blocked"); return false; } else { if (mPackagesShown.add(packageName)) { - toast(toastMessage + ". This will be blocked in S."); + mHandler.post(() -> toast(packageName, uid, /* blocked */ false)); } Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons"); return true; @@ -10661,10 +10661,19 @@ public class NotificationManagerService extends SystemService { && !CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid); } - private void toast(String message) { - mUiHandler.post(() -> - Toast.makeText(getUiContext(), message + "\nSee g.co/dev/trampolines.", - Toast.LENGTH_LONG).show()); + private void toast(String packageName, int uid, boolean blocked) { + final CharSequence label; + try { + label = mPackageManagerClient.getApplicationLabel( + mPackageManager.getApplicationInfo(packageName, 0, + UserHandle.getUserId(uid))); + } catch (RemoteException e) { + Slog.e(TAG, "Unexpected exception obtaining app label from PackageManager", e); + return; + } + mUiHandler.post(() -> Toast.makeText(getUiContext(), + label + " launch " + (blocked ? "blocked" : "will be blocked") + + "\ng.co/dev/trampolines", Toast.LENGTH_LONG).show()); } } } diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp index a73f6c6d8c2d..6cb4a63a5636 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -123,16 +123,9 @@ static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* } static void android_server_SystemServer_fdtrackAbort(JNIEnv*, jobject) { - raise(BIONIC_SIGNAL_FDTRACK); - - // Wait for a bit to allow fdtrack to dump backtraces to logcat. - std::this_thread::sleep_for(5s); - - // Abort on a different thread to avoid ART dumping runtime stacks. - std::thread([]() { - LOG_ALWAYS_FATAL("b/140703823: aborting due to fd leak: check logs for fd " - "backtraces"); - }).join(); + sigval val; + val.sival_int = 1; + sigqueue(getpid(), BIONIC_SIGNAL_FDTRACK, val); } static jlong android_server_SystemServer_startIncrementalService(JNIEnv* env, jclass klass, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4e23609bd774..592952354b8f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1327,7 +1327,7 @@ public final class SystemServer implements Dumpable { false); boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false); - boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); + boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1"); boolean isWatch = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_WATCH); diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java index 8d54ead75761..73a2febf72aa 100644 --- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java @@ -17,8 +17,10 @@ package com.android.server.am; import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_BT; import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_CPU; import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_DISPLAY; +import static com.android.internal.os.BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -39,6 +41,7 @@ import android.util.SparseArray; import androidx.test.InstrumentationRegistry; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.PowerProfile; import org.junit.Before; import org.junit.Test; @@ -61,7 +64,7 @@ public class BatteryExternalStatsWorkerTest { public void setUp() { final Context context = InstrumentationRegistry.getContext(); - mBatteryStatsImpl = new TestBatteryStatsImpl(); + mBatteryStatsImpl = new TestBatteryStatsImpl(context); mPowerStatsInternal = new TestPowerStatsInternal(); mBatteryExternalStatsWorker = new BatteryExternalStatsWorker(new TestInjector(context), mBatteryStatsImpl); @@ -72,13 +75,24 @@ public class BatteryExternalStatsWorkerTest { final int numCpuClusters = 4; final int numOther = 3; - final IntArray tempAllIds = new IntArray(); // Add some energy consumers used by BatteryExternalStatsWorker. + final IntArray tempAllIds = new IntArray(); + final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0, "display"); tempAllIds.add(displayId); mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345); + final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0, + "wifi"); + tempAllIds.add(wifiId); + mPowerStatsInternal.incrementEnergyConsumption(wifiId, 23456); + + final int btId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.BLUETOOTH, 0, + "bt"); + tempAllIds.add(btId); + mPowerStatsInternal.incrementEnergyConsumption(btId, 34567); + final int[] cpuClusterIds = new int[numCpuClusters]; for (int i = 0; i < numCpuClusters; i++) { cpuClusterIds[i] = mPowerStatsInternal.addEnergyConsumer( @@ -109,6 +123,18 @@ public class BatteryExternalStatsWorkerTest { assertEquals(1, displayResults.length); assertEquals(displayId, displayResults[0].id); + final EnergyConsumerResult[] wifiResults = + mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_WIFI).getNow(null); + // Results should only have the wifi energy consumer + assertEquals(1, wifiResults.length); + assertEquals(wifiId, wifiResults[0].id); + + final EnergyConsumerResult[] bluetoothResults = + mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_BT).getNow(null); + // Results should only have the bluetooth energy consumer + assertEquals(1, bluetoothResults.length); + assertEquals(btId, bluetoothResults[0].id); + final EnergyConsumerResult[] cpuResults = mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null); // Results should only have the cpu cluster energy consumers @@ -148,6 +174,9 @@ public class BatteryExternalStatsWorkerTest { } public class TestBatteryStatsImpl extends BatteryStatsImpl { + public TestBatteryStatsImpl(Context context) { + mPowerProfile = new PowerProfile(context, true /* forTest */); + } } public class TestPowerStatsInternal extends PowerStatsInternal { diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java index 5fb6b3381360..f5357b19c4de 100644 --- a/telecomm/java/android/telecom/CallDiagnosticService.java +++ b/telecomm/java/android/telecom/CallDiagnosticService.java @@ -27,8 +27,6 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; -import android.telephony.Annotation; -import android.telephony.ims.ImsReasonInfo; import android.util.ArrayMap; import com.android.internal.telecom.ICallDiagnosticService; @@ -59,7 +57,7 @@ import java.util.concurrent.Executor; * </pre> * <p> * <h2>Threading Model</h2> - * By default, all incoming IPC from Telecom in this service and in the {@link DiagnosticCall} + * By default, all incoming IPC from Telecom in this service and in the {@link CallDiagnostics} * instances will take place on the main thread. You can override {@link #getExecutor()} in your * implementation to provide your own {@link Executor}. * @hide @@ -116,26 +114,28 @@ public abstract class CallDiagnosticService extends Service { } /** - * Listens to events raised by a {@link DiagnosticCall}. + * Listens to events raised by a {@link CallDiagnostics}. */ - private android.telecom.DiagnosticCall.Listener mDiagnosticCallListener = - new android.telecom.DiagnosticCall.Listener() { + private CallDiagnostics.Listener mDiagnosticCallListener = + new CallDiagnostics.Listener() { @Override - public void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, - @DiagnosticCall.MessageType int message, int value) { - handleSendDeviceToDeviceMessage(diagnosticCall, message, value); + public void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics, + @CallDiagnostics.MessageType int message, int value) { + handleSendDeviceToDeviceMessage(callDiagnostics, message, value); } @Override - public void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId, + public void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, + int messageId, CharSequence message) { - handleDisplayDiagnosticMessage(diagnosticCall, messageId, message); + handleDisplayDiagnosticMessage(callDiagnostics, messageId, message); } @Override - public void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) { - handleClearDiagnosticMessage(diagnosticCall, messageId); + public void onClearDiagnosticMessage(CallDiagnostics callDiagnostics, + int messageId) { + handleClearDiagnosticMessage(callDiagnostics, messageId); } }; @@ -149,7 +149,7 @@ public abstract class CallDiagnosticService extends Service { * Map which tracks the Telecom calls received from the Telecom stack. */ private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>(); - private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>(); + private final Map<String, CallDiagnostics> mDiagnosticCallByTelecomCallId = new ArrayMap<>(); private final Object mLock = new Object(); private ICallDiagnosticServiceAdapter mAdapter; @@ -177,7 +177,7 @@ public abstract class CallDiagnosticService extends Service { * executor you want to use for incoming IPC. * * @return the {@link Executor} to use for incoming IPC from Telecom to - * {@link CallDiagnosticService} and {@link DiagnosticCall}. + * {@link CallDiagnosticService} and {@link CallDiagnostics}. */ @SuppressLint("OnNameExpected") @NonNull public Executor getExecutor() { @@ -188,30 +188,30 @@ public abstract class CallDiagnosticService extends Service { * Telecom calls this method on the {@link CallDiagnosticService} with details about a new call * which was added to Telecom. * <p> - * The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be + * The {@link CallDiagnosticService} returns an implementation of {@link CallDiagnostics} to be * used for the lifespan of this call. * <p> * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see * {@link CallDiagnosticService#getExecutor()} for more information. * * @param call The details of the new call. - * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService} + * @return An instance of {@link CallDiagnostics} which the {@link CallDiagnosticService} * provides to be used for the lifespan of the call. - * @throws IllegalArgumentException if a {@code null} {@link DiagnosticCall} is returned. + * @throws IllegalArgumentException if a {@code null} {@link CallDiagnostics} is returned. */ - public abstract @NonNull DiagnosticCall onInitializeDiagnosticCall(@NonNull + public abstract @NonNull CallDiagnostics onInitializeCallDiagnostics(@NonNull android.telecom.Call.Details call); /** - * Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed. - * This happens when Telecom is no longer tracking the call in question. + * Telecom calls this method when a previous created {@link CallDiagnostics} is no longer + * needed. This happens when Telecom is no longer tracking the call in question. * <p> * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see * {@link CallDiagnosticService#getExecutor()} for more information. * * @param call The diagnostic call which is no longer tracked by Telecom. */ - public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call); + public abstract void onRemoveCallDiagnostics(@NonNull CallDiagnostics call); /** * Telecom calls this method when the audio routing or available audio route information @@ -260,35 +260,35 @@ public abstract class CallDiagnosticService extends Service { } getExecutor().execute(() -> { - DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails); - if (diagnosticCall == null) { + CallDiagnostics callDiagnostics = onInitializeCallDiagnostics(newCallDetails); + if (callDiagnostics == null) { throw new IllegalArgumentException( "A valid DiagnosticCall instance was not provided."); } synchronized (mLock) { - diagnosticCall.setListener(mDiagnosticCallListener); - diagnosticCall.setCallId(telecomCallId); - mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall); + callDiagnostics.setListener(mDiagnosticCallListener); + callDiagnostics.setCallId(telecomCallId); + mDiagnosticCallByTelecomCallId.put(telecomCallId, callDiagnostics); } }); } /** * Handles an update to {@link Call.Details} notified by Telecom. - * Caches the call details and notifies the {@link DiagnosticCall} of the change via - * {@link DiagnosticCall#onCallDetailsChanged(Call.Details)}. + * Caches the call details and notifies the {@link CallDiagnostics} of the change via + * {@link CallDiagnostics#onCallDetailsChanged(Call.Details)}. * @param parcelableCall the new parceled call details from Telecom. */ private void handleCallUpdated(@NonNull ParcelableCall parcelableCall) { String telecomCallId = parcelableCall.getId(); Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId); Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall); - DiagnosticCall diagnosticCall; + CallDiagnostics callDiagnostics; synchronized (mLock) { - diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId); + callDiagnostics = mDiagnosticCallByTelecomCallId.get(telecomCallId); mCallByTelecomCallId.put(telecomCallId, newCallDetails); } - getExecutor().execute(() -> diagnosticCall.handleCallUpdated(newCallDetails)); + getExecutor().execute(() -> callDiagnostics.handleCallUpdated(newCallDetails)); } /** @@ -302,37 +302,37 @@ public abstract class CallDiagnosticService extends Service { mCallByTelecomCallId.remove(telecomCallId); } - DiagnosticCall diagnosticCall; + CallDiagnostics callDiagnostics; synchronized (mLock) { if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) { - diagnosticCall = mDiagnosticCallByTelecomCallId.remove(telecomCallId); + callDiagnostics = mDiagnosticCallByTelecomCallId.remove(telecomCallId); } else { - diagnosticCall = null; + callDiagnostics = null; } } // Inform the service of the removed call. - if (diagnosticCall != null) { - getExecutor().execute(() -> onRemoveDiagnosticCall(diagnosticCall)); + if (callDiagnostics != null) { + getExecutor().execute(() -> onRemoveCallDiagnostics(callDiagnostics)); } } /** * Handles an incoming device to device message received from Telecom. Notifies the - * {@link DiagnosticCall} via {@link DiagnosticCall#onReceiveDeviceToDeviceMessage(int, int)}. + * {@link CallDiagnostics} via {@link CallDiagnostics#onReceiveDeviceToDeviceMessage(int, int)}. * @param callId * @param message * @param value */ private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) { Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value); - DiagnosticCall diagnosticCall; + CallDiagnostics callDiagnostics; synchronized (mLock) { - diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId); + callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId); } - if (diagnosticCall != null) { + if (callDiagnostics != null) { getExecutor().execute( - () -> diagnosticCall.onReceiveDeviceToDeviceMessage(message, value)); + () -> callDiagnostics.onReceiveDeviceToDeviceMessage(message, value)); } } @@ -345,12 +345,12 @@ public abstract class CallDiagnosticService extends Service { private void handleCallDisconnected(@NonNull String callId, @NonNull DisconnectCause disconnectCause) { Log.i(this, "handleCallDisconnected: call=%s; cause=%s", callId, disconnectCause); - DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId); + CallDiagnostics callDiagnostics = mDiagnosticCallByTelecomCallId.get(callId); CharSequence message; if (disconnectCause.getImsReasonInfo() != null) { - message = diagnosticCall.onCallDisconnected(disconnectCause.getImsReasonInfo()); + message = callDiagnostics.onCallDisconnected(disconnectCause.getImsReasonInfo()); } else { - message = diagnosticCall.onCallDisconnected( + message = callDiagnostics.onCallDisconnected( disconnectCause.getTelephonyDisconnectCause(), disconnectCause.getTelephonyPreciseDisconnectCause()); } @@ -375,15 +375,15 @@ public abstract class CallDiagnosticService extends Service { } /** - * Handles a request from a {@link DiagnosticCall} to send a device to device message (received - * via {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}. - * @param diagnosticCall + * Handles a request from a {@link CallDiagnostics} to send a device to device message (received + * via {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}. + * @param callDiagnostics * @param message * @param value */ - private void handleSendDeviceToDeviceMessage(@NonNull DiagnosticCall diagnosticCall, + private void handleSendDeviceToDeviceMessage(@NonNull CallDiagnostics callDiagnostics, int message, int value) { - String callId = diagnosticCall.getCallId(); + String callId = callDiagnostics.getCallId(); try { mAdapter.sendDeviceToDeviceMessage(callId, message, value); Log.i(this, "handleSendDeviceToDeviceMessage: call=%s; msg=%d/%d", callId, message, @@ -395,15 +395,15 @@ public abstract class CallDiagnosticService extends Service { } /** - * Handles a request from a {@link DiagnosticCall} to display an in-call diagnostic message. - * Originates from {@link DiagnosticCall#displayDiagnosticMessage(int, CharSequence)}. - * @param diagnosticCall + * Handles a request from a {@link CallDiagnostics} to display an in-call diagnostic message. + * Originates from {@link CallDiagnostics#displayDiagnosticMessage(int, CharSequence)}. + * @param callDiagnostics * @param messageId * @param message */ - private void handleDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId, + private void handleDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId, CharSequence message) { - String callId = diagnosticCall.getCallId(); + String callId = callDiagnostics.getCallId(); try { mAdapter.displayDiagnosticMessage(callId, messageId, message); Log.i(this, "handleDisplayDiagnosticMessage: call=%s; msg=%d/%s", callId, messageId, @@ -415,14 +415,14 @@ public abstract class CallDiagnosticService extends Service { } /** - * Handles a request from a {@link DiagnosticCall} to clear a previously shown diagnostic + * Handles a request from a {@link CallDiagnostics} to clear a previously shown diagnostic * message. - * Originates from {@link DiagnosticCall#clearDiagnosticMessage(int)}. - * @param diagnosticCall + * Originates from {@link CallDiagnostics#clearDiagnosticMessage(int)}. + * @param callDiagnostics * @param messageId */ - private void handleClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId) { - String callId = diagnosticCall.getCallId(); + private void handleClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId) { + String callId = callDiagnostics.getCallId(); try { mAdapter.clearDiagnosticMessage(callId, messageId); Log.i(this, "handleClearDiagnosticMessage: call=%s; msg=%d", callId, messageId); diff --git a/telecomm/java/android/telecom/CallDiagnostics.java b/telecomm/java/android/telecom/CallDiagnostics.java new file mode 100644 index 000000000000..3356431f17b1 --- /dev/null +++ b/telecomm/java/android/telecom/CallDiagnostics.java @@ -0,0 +1,374 @@ +/* + * 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.telecom; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.telephony.Annotation; +import android.telephony.CallQuality; +import android.telephony.ims.ImsReasonInfo; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * {@link CallDiagnostics} provides a way for a {@link CallDiagnosticService} to receive diagnostic + * information about a mobile call on the device. A {@link CallDiagnostics} instance is similar to + * a {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic + * and messaging capabilities not present on a {@link Call}. The {@link CallDiagnosticService} + * creates a {@link CallDiagnostics} for each {@link Call} on the device. This means that for each + * in progress call on the device, the {@link CallDiagnosticService} will create an instance of + * {@link CallDiagnostics}. + * <p> + * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the + * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable + * information about conditions impacting their call and corrective actions. For example, if the + * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform + * the user that the call may soon drop and that they can try using a different calling method + * (e.g. VOIP or WIFI). + * <h2>Threading Model</h2> + * All incoming IPC from Telecom in this class will use the same {@link Executor} as the + * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more + * information. + * @hide + */ +@SystemApi +public abstract class CallDiagnostics { + + /** + * @hide + */ + public interface Listener { + /** + * Used to inform the {@link CallDiagnosticService} of a request to send a D2d message + * @param callDiagnostics the call the message is from. + * @param message the message type + * @param value the message value + */ + void onSendDeviceToDeviceMessage(CallDiagnostics callDiagnostics, int message, int value); + + /** + * Used to inform the {@link CallDiagnosticService} of a request to display a diagnostic + * message. + * @param callDiagnostics the call the message pertains to. + * @param messageId an identifier for the message. + * @param message the message to display. + */ + void onDisplayDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId, + CharSequence message); + + /** + * Used to inform the {@link CallDiagnosticService} that a previously shown message is no + * longer pertinent. + * @param callDiagnostics the call the message pertains to. + * @param messageId the ID of the previously posted message. + */ + void onClearDiagnosticMessage(CallDiagnostics callDiagnostics, int messageId); + } + + /** + * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via + * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type + * used for the current call. The call network type communicated here is an intentional + * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which + * removes some of the resolution inherent in those values; the + * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is + * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for + * efficiency of transport. For a discussion on the necessity of this simplification, see + * {@link #sendDeviceToDeviceMessage(int, int)}. + * <p> + * Valid values are below: + * <UL> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI> + * </UL> + */ + public static final int MESSAGE_CALL_NETWORK_TYPE = 1; + + /** + * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via + * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec + * used for the current call. + * <p> + * The audio codec communicated here is an intentional simplification of the + * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common + * variants of these audio codecs. Other variants of these codecs are reported as the next + * closest variant. For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec + * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}. + * For a discussion on the necessity of this simplification, see + * {@link #sendDeviceToDeviceMessage(int, int)}. + * <p> + * Valid values: + * <UL> + * <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI> + * <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI> + * <LI>{@link Connection#AUDIO_CODEC_AMR}</LI> + * </UL> + */ + public static final int MESSAGE_CALL_AUDIO_CODEC = 2; + + /** + * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via + * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of + * the device. Will typically mirror battery state reported via intents such as + * {@link android.content.Intent#ACTION_BATTERY_LOW}. + * <p> + * Valid values: + * <UL> + * <LI>{@link #BATTERY_STATE_LOW}</LI> + * <LI>{@link #BATTERY_STATE_GOOD}</LI> + * <LI>{@link #BATTERY_STATE_CHARGING}</LI> + * </UL> + */ + public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; + + /** + * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via + * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network + * coverage as it pertains to the current call. A {@link CallDiagnosticService} should signal + * poor coverage if the network coverage reaches a level where there is a high probability of + * the call dropping as a result. + * <p> + * Valid values: + * <UL> + * <LI>{@link #COVERAGE_POOR}</LI> + * <LI>{@link #COVERAGE_GOOD}</LI> + * </UL> + */ + public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "MESSAGE_", value = { + MESSAGE_CALL_NETWORK_TYPE, + MESSAGE_CALL_AUDIO_CODEC, + MESSAGE_DEVICE_BATTERY_STATE, + MESSAGE_DEVICE_NETWORK_COVERAGE + }) + public @interface MessageType {} + + /** + * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low. + */ + public static final int BATTERY_STATE_LOW = 1; + + /** + * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low. + */ + public static final int BATTERY_STATE_GOOD = 2; + + /** + * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging. + */ + public static final int BATTERY_STATE_CHARGING = 3; + + /** + * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor. + */ + public static final int COVERAGE_POOR = 1; + + /** + * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good. + */ + public static final int COVERAGE_GOOD = 2; + + private Listener mListener; + private String mCallId; + + /** + * @hide + */ + public void setListener(@NonNull Listener listener) { + mListener = listener; + } + + /** + * Sets the call ID for this {@link CallDiagnostics}. + * @param callId + * @hide + */ + public void setCallId(@NonNull String callId) { + mCallId = callId; + } + + /** + * @return the Telecom call ID for this {@link CallDiagnostics}. + * @hide + */ + public @NonNull String getCallId() { + return mCallId; + } + + /** + * Telecom calls this method when the details of a call changes. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details); + + /** + * The {@link CallDiagnosticService} implements this method to handle messages received via + * device to device communication. + * <p> + * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device + * communication. + * <p> + * The underlying device to device communication protocol assumes that where there the two + * devices communicating are using a different version of the protocol, messages the recipient + * are not aware of are silently discarded. This means an older client talking to a new client + * will not receive newer messages and values sent by the new client. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + public abstract void onReceiveDeviceToDeviceMessage( + @MessageType int message, + int value); + + /** + * Sends a device to device message to the device on the other end of this call. + * <p> + * Device to device communication is an Android platform feature which supports low bandwidth + * communication between Android devices while they are in a call. The device to device + * communication leverages DTMF tones or RTP header extensions to pass messages. The + * messages are unacknowledged and sent in a best-effort manner. The protocols assume that the + * nature of the message are informational only and are used only to convey basic state + * information between devices. + * <p> + * Device to device messages are intentional simplifications of more rich indicators in the + * platform due to the extreme bandwidth constraints inherent with underlying device to device + * communication transports used by the telephony framework. Device to device communication is + * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets + * for a call, or using the DTMF digits A-D as a communication pathway. RTP header extension + * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for + * a message. Signalling requirements for DTMF digits place even more significant limitations + * on the amount of information which can be communicated during a call, offering only a few + * bits of potential information per message. The messages and values are constrained in order + * to meet the limited bandwidth inherent with DTMF signalling. + * <p> + * Allowed message types are: + * <ul> + * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI> + * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI> + * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI> + * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI> + * </ul> + * @param message The message type to send. + * @param value The message value corresponding to the type. + */ + public final void sendDeviceToDeviceMessage(int message, int value) { + if (mListener != null) { + mListener.onSendDeviceToDeviceMessage(this, message, value); + } + } + + /** + * Telecom calls this method when a GSM or CDMA call disconnects. + * The CallDiagnosticService can return a human readable disconnect message which will be passed + * to the Dialer app as the {@link DisconnectCause#getDescription()}. A dialer app typically + * shows this message at the termination of the call. If {@code null} is returned, the + * disconnect message generated by the telephony stack will be shown instead. + * <p> + * @param disconnectCause the disconnect cause for the call. + * @param preciseDisconnectCause the precise disconnect cause for the call. + * @return the disconnect message to use in place of the default Telephony message, or + * {@code null} if the default message will not be overridden. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + // TODO: Wire in Telephony support for this. + public abstract @Nullable CharSequence onCallDisconnected( + @Annotation.DisconnectCauses int disconnectCause, + @Annotation.PreciseDisconnectCauses int preciseDisconnectCause); + + /** + * Telecom calls this method when an IMS call disconnects and Telephony has already + * provided the disconnect reason info and disconnect message for the call. The + * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and + * combine it with other call diagnostic information it is aware of to override the disconnect + * call message if desired. + * + * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection. + * @return A user-readable call disconnect message to use in place of the platform-generated + * disconnect message, or {@code null} if the disconnect message should not be overridden. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + // TODO: Wire in Telephony support for this. + public abstract @Nullable CharSequence onCallDisconnected( + @NonNull ImsReasonInfo disconnectReason); + + /** + * Telecom calls this method when a {@link CallQuality} report is received from the telephony + * stack for a call. + * @param callQuality The call quality report for this call. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. + */ + public abstract void onCallQualityReceived(@NonNull CallQuality callQuality); + + /** + * Signals the active default dialer app to display a call diagnostic message. This can be + * used to report problems encountered during the span of a call. + * <p> + * The {@link CallDiagnosticService} provides a unique client-specific identifier used to + * identify the specific diagnostic message type. + * <p> + * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the + * diagnostic condition has cleared. + * @param messageId the unique message identifier. + * @param message a human-readable, localized message to be shown to the user indicating a + * call issue which has occurred, along with potential mitigating actions. + */ + public final void displayDiagnosticMessage(int messageId, @NonNull + CharSequence message) { + if (mListener != null) { + mListener.onDisplayDiagnosticMessage(this, messageId, message); + } + } + + /** + * Signals to the active default dialer that the diagnostic message previously signalled using + * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no + * longer applicable (e.g. service has improved, for example. + * @param messageId the message identifier for a message previously shown via + * {@link #displayDiagnosticMessage(int, CharSequence)}. + */ + public final void clearDiagnosticMessage(int messageId) { + if (mListener != null) { + mListener.onClearDiagnosticMessage(this, messageId); + } + } + + /** + * Called by the {@link CallDiagnosticService} to update the call details for this + * {@link CallDiagnostics} based on an update received from Telecom. + * @param newDetails the new call details. + * @hide + */ + public void handleCallUpdated(@NonNull Call.Details newDetails) { + onCallDetailsChanged(newDetails); + } +} diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 335857af8883..6dab6df22cf9 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -947,7 +947,7 @@ public abstract class Connection extends Conferenceable { * {@link CallDiagnosticService} implementation which is active. * <p> * Likewise, if a {@link CallDiagnosticService} sends a message using - * {@link DiagnosticCall#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony + * {@link CallDiagnostics#sendDeviceToDeviceMessage(int, int)}, it will be routed to telephony * via {@link Connection#onCallEvent(String, Bundle)}. The telephony stack will relay the * message to the other device. * @hide @@ -960,7 +960,7 @@ public abstract class Connection extends Conferenceable { * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device * message type. * - * See {@link DiagnosticCall} for more information. + * See {@link CallDiagnostics} for more information. * @hide */ @SystemApi @@ -971,7 +971,7 @@ public abstract class Connection extends Conferenceable { * Sent along with {@link #EVENT_DEVICE_TO_DEVICE_MESSAGE} to indicate the device to device * message value. * <p> - * See {@link DiagnosticCall} for more information. + * See {@link CallDiagnostics} for more information. * @hide */ @SystemApi diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java index af46b7759fb5..a6b7258052a4 100644 --- a/telecomm/java/android/telecom/DiagnosticCall.java +++ b/telecomm/java/android/telecom/DiagnosticCall.java @@ -16,338 +16,12 @@ package android.telecom; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; -import android.telephony.Annotation; -import android.telephony.CallQuality; -import android.telephony.ims.ImsReasonInfo; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.concurrent.Executor; /** - * A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic - * information about a mobile call on the device. A {@link DiagnosticCall} is similar to a - * {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic - * and messaging capabilities not present on a {@link Call}. The {@link CallDiagnosticService} - * creates a {@link DiagnosticCall} for each {@link Call} on the device. This means that for each - * in progress call on the device, the {@link CallDiagnosticService} will create an instance of - * {@link DiagnosticCall}. - * <p> - * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the - * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable - * information about conditions impacting their call and corrective actions. For example, if the - * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform - * the user that the call may soon drop and that they can try using a different calling method - * (e.g. VOIP or WIFI). - * <h2>Threading Model</h2> - * All incoming IPC from Telecom in this class will use the same {@link Executor} as the - * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more - * information. + * @deprecated use {@link CallDiagnostics} instead. * @hide */ @SystemApi -public abstract class DiagnosticCall { - - /** - * @hide - */ - public interface Listener { - void onSendDeviceToDeviceMessage(DiagnosticCall diagnosticCall, int message, int value); - void onDisplayDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId, - CharSequence message); - void onClearDiagnosticMessage(DiagnosticCall diagnosticCall, int messageId); - } - - /** - * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via - * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type - * used for the current call. The call network type communicated here is an intentional - * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which - * removes some of the resolution inherent in those values; the - * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is - * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for - * efficiency of transport. For a discussion on the necessity of this simplification, see - * {@link #sendDeviceToDeviceMessage(int, int)}. - * <p> - * Valid values are below: - * <UL> - * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI> - * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI> - * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI> - * </UL> - */ - public static final int MESSAGE_CALL_NETWORK_TYPE = 1; - - /** - * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via - * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec - * used for the current call. - * <p> - * The audio codec communicated here is an intentional simplification of the - * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common - * variants of these audio codecs. Other variants of these codecs are reported as the next - * closest variant. For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec - * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}. - * For a discussion on the necessity of this simplification, see - * {@link #sendDeviceToDeviceMessage(int, int)}. - * <p> - * Valid values: - * <UL> - * <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI> - * <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI> - * <LI>{@link Connection#AUDIO_CODEC_AMR}</LI> - * </UL> - */ - public static final int MESSAGE_CALL_AUDIO_CODEC = 2; - - /** - * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via - * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the battery state of - * the device. Will typically mirror battery state reported via intents such as - * {@link android.content.Intent#ACTION_BATTERY_LOW}. - * <p> - * Valid values: - * <UL> - * <LI>{@link #BATTERY_STATE_LOW}</LI> - * <LI>{@link #BATTERY_STATE_GOOD}</LI> - * <LI>{@link #BATTERY_STATE_CHARGING}</LI> - * </UL> - */ - public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; - - /** - * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via - * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the overall network - * coverage as it pertains to the current call. A {@link CallDiagnosticService} should signal - * poor coverage if the network coverage reaches a level where there is a high probability of - * the call dropping as a result. - * <p> - * Valid values: - * <UL> - * <LI>{@link #COVERAGE_POOR}</LI> - * <LI>{@link #COVERAGE_GOOD}</LI> - * </UL> - */ - public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; - - /**@hide*/ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "MESSAGE_", value = { - MESSAGE_CALL_NETWORK_TYPE, - MESSAGE_CALL_AUDIO_CODEC, - MESSAGE_DEVICE_BATTERY_STATE, - MESSAGE_DEVICE_NETWORK_COVERAGE - }) - public @interface MessageType {} - - /** - * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low. - */ - public static final int BATTERY_STATE_LOW = 1; - - /** - * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is not low. - */ - public static final int BATTERY_STATE_GOOD = 2; - - /** - * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is charging. - */ - public static final int BATTERY_STATE_CHARGING = 3; - - /** - * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is poor. - */ - public static final int COVERAGE_POOR = 1; - - /** - * Used with {@link #MESSAGE_DEVICE_NETWORK_COVERAGE} to indicate that the coverage is good. - */ - public static final int COVERAGE_GOOD = 2; - - private Listener mListener; - private String mCallId; - - /** - * @hide - */ - public void setListener(@NonNull Listener listener) { - mListener = listener; - } - - /** - * Sets the call ID for this {@link DiagnosticCall}. - * @param callId - * @hide - */ - public void setCallId(@NonNull String callId) { - mCallId = callId; - } - - /** - * @return the Telecom call ID for this {@link DiagnosticCall}. - * @hide - */ - public @NonNull String getCallId() { - return mCallId; - } - - /** - * Telecom calls this method when the details of a call changes. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details); - - /** - * The {@link CallDiagnosticService} implements this method to handle messages received via - * device to device communication. - * <p> - * See {@link #sendDeviceToDeviceMessage(int, int)} for background on device to device - * communication. - * <p> - * The underlying device to device communication protocol assumes that where there the two - * devices communicating are using a different version of the protocol, messages the recipient - * are not aware of are silently discarded. This means an older client talking to a new client - * will not receive newer messages and values sent by the new client. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - public abstract void onReceiveDeviceToDeviceMessage( - @MessageType int message, - int value); - - /** - * Sends a device to device message to the device on the other end of this call. - * <p> - * Device to device communication is an Android platform feature which supports low bandwidth - * communication between Android devices while they are in a call. The device to device - * communication leverages DTMF tones or RTP header extensions to pass messages. The - * messages are unacknowledged and sent in a best-effort manner. The protocols assume that the - * nature of the message are informational only and are used only to convey basic state - * information between devices. - * <p> - * Device to device messages are intentional simplifications of more rich indicators in the - * platform due to the extreme bandwidth constraints inherent with underlying device to device - * communication transports used by the telephony framework. Device to device communication is - * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets - * for a call, or using the DTMF digits A-D as a communication pathway. RTP header extension - * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for - * a message. Signalling requirements for DTMF digits place even more significant limitations - * on the amount of information which can be communicated during a call, offering only a few - * bits of potential information per message. The messages and values are constrained in order - * to meet the limited bandwidth inherent with DTMF signalling. - * <p> - * Allowed message types are: - * <ul> - * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI> - * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI> - * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI> - * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI> - * </ul> - * @param message The message type to send. - * @param value The message value corresponding to the type. - */ - public final void sendDeviceToDeviceMessage(int message, int value) { - if (mListener != null) { - mListener.onSendDeviceToDeviceMessage(this, message, value); - } - } - - /** - * Telecom calls this method when a GSM or CDMA call disconnects. - * The CallDiagnosticService can return a human readable disconnect message which will be passed - * to the Dialer app as the {@link DisconnectCause#getDescription()}. A dialer app typically - * shows this message at the termination of the call. If {@code null} is returned, the - * disconnect message generated by the telephony stack will be shown instead. - * <p> - * @param disconnectCause the disconnect cause for the call. - * @param preciseDisconnectCause the precise disconnect cause for the call. - * @return the disconnect message to use in place of the default Telephony message, or - * {@code null} if the default message will not be overridden. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - // TODO: Wire in Telephony support for this. - public abstract @Nullable CharSequence onCallDisconnected( - @Annotation.DisconnectCauses int disconnectCause, - @Annotation.PreciseDisconnectCauses int preciseDisconnectCause); - - /** - * Telecom calls this method when an IMS call disconnects and Telephony has already - * provided the disconnect reason info and disconnect message for the call. The - * {@link CallDiagnosticService} can intercept the raw IMS disconnect reason at this point and - * combine it with other call diagnostic information it is aware of to override the disconnect - * call message if desired. - * - * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection. - * @return A user-readable call disconnect message to use in place of the platform-generated - * disconnect message, or {@code null} if the disconnect message should not be overridden. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - // TODO: Wire in Telephony support for this. - public abstract @Nullable CharSequence onCallDisconnected( - @NonNull ImsReasonInfo disconnectReason); - - /** - * Telecom calls this method when a {@link CallQuality} report is received from the telephony - * stack for a call. - * @param callQuality The call quality report for this call. - * <p> - * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; - * see {@link CallDiagnosticService#getExecutor()} for more information. - */ - public abstract void onCallQualityReceived(@NonNull CallQuality callQuality); - - /** - * Signals the active default dialer app to display a call diagnostic message. This can be - * used to report problems encountered during the span of a call. - * <p> - * The {@link CallDiagnosticService} provides a unique client-specific identifier used to - * identify the specific diagnostic message type. - * <p> - * The {@link CallDiagnosticService} should call {@link #clearDiagnosticMessage(int)} when the - * diagnostic condition has cleared. - * @param messageId the unique message identifier. - * @param message a human-readable, localized message to be shown to the user indicating a - * call issue which has occurred, along with potential mitigating actions. - */ - public final void displayDiagnosticMessage(int messageId, @NonNull - CharSequence message) { - if (mListener != null) { - mListener.onDisplayDiagnosticMessage(this, messageId, message); - } - } - - /** - * Signals to the active default dialer that the diagnostic message previously signalled using - * {@link #displayDiagnosticMessage(int, CharSequence)} with the specified messageId is no - * longer applicable (e.g. service has improved, for example. - * @param messageId the message identifier for a message previously shown via - * {@link #displayDiagnosticMessage(int, CharSequence)}. - */ - public final void clearDiagnosticMessage(int messageId) { - if (mListener != null) { - mListener.onClearDiagnosticMessage(this, messageId); - } - } - - /** - * Called by the {@link CallDiagnosticService} to update the call details for this - * {@link DiagnosticCall} based on an update received from Telecom. - * @param newDetails the new call details. - * @hide - */ - public void handleCallUpdated(@NonNull Call.Details newDetails) { - onCallDetailsChanged(newDetails); - } +public abstract class DiagnosticCall extends CallDiagnostics { } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index faa5deed0f1c..962200b82a81 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -15454,7 +15454,9 @@ public class TelephonyManager { public static final int PREPARE_UNATTENDED_REBOOT_PIN_REQUIRED = 1; /** - * The unattended reboot was not prepared due to generic error. + * The unattended reboot was not prepared due to a non-recoverable error. After this error, + * the client that manages the unattended reboot should not try to invoke the API again + * until the next power cycle. * @hide */ @SystemApi diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index 3c12aaa0f1c8..c92d40cdd555 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.close -import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -50,12 +49,7 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio @JvmStatic fun getParams(): List<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - repetitions = 5, - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) - ) + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index 8359ccf12d2d..1f880f61d65e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.close -import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -50,12 +49,7 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - repetitions = 5, - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) - ) + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index fadd1eac14ef..58d8ee512dc2 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -202,6 +202,7 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkPolicyManager; import android.net.NetworkRequest; +import android.net.NetworkScore; import android.net.NetworkSpecifier; import android.net.NetworkStack; import android.net.NetworkStackClient; @@ -1078,6 +1079,10 @@ public class ConnectivityServiceTest { public void triggerUnfulfillable(NetworkRequest r) { super.releaseRequestAsUnfulfillableByAnyFactory(r); } + + public void assertNoRequestChanged() { + assertNull(mRequestHistory.poll(0, r -> true)); + } } private Set<UidRange> uidRangesForUids(int... uids) { @@ -9160,7 +9165,8 @@ public class ConnectivityServiceTest { ConnectivityManager.getNetworkTypeName(TYPE_MOBILE), TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE)); return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(), - nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, + nc, new NetworkScore.Builder().setLegacyInt(0).build(), + mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies()); } @@ -11119,11 +11125,99 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(cellCb); } + // Cannot be part of MockNetworkFactory since it requires method of the test. + private void expectNoRequestChanged(@NonNull MockNetworkFactory factory) { + waitForIdle(); + factory.assertNoRequestChanged(); + } + @Test - public void testRegisterBestMatchingNetworkCallback() throws Exception { - final NetworkRequest request = new NetworkRequest.Builder().build(); - assertThrows(UnsupportedOperationException.class, - () -> mCm.registerBestMatchingNetworkCallback(request, new NetworkCallback(), - mCsHandlerThread.getThreadHandler())); + public void testRegisterBestMatchingNetworkCallback_noIssueToFactory() throws Exception { + // Prepare mock mms factory. + final HandlerThread handlerThread = new HandlerThread("MockCellularFactory"); + handlerThread.start(); + NetworkCapabilities filter = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_MMS); + final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactory", filter, mCsHandlerThread); + testFactory.setScoreFilter(40); + + try { + // Register the factory and expect it will see default request, because all requests + // are sent to all factories. + testFactory.register(); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(1); + // The factory won't try to start the network since the default request doesn't + // match the filter (no INTERNET capability). + assertFalse(testFactory.getMyStartRequested()); + + // Register callback for listening best matching network. Verify that the request won't + // be sent to factory. + final TestNetworkCallback bestMatchingCb = new TestNetworkCallback(); + mCm.registerBestMatchingNetworkCallback( + new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(), + bestMatchingCb, mCsHandlerThread.getThreadHandler()); + bestMatchingCb.assertNoCallback(); + expectNoRequestChanged(testFactory); + testFactory.assertRequestCountEquals(1); + assertFalse(testFactory.getMyStartRequested()); + + // Fire a normal mms request, verify the factory will only see the request. + final TestNetworkCallback mmsNetworkCallback = new TestNetworkCallback(); + final NetworkRequest mmsRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_MMS).build(); + mCm.requestNetwork(mmsRequest, mmsNetworkCallback); + testFactory.expectRequestAdd(); + testFactory.assertRequestCountEquals(2); + assertTrue(testFactory.getMyStartRequested()); + + // Unregister best matching callback, verify factory see no change. + mCm.unregisterNetworkCallback(bestMatchingCb); + expectNoRequestChanged(testFactory); + testFactory.assertRequestCountEquals(2); + assertTrue(testFactory.getMyStartRequested()); + } finally { + testFactory.terminate(); + } + } + + @Test + public void testRegisterBestMatchingNetworkCallback_trackBestNetwork() throws Exception { + final TestNetworkCallback bestMatchingCb = new TestNetworkCallback(); + mCm.registerBestMatchingNetworkCallback( + new NetworkRequest.Builder().addCapability(NET_CAPABILITY_TRUSTED).build(), + bestMatchingCb, mCsHandlerThread.getThreadHandler()); + + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + bestMatchingCb.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + + // Change something on cellular to trigger capabilities changed, since the callback + // only cares about the best network, verify it received nothing from cellular. + mCellNetworkAgent.addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED); + bestMatchingCb.assertNoCallback(); + + // Make cellular the best network again, verify the callback now tracks cellular. + mWiFiNetworkAgent.adjustScore(-50); + bestMatchingCb.expectAvailableCallbacksValidated(mCellNetworkAgent); + + // Make cellular temporary non-trusted, which will not satisfying the request. + // Verify the callback switch from/to the other network accordingly. + mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); + bestMatchingCb.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + mCellNetworkAgent.addCapability(NET_CAPABILITY_TRUSTED); + bestMatchingCb.expectAvailableDoubleValidatedCallbacks(mCellNetworkAgent); + + // Verify the callback doesn't care about wifi disconnect. + mWiFiNetworkAgent.disconnect(); + bestMatchingCb.assertNoCallback(); + mCellNetworkAgent.disconnect(); + bestMatchingCb.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); } } diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 1c0ba4f8d8f5..ea2b362c537a 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -40,6 +40,7 @@ import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkProvider; +import android.net.NetworkScore; import android.os.Binder; import android.text.format.DateUtils; @@ -355,8 +356,9 @@ public class LingerMonitorTest { caps.addCapability(0); caps.addTransportType(transport); NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, - new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */, - mConnService, mNetd, mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), + new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(), + mCtx, null, new NetworkAgentConfig() /* config */, mConnService, mNetd, + mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), mQosCallbackTracker, new ConnectivityService.Dependencies()); nai.everValidated = true; return nai; diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index b8f7fbca3983..11fcea60d98d 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -1026,7 +1026,11 @@ public class VpnTest { .thenReturn(new Network[] { new Network(101) }); when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), - anyInt(), any(), anyInt())).thenReturn(new Network(102)); + any(), any(), anyInt())).thenAnswer(invocation -> { + // The runner has registered an agent and is now ready. + legacyRunnerReady.open(); + return new Network(102); + }); final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; try { @@ -1048,7 +1052,7 @@ public class VpnTest { ArgumentCaptor<NetworkCapabilities> ncCaptor = ArgumentCaptor.forClass(NetworkCapabilities.class); verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), - lpCaptor.capture(), ncCaptor.capture(), anyInt(), any(), anyInt()); + lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt()); // In this test the expected address is always v4 so /32. // Note that the interface needs to be specified because RouteInfo objects stored in diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index 0e5f5e43f282..ca6448ca9b8c 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -188,7 +188,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection any(), lpCaptor.capture(), ncCaptor.capture(), - anyInt(), + any(), any(), anyInt()); verify(mIpSecSvc) |