Add playback track change event
Test: make;
Bug: 167036690
Change-Id: I083ca1d5e7a8e708f2f6f63e19483d7fc59c492e
diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
index 52d952c..e55678d9 100644
--- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
+++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
@@ -20,6 +20,7 @@
import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
/**
* Interface to the playback manager service.
@@ -31,4 +32,5 @@
void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
+ void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId);
}
\ No newline at end of file
diff --git a/media/java/android/media/metrics/PlaybackMetricsManager.java b/media/java/android/media/metrics/PlaybackMetricsManager.java
index 63a50ae..f48ffe7 100644
--- a/media/java/android/media/metrics/PlaybackMetricsManager.java
+++ b/media/java/android/media/metrics/PlaybackMetricsManager.java
@@ -73,6 +73,18 @@
}
/**
+ * Reports track change event.
+ * @hide
+ */
+ public void reportTrackChangeEvent(@NonNull String sessionId, TrackChangeEvent event) {
+ try {
+ mService.reportTrackChangeEvent(sessionId, event, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a playback session.
*/
public PlaybackSession createSession() {
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 061e665..0a77516 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -71,6 +71,13 @@
mManager.reportPlaybackStateEvent(mId, event);
}
+ /**
+ * Reports track change event.
+ */
+ public void reportTrackChangeEvent(TrackChangeEvent event) {
+ mManager.reportTrackChangeEvent(mId, event);
+ }
+
public @NonNull String getId() {
return mId;
}
diff --git a/media/java/android/media/metrics/TrackChangeEvent.aidl b/media/java/android/media/metrics/TrackChangeEvent.aidl
new file mode 100644
index 0000000..8fbe4a9
--- /dev/null
+++ b/media/java/android/media/metrics/TrackChangeEvent.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 TrackChangeEvent;
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
new file mode 100644
index 0000000..fff0e36
--- /dev/null
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -0,0 +1,480 @@
+/*
+ * 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.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Playback track change event.
+ * @hide
+ */
+public final class TrackChangeEvent implements Parcelable {
+ public static final int TRACK_STATE_OFF = 0;
+ public static final int TRACK_STATE_ON = 1;
+
+ public static final int TRACK_CHANGE_REASON_UNKNOWN = 0;
+ public static final int TRACK_CHANGE_REASON_OTHER = 1;
+ public static final int TRACK_CHANGE_REASON_INITIAL = 2;
+ public static final int TRACK_CHANGE_REASON_MANUAL = 3;
+ public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4;
+
+ public static final int TRACK_TYPE_AUDIO = 0;
+ public static final int TRACK_TYPE_VIDEO = 1;
+ public static final int TRACK_TYPE_TEXT = 2;
+
+ private final int mState;
+ private final int mReason;
+ private final @Nullable String mContainerMimeType;
+ private final @Nullable String mSampleMimeType;
+ private final @Nullable String mCodecName;
+ private final int mBitrate;
+ private final long mTimeSincePlaybackCreatedMillis;
+ private final int mType;
+ private final @Nullable String mLanguage;
+ private final @Nullable String mLanguageRegion;
+ private final int mChannelCount;
+ private final int mSampleRate;
+ private final int mWidth;
+ private final int mHeight;
+
+
+
+ /** @hide */
+ @IntDef(prefix = "TRACK_STATE_", value = {
+ TRACK_STATE_OFF,
+ TRACK_STATE_ON
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrackState {}
+
+ /** @hide */
+ @IntDef(prefix = "TRACK_CHANGE_REASON_", value = {
+ TRACK_CHANGE_REASON_UNKNOWN,
+ TRACK_CHANGE_REASON_OTHER,
+ TRACK_CHANGE_REASON_INITIAL,
+ TRACK_CHANGE_REASON_MANUAL,
+ TRACK_CHANGE_REASON_ADAPTIVE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrackChangeReason {}
+
+ /** @hide */
+ @IntDef(prefix = "TRACK_TYPE_", value = {
+ TRACK_TYPE_AUDIO,
+ TRACK_TYPE_VIDEO,
+ TRACK_TYPE_TEXT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrackType {}
+
+ public TrackChangeEvent(
+ int state,
+ int reason,
+ @Nullable String containerMimeType,
+ @Nullable String sampleMimeType,
+ @Nullable String codecName,
+ int bitrate,
+ long timeSincePlaybackCreatedMillis,
+ int type,
+ @Nullable String language,
+ @Nullable String languageRegion,
+ int channelCount,
+ int sampleRate,
+ int width,
+ int height) {
+ this.mState = state;
+ this.mReason = reason;
+ this.mContainerMimeType = containerMimeType;
+ this.mSampleMimeType = sampleMimeType;
+ this.mCodecName = codecName;
+ this.mBitrate = bitrate;
+ this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mType = type;
+ this.mLanguage = language;
+ this.mLanguageRegion = languageRegion;
+ this.mChannelCount = channelCount;
+ this.mSampleRate = sampleRate;
+ this.mWidth = width;
+ this.mHeight = height;
+ }
+
+ @TrackState
+ public int getTrackState() {
+ return mState;
+ }
+
+ @TrackChangeReason
+ public int getTrackChangeReason() {
+ return mReason;
+ }
+
+ public @Nullable String getContainerMimeType() {
+ return mContainerMimeType;
+ }
+
+ public @Nullable String getSampleMimeType() {
+ return mSampleMimeType;
+ }
+
+ public @Nullable String getCodecName() {
+ return mCodecName;
+ }
+
+ public int getBitrate() {
+ return mBitrate;
+ }
+
+ public long getTimeSincePlaybackCreatedMillis() {
+ return mTimeSincePlaybackCreatedMillis;
+ }
+
+ @TrackType
+ public int getTrackType() {
+ return mType;
+ }
+
+ public @Nullable String getLanguage() {
+ return mLanguage;
+ }
+
+ public @Nullable String getLanguageRegion() {
+ return mLanguageRegion;
+ }
+
+ public int getChannelCount() {
+ return mChannelCount;
+ }
+
+ public int getSampleRate() {
+ return mSampleRate;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ int flg = 0;
+ if (mContainerMimeType != null) flg |= 0x4;
+ if (mSampleMimeType != null) flg |= 0x8;
+ if (mCodecName != null) flg |= 0x10;
+ if (mLanguage != null) flg |= 0x100;
+ if (mLanguageRegion != null) flg |= 0x200;
+ dest.writeInt(flg);
+ dest.writeInt(mState);
+ dest.writeInt(mReason);
+ if (mContainerMimeType != null) dest.writeString(mContainerMimeType);
+ if (mSampleMimeType != null) dest.writeString(mSampleMimeType);
+ if (mCodecName != null) dest.writeString(mCodecName);
+ dest.writeInt(mBitrate);
+ dest.writeLong(mTimeSincePlaybackCreatedMillis);
+ dest.writeInt(mType);
+ if (mLanguage != null) dest.writeString(mLanguage);
+ if (mLanguageRegion != null) dest.writeString(mLanguageRegion);
+ dest.writeInt(mChannelCount);
+ dest.writeInt(mSampleRate);
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ /* package-private */ TrackChangeEvent(@NonNull Parcel in) {
+ int flg = in.readInt();
+ int state = in.readInt();
+ int reason = in.readInt();
+ String containerMimeType = (flg & 0x4) == 0 ? null : in.readString();
+ String sampleMimeType = (flg & 0x8) == 0 ? null : in.readString();
+ String codecName = (flg & 0x10) == 0 ? null : in.readString();
+ int bitrate = in.readInt();
+ long timeSincePlaybackCreatedMillis = in.readLong();
+ int type = in.readInt();
+ String language = (flg & 0x100) == 0 ? null : in.readString();
+ String languageRegion = (flg & 0x200) == 0 ? null : in.readString();
+ int channelCount = in.readInt();
+ int sampleRate = in.readInt();
+ int width = in.readInt();
+ int height = in.readInt();
+
+ this.mState = state;
+ this.mReason = reason;
+ this.mContainerMimeType = containerMimeType;
+ this.mSampleMimeType = sampleMimeType;
+ this.mCodecName = codecName;
+ this.mBitrate = bitrate;
+ this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mType = type;
+ this.mLanguage = language;
+ this.mLanguageRegion = languageRegion;
+ this.mChannelCount = channelCount;
+ this.mSampleRate = sampleRate;
+ this.mWidth = width;
+ this.mHeight = height;
+ }
+
+ public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR =
+ new Parcelable.Creator<TrackChangeEvent>() {
+ @Override
+ public TrackChangeEvent[] newArray(int size) {
+ return new TrackChangeEvent[size];
+ }
+
+ @Override
+ public TrackChangeEvent createFromParcel(@NonNull Parcel in) {
+ return new TrackChangeEvent(in);
+ }
+ };
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/metrics/TrackChangeEvent.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+ @Override
+ public String toString() {
+ return "TrackChangeEvent { " +
+ "state = " + mState + ", " +
+ "reason = " + mReason + ", " +
+ "containerMimeType = " + mContainerMimeType + ", " +
+ "sampleMimeType = " + mSampleMimeType + ", " +
+ "codecName = " + mCodecName + ", " +
+ "bitrate = " + mBitrate + ", " +
+ "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis + ", " +
+ "type = " + mType + ", " +
+ "language = " + mLanguage + ", " +
+ "languageRegion = " + mLanguageRegion + ", " +
+ "channelCount = " + mChannelCount + ", " +
+ "sampleRate = " + mSampleRate + ", " +
+ "width = " + mWidth + ", " +
+ "height = " + mHeight +
+ " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TrackChangeEvent that = (TrackChangeEvent) o;
+ return mState == that.mState
+ && mReason == that.mReason
+ && Objects.equals(mContainerMimeType, that.mContainerMimeType)
+ && Objects.equals(mSampleMimeType, that.mSampleMimeType)
+ && Objects.equals(mCodecName, that.mCodecName)
+ && mBitrate == that.mBitrate
+ && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis
+ && mType == that.mType
+ && Objects.equals(mLanguage, that.mLanguage)
+ && Objects.equals(mLanguageRegion, that.mLanguageRegion)
+ && mChannelCount == that.mChannelCount
+ && mSampleRate == that.mSampleRate
+ && mWidth == that.mWidth
+ && mHeight == that.mHeight;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName,
+ mBitrate, mTimeSincePlaybackCreatedMillis, mType, mLanguage, mLanguageRegion,
+ mChannelCount, mSampleRate, mWidth, mHeight);
+ }
+
+ /**
+ * A builder for {@link TrackChangeEvent}
+ */
+ public static final class Builder {
+ // TODO: check track type for the setters.
+ private int mState;
+ private int mReason;
+ private @Nullable String mContainerMimeType;
+ private @Nullable String mSampleMimeType;
+ private @Nullable String mCodecName;
+ private int mBitrate;
+ private long mTimeSincePlaybackCreatedMillis;
+ private int mType;
+ private @Nullable String mLanguage;
+ private @Nullable String mLanguageRegion;
+ private int mChannelCount;
+ private int mSampleRate;
+ private int mWidth;
+ private int mHeight;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @hide
+ */
+ public Builder(int type) {
+ mType = type;
+ }
+
+ public @NonNull Builder setTrackState(@TrackState int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mState = value;
+ return this;
+ }
+
+ public @NonNull Builder setTrackChangeReason(@TrackChangeReason int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mReason = value;
+ return this;
+ }
+
+ public @NonNull Builder setContainerMimeType(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mContainerMimeType = value;
+ return this;
+ }
+
+ public @NonNull Builder setSampleMimeType(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mSampleMimeType = value;
+ return this;
+ }
+
+ public @NonNull Builder setCodecName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mCodecName = value;
+ return this;
+ }
+
+ public @NonNull Builder setBitrate(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mBitrate = value;
+ return this;
+ }
+
+ public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40;
+ mTimeSincePlaybackCreatedMillis = value;
+ return this;
+ }
+
+ public @NonNull Builder setTrackType(@TrackType int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80;
+ mType = value;
+ return this;
+ }
+
+ public @NonNull Builder setLanguage(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100;
+ mLanguage = value;
+ return this;
+ }
+
+ public @NonNull Builder setLanguageRegion(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x200;
+ mLanguageRegion = value;
+ return this;
+ }
+
+ public @NonNull Builder setChannelCount(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x400;
+ mChannelCount = value;
+ return this;
+ }
+
+ public @NonNull Builder setSampleRate(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x800;
+ mSampleRate = value;
+ return this;
+ }
+
+ public @NonNull Builder setWidth(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1000;
+ mWidth = value;
+ return this;
+ }
+
+ public @NonNull Builder setHeight(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2000;
+ mHeight = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull TrackChangeEvent build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4000; // Mark builder used
+
+ TrackChangeEvent o = new TrackChangeEvent(
+ mState,
+ mReason,
+ mContainerMimeType,
+ mSampleMimeType,
+ mCodecName,
+ mBitrate,
+ mTimeSincePlaybackCreatedMillis,
+ mType,
+ mLanguage,
+ mLanguageRegion,
+ mChannelCount,
+ mSampleRate,
+ mWidth,
+ mHeight);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4000) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
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 2a3cc90..5fa7998 100644
--- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
@@ -22,6 +22,7 @@
import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
import android.os.Binder;
import android.util.Base64;
import android.util.StatsEvent;
@@ -120,5 +121,30 @@
.build();
StatsLog.write(statsEvent);
}
+
+ @Override
+ public void reportTrackChangeEvent(
+ String sessionId, TrackChangeEvent event, int userId) {
+ StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(321)
+ .writeString(sessionId)
+ .writeInt(event.getTrackState())
+ .writeInt(event.getTrackChangeReason())
+ .writeString(event.getContainerMimeType())
+ .writeString(event.getSampleMimeType())
+ .writeString(event.getCodecName())
+ .writeInt(event.getBitrate())
+ .writeLong(event.getTimeSincePlaybackCreatedMillis())
+ .writeInt(event.getTrackType())
+ .writeString(event.getLanguage())
+ .writeString(event.getLanguageRegion())
+ .writeInt(event.getChannelCount())
+ .writeInt(event.getSampleRate())
+ .writeInt(event.getWidth())
+ .writeInt(event.getHeight())
+ .usePooledBuffer()
+ .build();
+ StatsLog.write(statsEvent);
+ }
}
}