| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.media.metrics; |
| |
| import android.annotation.IntDef; |
| import android.annotation.IntRange; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.SuppressLint; |
| import android.os.Bundle; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| |
| import java.lang.annotation.Retention; |
| import java.util.Objects; |
| |
| /** |
| * Playback error event. |
| */ |
| public final class PlaybackErrorEvent extends Event implements Parcelable { |
| /** Unknown error code. */ |
| public static final int ERROR_UNKNOWN = 0; |
| /** Error code for other errors */ |
| public static final int ERROR_OTHER = 1; |
| /** Error code for runtime errors */ |
| public static final int ERROR_RUNTIME = 2; |
| |
| /** Error code for lack of network connectivity while trying to access a network resource */ |
| public static final int ERROR_IO_NETWORK_UNAVAILABLE = 3; |
| /** Error code for a failure while establishing a network connection */ |
| public static final int ERROR_IO_NETWORK_CONNECTION_FAILED = 4; |
| /** Error code for an HTTP server returning an unexpected HTTP response status code */ |
| public static final int ERROR_IO_BAD_HTTP_STATUS = 5; |
| /** Error code for failing to resolve a hostname */ |
| public static final int ERROR_IO_DNS_FAILED = 6; |
| /** |
| * Error code for a network timeout, meaning the server is taking too long to fulfill |
| * a request |
| */ |
| public static final int ERROR_IO_CONNECTION_TIMEOUT = 7; |
| /** Error code for an existing network connection being unexpectedly closed */ |
| public static final int ERROR_IO_CONNECTION_CLOSED = 8; |
| /** Error code for other Input/Output errors */ |
| public static final int ERROR_IO_OTHER = 9; |
| |
| /** Error code for a parsing error associated to a media manifest */ |
| public static final int ERROR_PARSING_MANIFEST_MALFORMED = 10; |
| /** Error code for a parsing error associated to a media container format bitstream */ |
| public static final int ERROR_PARSING_CONTAINER_MALFORMED = 11; |
| /** Error code for other media parsing errors */ |
| public static final int ERROR_PARSING_OTHER = 12; |
| |
| /** Error code for a decoder initialization failure */ |
| public static final int ERROR_DECODER_INIT_FAILED = 13; |
| /** Error code for a failure while trying to decode media samples */ |
| public static final int ERROR_DECODING_FAILED = 14; |
| /** |
| * Error code for trying to decode content whose format exceeds the capabilities of the device. |
| */ |
| public static final int ERROR_DECODING_FORMAT_EXCEEDS_CAPABILITIES = 15; |
| /** Error code for other decoding errors */ |
| public static final int ERROR_DECODING_OTHER = 16; |
| |
| /** Error code for an AudioTrack initialization failure */ |
| public static final int ERROR_AUDIO_TRACK_INIT_FAILED = 17; |
| /** Error code for an AudioTrack write operation failure */ |
| public static final int ERROR_AUDIO_TRACK_WRITE_FAILED = 18; |
| /** Error code for other AudioTrack errors */ |
| public static final int ERROR_AUDIO_TRACK_OTHER = 19; |
| |
| /** Error code for an unidentified error in a remote controller or player */ |
| public static final int ERROR_PLAYER_REMOTE = 20; |
| /** |
| * Error code for the loading position falling behind the sliding window of available live |
| * content. |
| */ |
| public static final int ERROR_PLAYER_BEHIND_LIVE_WINDOW = 21; |
| /** Error code for other player errors */ |
| public static final int ERROR_PLAYER_OTHER = 22; |
| |
| /** Error code for a chosen DRM protection scheme not being supported by the device */ |
| public static final int ERROR_DRM_SCHEME_UNSUPPORTED = 23; |
| /** Error code for a failure while provisioning the device */ |
| public static final int ERROR_DRM_PROVISIONING_FAILED = 24; |
| /** Error code for a failure while trying to obtain a license */ |
| public static final int ERROR_DRM_LICENSE_ACQUISITION_FAILED = 25; |
| /** Error code an operation being disallowed by a license policy */ |
| public static final int ERROR_DRM_DISALLOWED_OPERATION = 26; |
| /** Error code for an error in the DRM system */ |
| public static final int ERROR_DRM_SYSTEM_ERROR = 27; |
| /** Error code for attempting to play incompatible DRM-protected content */ |
| public static final int ERROR_DRM_CONTENT_ERROR = 28; |
| /** Error code for the device having revoked DRM privileges */ |
| public static final int ERROR_DRM_DEVICE_REVOKED = 29; |
| /** Error code for other DRM errors */ |
| public static final int ERROR_DRM_OTHER = 30; |
| |
| /** Error code for a non-existent file */ |
| public static final int ERROR_IO_FILE_NOT_FOUND = 31; |
| /** |
| * Error code for lack of permission to perform an IO operation, for example, lack of permission |
| * to access internet or external storage. |
| */ |
| public static final int ERROR_IO_NO_PERMISSION = 32; |
| |
| /** Error code for an unsupported feature in a media manifest */ |
| public static final int ERROR_PARSING_MANIFEST_UNSUPPORTED = 33; |
| /** |
| * Error code for attempting to extract a file with an unsupported media container format, or an |
| * unsupported media container feature |
| */ |
| public static final int ERROR_PARSING_CONTAINER_UNSUPPORTED = 34; |
| |
| /** Error code for trying to decode content whose format is not supported */ |
| public static final int ERROR_DECODING_FORMAT_UNSUPPORTED = 35; |
| |
| |
| private final @Nullable String mExceptionStack; |
| private final int mErrorCode; |
| private final int mSubErrorCode; |
| private final long mTimeSinceCreatedMillis; |
| |
| |
| /** @hide */ |
| @IntDef(prefix = "ERROR_", value = { |
| ERROR_UNKNOWN, |
| ERROR_OTHER, |
| ERROR_RUNTIME, |
| ERROR_IO_NETWORK_UNAVAILABLE, |
| ERROR_IO_NETWORK_CONNECTION_FAILED, |
| ERROR_IO_BAD_HTTP_STATUS, |
| ERROR_IO_DNS_FAILED, |
| ERROR_IO_CONNECTION_TIMEOUT, |
| ERROR_IO_CONNECTION_CLOSED, |
| ERROR_IO_OTHER, |
| ERROR_PARSING_MANIFEST_MALFORMED, |
| ERROR_PARSING_CONTAINER_MALFORMED, |
| ERROR_PARSING_OTHER, |
| ERROR_DECODER_INIT_FAILED, |
| ERROR_DECODING_FAILED, |
| ERROR_DECODING_FORMAT_EXCEEDS_CAPABILITIES, |
| ERROR_DECODING_OTHER, |
| ERROR_AUDIO_TRACK_INIT_FAILED, |
| ERROR_AUDIO_TRACK_WRITE_FAILED, |
| ERROR_AUDIO_TRACK_OTHER, |
| ERROR_PLAYER_REMOTE, |
| ERROR_PLAYER_BEHIND_LIVE_WINDOW, |
| ERROR_PLAYER_OTHER, |
| ERROR_DRM_SCHEME_UNSUPPORTED, |
| ERROR_DRM_PROVISIONING_FAILED, |
| ERROR_DRM_LICENSE_ACQUISITION_FAILED, |
| ERROR_DRM_DISALLOWED_OPERATION, |
| ERROR_DRM_SYSTEM_ERROR, |
| ERROR_DRM_CONTENT_ERROR, |
| ERROR_DRM_DEVICE_REVOKED, |
| ERROR_DRM_OTHER, |
| ERROR_IO_FILE_NOT_FOUND, |
| ERROR_IO_NO_PERMISSION, |
| ERROR_PARSING_MANIFEST_UNSUPPORTED, |
| ERROR_PARSING_CONTAINER_UNSUPPORTED, |
| ERROR_DECODING_FORMAT_UNSUPPORTED, |
| }) |
| @Retention(java.lang.annotation.RetentionPolicy.SOURCE) |
| public @interface ErrorCode {} |
| |
| /** |
| * Creates a new PlaybackErrorEvent. |
| */ |
| private PlaybackErrorEvent( |
| @Nullable String exceptionStack, |
| int errorCode, |
| int subErrorCode, |
| long timeSinceCreatedMillis, |
| @NonNull Bundle extras) { |
| this.mExceptionStack = exceptionStack; |
| this.mErrorCode = errorCode; |
| this.mSubErrorCode = subErrorCode; |
| this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; |
| this.mMetricsBundle = extras.deepCopy(); |
| } |
| |
| /** @hide */ |
| @Nullable |
| public String getExceptionStack() { |
| return mExceptionStack; |
| } |
| |
| |
| /** |
| * Gets error code. |
| */ |
| @ErrorCode |
| public int getErrorCode() { |
| return mErrorCode; |
| } |
| |
| |
| /** |
| * Gets sub error code. |
| */ |
| @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) |
| public int getSubErrorCode() { |
| return mSubErrorCode; |
| } |
| |
| /** |
| * Gets the timestamp since creation of the playback session in milliseconds. |
| * @return the timestamp since the playback is created, or -1 if unknown. |
| * @see LogSessionId |
| * @see PlaybackSession |
| */ |
| @Override |
| @IntRange(from = -1) |
| public long getTimeSinceCreatedMillis() { |
| return mTimeSinceCreatedMillis; |
| } |
| |
| /** |
| * Gets metrics-related information that is not supported by dedicated methods. |
| * <p>It is intended to be used for backwards compatibility by the metrics infrastructure. |
| */ |
| @Override |
| @NonNull |
| public Bundle getMetricsBundle() { |
| return mMetricsBundle; |
| } |
| |
| @Override |
| public String toString() { |
| return "PlaybackErrorEvent { " |
| + "exceptionStack = " + mExceptionStack + ", " |
| + "errorCode = " + mErrorCode + ", " |
| + "subErrorCode = " + mSubErrorCode + ", " |
| + "timeSinceCreatedMillis = " + mTimeSinceCreatedMillis |
| + " }"; |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| PlaybackErrorEvent that = (PlaybackErrorEvent) o; |
| return Objects.equals(mExceptionStack, that.mExceptionStack) |
| && mErrorCode == that.mErrorCode |
| && mSubErrorCode == that.mSubErrorCode |
| && mTimeSinceCreatedMillis == that.mTimeSinceCreatedMillis; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(mExceptionStack, mErrorCode, mSubErrorCode, |
| mTimeSinceCreatedMillis); |
| } |
| |
| @Override |
| public void writeToParcel(@NonNull Parcel dest, int flags) { |
| byte flg = 0; |
| if (mExceptionStack != null) flg |= 0x1; |
| dest.writeByte(flg); |
| if (mExceptionStack != null) dest.writeString(mExceptionStack); |
| dest.writeInt(mErrorCode); |
| dest.writeInt(mSubErrorCode); |
| dest.writeLong(mTimeSinceCreatedMillis); |
| dest.writeBundle(mMetricsBundle); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| private PlaybackErrorEvent(@NonNull Parcel in) { |
| byte flg = in.readByte(); |
| String exceptionStack = (flg & 0x1) == 0 ? null : in.readString(); |
| int errorCode = in.readInt(); |
| int subErrorCode = in.readInt(); |
| long timeSinceCreatedMillis = in.readLong(); |
| Bundle extras = in.readBundle(); |
| |
| this.mExceptionStack = exceptionStack; |
| this.mErrorCode = errorCode; |
| this.mSubErrorCode = subErrorCode; |
| this.mTimeSinceCreatedMillis = timeSinceCreatedMillis; |
| this.mMetricsBundle = extras; |
| } |
| |
| |
| public static final @NonNull Parcelable.Creator<PlaybackErrorEvent> CREATOR = |
| new Parcelable.Creator<PlaybackErrorEvent>() { |
| @Override |
| public PlaybackErrorEvent[] newArray(int size) { |
| return new PlaybackErrorEvent[size]; |
| } |
| |
| @Override |
| public PlaybackErrorEvent createFromParcel(@NonNull Parcel in) { |
| return new PlaybackErrorEvent(in); |
| } |
| }; |
| |
| /** |
| * A builder for {@link PlaybackErrorEvent} |
| */ |
| public static final class Builder { |
| private @Nullable Exception mException; |
| private int mErrorCode = ERROR_UNKNOWN; |
| private int mSubErrorCode; |
| private long mTimeSinceCreatedMillis = -1; |
| private Bundle mMetricsBundle = new Bundle(); |
| |
| /** |
| * Creates a new Builder. |
| */ |
| public Builder() { |
| } |
| |
| /** |
| * Sets the {@link Exception} object. |
| */ |
| @SuppressLint("MissingGetterMatchingBuilder") // Exception is not parcelable. |
| public @NonNull Builder setException(@NonNull Exception value) { |
| mException = value; |
| return this; |
| } |
| |
| /** |
| * Sets error code. |
| */ |
| public @NonNull Builder setErrorCode(@ErrorCode int value) { |
| mErrorCode = value; |
| return this; |
| } |
| |
| /** |
| * Sets sub error code. |
| */ |
| public @NonNull Builder setSubErrorCode( |
| @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int value) { |
| mSubErrorCode = value; |
| return this; |
| } |
| |
| /** |
| * Set the timestamp since creation in milliseconds. |
| * @param value the timestamp since the creation in milliseconds. |
| * -1 indicates the value is unknown. |
| * @see #getTimeSinceCreatedMillis() |
| */ |
| public @NonNull Builder setTimeSinceCreatedMillis(@IntRange(from = -1) long value) { |
| mTimeSinceCreatedMillis = value; |
| return this; |
| } |
| |
| /** |
| * Sets metrics-related information that is not supported by dedicated |
| * methods. |
| * <p>It is intended to be used for backwards compatibility by the |
| * metrics infrastructure. |
| */ |
| public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) { |
| mMetricsBundle = metricsBundle; |
| return this; |
| } |
| |
| /** Builds the instance. */ |
| public @NonNull PlaybackErrorEvent build() { |
| |
| String stack; |
| if (mException.getStackTrace() != null && mException.getStackTrace().length > 0) { |
| // TODO: a better definition of the stack trace |
| stack = mException.getStackTrace()[0].toString(); |
| } else { |
| stack = null; |
| } |
| |
| PlaybackErrorEvent o = new PlaybackErrorEvent( |
| stack, |
| mErrorCode, |
| mSubErrorCode, |
| mTimeSinceCreatedMillis, |
| mMetricsBundle); |
| return o; |
| } |
| } |
| } |