From c35d227793c91a8704b31815d3dd7a4724291ccc Mon Sep 17 00:00:00 2001 From: shubang Date: Wed, 2 Dec 2020 17:03:05 -0800 Subject: [PATCH] Add playback error event Test: make; Bug: 167036690 Change-Id: I38d01941527186ec4ca69ea4c315f4030096fc29 --- .../metrics/IPlaybackMetricsManager.aidl | 2 + .../media/metrics/PlaybackErrorEvent.aidl | 19 ++ .../media/metrics/PlaybackErrorEvent.java | 234 ++++++++++++++++++ .../media/metrics/PlaybackMetricsManager.java | 12 + .../media/metrics/PlaybackSession.java | 7 + .../PlaybackMetricsManagerService.java | 18 ++ 6 files changed, 292 insertions(+) create mode 100644 media/java/android/media/metrics/PlaybackErrorEvent.aidl create mode 100644 media/java/android/media/metrics/PlaybackErrorEvent.java diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl index 47debe90c854c..85c93612e7e2c 100644 --- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl +++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl @@ -16,6 +16,7 @@ package android.media.metrics; +import android.media.metrics.PlaybackErrorEvent; import android.media.metrics.PlaybackMetrics; /** @@ -25,4 +26,5 @@ import android.media.metrics.PlaybackMetrics; interface IPlaybackMetricsManager { void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId); String getSessionId(int userId); + void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId); } \ No newline at end of file diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.aidl b/media/java/android/media/metrics/PlaybackErrorEvent.aidl new file mode 100644 index 0000000000000..b0d6b4beae573 --- /dev/null +++ b/media/java/android/media/metrics/PlaybackErrorEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.metrics; + +parcelable PlaybackErrorEvent; diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java new file mode 100644 index 0000000000000..db7000536299b --- /dev/null +++ b/media/java/android/media/metrics/PlaybackErrorEvent.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.metrics; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.util.Objects; + +/** + * Playback error event. + * @hide + */ +public final class PlaybackErrorEvent implements Parcelable { + public static final int ERROR_CODE_UNKNOWN = 0; + public static final int ERROR_CODE_OTHER = 1; + public static final int ERROR_CODE_RUNTIME = 2; + + private final @Nullable String mExceptionStack; + private final int mErrorCode; + private final int mSubErrorCode; + private final long mTimeSincePlaybackCreatedMillis; + + + /** @hide */ + // TODO: more error types + @IntDef(prefix = "ERROR_CODE_", value = { + ERROR_CODE_UNKNOWN, + ERROR_CODE_OTHER, + ERROR_CODE_RUNTIME + }) + @Retention(java.lang.annotation.RetentionPolicy.SOURCE) + public @interface ErrorCode {} + + /** + * Creates a new PlaybackErrorEvent. + * + * @hide + */ + public PlaybackErrorEvent( + @Nullable String exceptionStack, + int errorCode, + int subErrorCode, + long timeSincePlaybackCreatedMillis) { + this.mExceptionStack = exceptionStack; + this.mErrorCode = errorCode; + this.mSubErrorCode = subErrorCode; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + } + + /** @hide */ + @Nullable + public String getExceptionStack() { + return mExceptionStack; + } + + @ErrorCode + public int getErrorCode() { + return mErrorCode; + } + + public int getSubErrorCode() { + return mSubErrorCode; + } + + public long getTimeSincePlaybackCreatedMillis() { + return mTimeSincePlaybackCreatedMillis; + } + + @Override + public String toString() { + return "PlaybackErrorEvent { " + + "exceptionStack = " + mExceptionStack + ", " + + "errorCode = " + mErrorCode + ", " + + "subErrorCode = " + mSubErrorCode + ", " + + "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis + + " }"; + } + + @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 + && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis; + } + + @Override + public int hashCode() { + return Objects.hash(mExceptionStack, mErrorCode, mSubErrorCode, + mTimeSincePlaybackCreatedMillis); + } + + @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(mTimeSincePlaybackCreatedMillis); + } + + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + /* package-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 timeSincePlaybackCreatedMillis = in.readLong(); + + this.mExceptionStack = exceptionStack; + this.mErrorCode = errorCode; + this.mSubErrorCode = subErrorCode; + this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @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; + private int mSubErrorCode; + private long mTimeSincePlaybackCreatedMillis; + + /** + * Creates a new Builder. + * + * @hide + */ + public Builder( + @Nullable Exception exception, + int errorCode, + int subErrorCode, + long timeSincePlaybackCreatedMillis) { + mException = exception; + mErrorCode = errorCode; + mSubErrorCode = subErrorCode; + mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis; + } + + /** + * Sets the {@link Exception} object. + */ + 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(int value) { + mSubErrorCode = value; + return this; + } + + /** + * Set the timestamp in milliseconds. + */ + public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) { + mTimeSincePlaybackCreatedMillis = value; + 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, + mTimeSincePlaybackCreatedMillis); + return o; + } + } +} diff --git a/media/java/android/media/metrics/PlaybackMetricsManager.java b/media/java/android/media/metrics/PlaybackMetricsManager.java index d51ff473696d2..b778bbd6fa1d9 100644 --- a/media/java/android/media/metrics/PlaybackMetricsManager.java +++ b/media/java/android/media/metrics/PlaybackMetricsManager.java @@ -61,4 +61,16 @@ public class PlaybackMetricsManager { throw e.rethrowFromSystemServer(); } } + + /** + * Reports error event. + * @hide + */ + public void reportPlaybackErrorEvent(@NonNull String sessionId, PlaybackErrorEvent event) { + try { + mService.reportPlaybackErrorEvent(sessionId, event, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java index 4ad89067952c4..07f4fbc219c73 100644 --- a/media/java/android/media/metrics/PlaybackSession.java +++ b/media/java/android/media/metrics/PlaybackSession.java @@ -50,6 +50,13 @@ public final class PlaybackSession implements AutoCloseable { mManager.reportPlaybackMetrics(mId, metrics); } + /** + * Reports error event. + */ + public void reportPlaybackErrorEvent(PlaybackErrorEvent event) { + mManager.reportPlaybackErrorEvent(mId, event); + } + public @NonNull String getId() { return mId; } diff --git a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java index 9c68349af7d5e..3e40b2756b10a 100644 --- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java +++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java @@ -18,8 +18,11 @@ package com.android.server.media.metrics; import android.content.Context; import android.media.metrics.IPlaybackMetricsManager; +import android.media.metrics.PlaybackErrorEvent; import android.media.metrics.PlaybackMetrics; import android.util.Base64; +import android.util.StatsEvent; +import android.util.StatsLog; import com.android.server.SystemService; @@ -60,5 +63,20 @@ public final class PlaybackMetricsManagerService extends SystemService { String id = Base64.encodeToString(byteId, Base64.DEFAULT); return id; } + + @Override + public void reportPlaybackErrorEvent( + String sessionId, PlaybackErrorEvent event, int userId) { + StatsEvent statsEvent = StatsEvent.newBuilder() + .setAtomId(323) + .writeString(sessionId) + .writeString(event.getExceptionStack()) + .writeInt(event.getErrorCode()) + .writeInt(event.getSubErrorCode()) + .writeLong(event.getTimeSincePlaybackCreatedMillis()) + .usePooledBuffer() + .build(); + StatsLog.write(statsEvent); + } } }