Add initial editing metrics reporting
This covers only the event for editing ending due to
cancelation/success/failure, and the associated failure code if
applicable. The event also includes a bundle for backwards
compatibility, like all of the other media metrics events.
Bug: 297487694
Test: atest MediaMetricsAtomTests
Change-Id: I032f1d5fe9f2d11ba69d2131bea5ac57b185386f
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c136dd7..beb11fc 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -64,6 +64,7 @@
":com.android.input.flags-aconfig-java{.generated_srcjars}",
":com.android.internal.foldables.flags-aconfig-java{.generated_srcjars}",
":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
+ ":com.android.media.flags.editing-aconfig-java{.generated_srcjars}",
":com.android.net.flags-aconfig-java{.generated_srcjars}",
":com.android.net.thread.flags-aconfig-java{.generated_srcjars}",
":com.android.server.flags.services-aconfig-java{.generated_srcjars}",
@@ -538,6 +539,21 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Media Editing
+aconfig_declarations {
+ name: "com.android.media.flags.editing-aconfig",
+ package: "com.android.media.editing.flags",
+ srcs: [
+ "media/java/android/media/flags/editing.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "com.android.media.flags.editing-aconfig-java",
+ aconfig_declarations: "com.android.media.flags.editing-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Media TV
aconfig_declarations {
name: "android.media.tv.flags-aconfig",
diff --git a/core/api/current.txt b/core/api/current.txt
index 987b5c6..9afd4ef 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -25715,9 +25715,48 @@
field public static final String KEY_STATSD_ATOM = "bundlesession-statsd-atom";
}
+ @FlaggedApi("com.android.media.editing.flags.add_media_metrics_editing") public final class EditingEndedEvent extends android.media.metrics.Event implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getErrorCode();
+ method public int getFinalState();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.EditingEndedEvent> CREATOR;
+ field public static final int ERROR_CODE_AUDIO_PROCESSING_FAILED = 18; // 0x12
+ field public static final int ERROR_CODE_DECODER_INIT_FAILED = 11; // 0xb
+ field public static final int ERROR_CODE_DECODING_FAILED = 12; // 0xc
+ field public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 13; // 0xd
+ field public static final int ERROR_CODE_ENCODER_INIT_FAILED = 14; // 0xe
+ field public static final int ERROR_CODE_ENCODING_FAILED = 15; // 0xf
+ field public static final int ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED = 16; // 0x10
+ field public static final int ERROR_CODE_FAILED_RUNTIME_CHECK = 2; // 0x2
+ field public static final int ERROR_CODE_IO_BAD_HTTP_STATUS = 6; // 0x6
+ field public static final int ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED = 9; // 0x9
+ field public static final int ERROR_CODE_IO_FILE_NOT_FOUND = 7; // 0x7
+ field public static final int ERROR_CODE_IO_NETWORK_CONNECTION_FAILED = 4; // 0x4
+ field public static final int ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT = 5; // 0x5
+ field public static final int ERROR_CODE_IO_NO_PERMISSION = 8; // 0x8
+ field public static final int ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE = 10; // 0xa
+ field public static final int ERROR_CODE_IO_UNSPECIFIED = 3; // 0x3
+ field public static final int ERROR_CODE_MUXING_FAILED = 19; // 0x13
+ field public static final int ERROR_CODE_NONE = 1; // 0x1
+ field public static final int ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED = 17; // 0x11
+ field public static final int FINAL_STATE_CANCELED = 2; // 0x2
+ field public static final int FINAL_STATE_ERROR = 3; // 0x3
+ field public static final int FINAL_STATE_SUCCEEDED = 1; // 0x1
+ }
+
+ @FlaggedApi("com.android.media.editing.flags.add_media_metrics_editing") public static final class EditingEndedEvent.Builder {
+ ctor public EditingEndedEvent.Builder(int);
+ method @NonNull public android.media.metrics.EditingEndedEvent build();
+ method @NonNull public android.media.metrics.EditingEndedEvent.Builder setErrorCode(int);
+ method @NonNull public android.media.metrics.EditingEndedEvent.Builder setMetricsBundle(@NonNull android.os.Bundle);
+ method @NonNull public android.media.metrics.EditingEndedEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
+ }
+
public final class EditingSession implements java.lang.AutoCloseable {
method public void close();
method @NonNull public android.media.metrics.LogSessionId getSessionId();
+ method @FlaggedApi("com.android.media.editing.flags.add_media_metrics_editing") public void reportEditingEndedEvent(@NonNull android.media.metrics.EditingEndedEvent);
}
public abstract class Event {
diff --git a/media/java/android/media/flags/editing.aconfig b/media/java/android/media/flags/editing.aconfig
new file mode 100644
index 0000000..c3997e9
--- /dev/null
+++ b/media/java/android/media/flags/editing.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.media.editing.flags"
+
+flag {
+ name: "add_media_metrics_editing"
+ namespace: "media_solutions"
+ description: "Add media metrics for transcoding/editing events."
+ bug: "297487694"
+}
diff --git a/media/java/android/media/metrics/EditingEndedEvent.aidl b/media/java/android/media/metrics/EditingEndedEvent.aidl
new file mode 100644
index 0000000..e099dea
--- /dev/null
+++ b/media/java/android/media/metrics/EditingEndedEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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 EditingEndedEvent;
diff --git a/media/java/android/media/metrics/EditingEndedEvent.java b/media/java/android/media/metrics/EditingEndedEvent.java
new file mode 100644
index 0000000..72e6db8
--- /dev/null
+++ b/media/java/android/media/metrics/EditingEndedEvent.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2024 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 static com.android.media.editing.flags.Flags.FLAG_ADD_MEDIA_METRICS_EDITING;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.util.Objects;
+
+/** Event for an editing operation having ended. */
+@FlaggedApi(FLAG_ADD_MEDIA_METRICS_EDITING)
+public final class EditingEndedEvent extends Event implements Parcelable {
+
+ // The special value 0 is reserved for the field being unspecified in the proto.
+
+ /** The editing operation was successful. */
+ public static final int FINAL_STATE_SUCCEEDED = 1;
+
+ /** The editing operation was canceled. */
+ public static final int FINAL_STATE_CANCELED = 2;
+
+ /** The editing operation failed due to an error. */
+ public static final int FINAL_STATE_ERROR = 3;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"FINAL_STATE_"},
+ value = {
+ FINAL_STATE_SUCCEEDED,
+ FINAL_STATE_CANCELED,
+ FINAL_STATE_ERROR,
+ })
+ @Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ public @interface FinalState {}
+
+ private final @FinalState int mFinalState;
+
+ // The special value 0 is reserved for the field being unspecified in the proto.
+
+ /** Special value representing that no error occurred. */
+ public static final int ERROR_CODE_NONE = 1;
+
+ /** Error code for unexpected runtime errors. */
+ public static final int ERROR_CODE_FAILED_RUNTIME_CHECK = 2;
+
+ /** Error code for non-specific errors during input/output. */
+ public static final int ERROR_CODE_IO_UNSPECIFIED = 3;
+
+ /** Error code for network connection failures. */
+ public static final int ERROR_CODE_IO_NETWORK_CONNECTION_FAILED = 4;
+
+ /** Error code for network timeouts. */
+ public static final int ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT = 5;
+
+ /** Caused by an HTTP server returning an unexpected HTTP response status code. */
+ public static final int ERROR_CODE_IO_BAD_HTTP_STATUS = 6;
+
+ /** Caused by a non-existent file. */
+ public static final int ERROR_CODE_IO_FILE_NOT_FOUND = 7;
+
+ /**
+ * Caused by lack of permission to perform an IO operation. For example, lack of permission to
+ * access internet or external storage.
+ */
+ public static final int ERROR_CODE_IO_NO_PERMISSION = 8;
+
+ /** */
+ public static final int ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED = 9;
+
+ /** Caused by reading data out of the data bounds. */
+ public static final int ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE = 10;
+
+ /** Caused by a decoder initialization failure. */
+ public static final int ERROR_CODE_DECODER_INIT_FAILED = 11;
+
+ /** Caused by a failure while trying to decode media samples. */
+ public static final int ERROR_CODE_DECODING_FAILED = 12;
+
+ /** Caused by trying to decode content whose format is not supported. */
+ public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 13;
+
+ /** Caused by an encoder initialization failure. */
+ public static final int ERROR_CODE_ENCODER_INIT_FAILED = 14;
+
+ /** Caused by a failure while trying to encode media samples. */
+ public static final int ERROR_CODE_ENCODING_FAILED = 15;
+
+ /** Caused by trying to encode content whose format is not supported. */
+ public static final int ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED = 16;
+
+ /** Caused by a video frame processing failure. */
+ public static final int ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED = 17;
+
+ /** Caused by an audio processing failure. */
+ public static final int ERROR_CODE_AUDIO_PROCESSING_FAILED = 18;
+
+ /** Caused by a failure while muxing media samples. */
+ public static final int ERROR_CODE_MUXING_FAILED = 19;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"ERROR_CODE_"},
+ value = {
+ ERROR_CODE_NONE,
+ ERROR_CODE_FAILED_RUNTIME_CHECK,
+ ERROR_CODE_IO_UNSPECIFIED,
+ ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
+ ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT,
+ ERROR_CODE_IO_BAD_HTTP_STATUS,
+ ERROR_CODE_IO_FILE_NOT_FOUND,
+ ERROR_CODE_IO_NO_PERMISSION,
+ ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED,
+ ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE,
+ ERROR_CODE_DECODER_INIT_FAILED,
+ ERROR_CODE_DECODING_FAILED,
+ ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
+ ERROR_CODE_ENCODER_INIT_FAILED,
+ ERROR_CODE_ENCODING_FAILED,
+ ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED,
+ ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED,
+ ERROR_CODE_AUDIO_PROCESSING_FAILED,
+ ERROR_CODE_MUXING_FAILED,
+ })
+ @Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ public @interface ErrorCode {}
+
+ private final @ErrorCode int mErrorCode;
+ @SuppressWarnings("HidingField") // Hiding field from superclass as for playback events.
+ private final long mTimeSinceCreatedMillis;
+
+ private EditingEndedEvent(
+ @FinalState int finalState,
+ @ErrorCode int errorCode,
+ long timeSinceCreatedMillis,
+ @NonNull Bundle extras) {
+ mFinalState = finalState;
+ mErrorCode = errorCode;
+ mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ mMetricsBundle = extras.deepCopy();
+ }
+
+ /** Returns the state of the editing session when it ended. */
+ @FinalState
+ public int getFinalState() {
+ return mFinalState;
+ }
+
+ /** Returns the error code for a {@linkplain #FINAL_STATE_ERROR failed} editing session. */
+ @ErrorCode
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ /**
+ * Gets the elapsed time since creating of the editing session, in milliseconds, or -1 if
+ * unknown.
+ *
+ * @return The elapsed time since creating the editing session, in milliseconds, or -1 if
+ * unknown.
+ * @see LogSessionId
+ * @see EditingSession
+ */
+ @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
+ @NonNull
+ public String toString() {
+ return "PlaybackErrorEvent { "
+ + "finalState = "
+ + mFinalState
+ + ", "
+ + "errorCode = "
+ + mErrorCode
+ + ", "
+ + "timeSinceCreatedMillis = "
+ + mTimeSinceCreatedMillis
+ + " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ EditingEndedEvent that = (EditingEndedEvent) o;
+ return mFinalState == that.mFinalState
+ && mErrorCode == that.mErrorCode
+ && mTimeSinceCreatedMillis == that.mTimeSinceCreatedMillis;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFinalState, mErrorCode, mTimeSinceCreatedMillis);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mFinalState);
+ dest.writeInt(mErrorCode);
+ dest.writeLong(mTimeSinceCreatedMillis);
+ dest.writeBundle(mMetricsBundle);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private EditingEndedEvent(@NonNull Parcel in) {
+ int finalState = in.readInt();
+ int errorCode = in.readInt();
+ long timeSinceCreatedMillis = in.readLong();
+ Bundle metricsBundle = in.readBundle();
+
+ mFinalState = finalState;
+ mErrorCode = errorCode;
+ mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ mMetricsBundle = metricsBundle;
+ }
+
+ public static final @NonNull Creator<EditingEndedEvent> CREATOR =
+ new Creator<>() {
+ @Override
+ public EditingEndedEvent[] newArray(int size) {
+ return new EditingEndedEvent[size];
+ }
+
+ @Override
+ public EditingEndedEvent createFromParcel(@NonNull Parcel in) {
+ return new EditingEndedEvent(in);
+ }
+ };
+
+ /** Builder for {@link EditingEndedEvent} */
+ @FlaggedApi(FLAG_ADD_MEDIA_METRICS_EDITING)
+ public static final class Builder {
+ private final @FinalState int mFinalState;
+ private @ErrorCode int mErrorCode;
+ private long mTimeSinceCreatedMillis;
+ private Bundle mMetricsBundle;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param finalState The state of the editing session when it ended.
+ */
+ public Builder(@FinalState int finalState) {
+ mFinalState = finalState;
+ mErrorCode = ERROR_CODE_NONE;
+ mTimeSinceCreatedMillis = -1;
+ mMetricsBundle = new Bundle();
+ }
+
+ /**
+ * Sets the elapsed time since creating the editing session, in milliseconds.
+ *
+ * @param timeSinceCreatedMillis The elapsed time since creating the editing session, in
+ * milliseconds, or -1 if the value is unknown.
+ * @see #getTimeSinceCreatedMillis()
+ */
+ public @NonNull Builder setTimeSinceCreatedMillis(
+ @IntRange(from = -1) long timeSinceCreatedMillis) {
+ mTimeSinceCreatedMillis = timeSinceCreatedMillis;
+ return this;
+ }
+
+ /** Sets the error code for a {@linkplain #FINAL_STATE_ERROR failed} editing session. */
+ public @NonNull Builder setErrorCode(@ErrorCode int value) {
+ mErrorCode = value;
+ return this;
+ }
+
+ /**
+ * Sets metrics-related information that is not supported by dedicated methods.
+ *
+ * <p>Used for backwards compatibility by the metrics infrastructure.
+ */
+ public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) {
+ mMetricsBundle = metricsBundle;
+ return this;
+ }
+
+ /** Builds an instance. */
+ public @NonNull EditingEndedEvent build() {
+ return new EditingEndedEvent(
+ mFinalState, mErrorCode, mTimeSinceCreatedMillis, mMetricsBundle);
+ }
+ }
+}
diff --git a/media/java/android/media/metrics/EditingSession.java b/media/java/android/media/metrics/EditingSession.java
index 2ddf623b..964e12c 100644
--- a/media/java/android/media/metrics/EditingSession.java
+++ b/media/java/android/media/metrics/EditingSession.java
@@ -16,6 +16,9 @@
package android.media.metrics;
+import static com.android.media.editing.flags.Flags.FLAG_ADD_MEDIA_METRICS_EDITING;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -24,7 +27,8 @@
import java.util.Objects;
/**
- * An instances of this class represents a session of media editing.
+ * Represents a session of media editing, for example, transcoding between formats, transmuxing or
+ * applying trimming or audio/video effects to a stream.
*/
public final class EditingSession implements AutoCloseable {
private final @NonNull String mId;
@@ -40,6 +44,13 @@
mLogSessionId = new LogSessionId(mId);
}
+ /** Reports that an editing operation ended. */
+ @FlaggedApi(FLAG_ADD_MEDIA_METRICS_EDITING)
+ public void reportEditingEndedEvent(@NonNull EditingEndedEvent editingEndedEvent) {
+ mManager.reportEditingEndedEvent(mId, editingEndedEvent);
+ }
+
+ /** Returns the identifier for logging this session. */
public @NonNull LogSessionId getSessionId() {
return mLogSessionId;
}
diff --git a/media/java/android/media/metrics/IMediaMetricsManager.aidl b/media/java/android/media/metrics/IMediaMetricsManager.aidl
index 51b1cc2..e07ca67 100644
--- a/media/java/android/media/metrics/IMediaMetricsManager.aidl
+++ b/media/java/android/media/metrics/IMediaMetricsManager.aidl
@@ -16,6 +16,7 @@
package android.media.metrics;
+import android.media.metrics.EditingEndedEvent;
import android.media.metrics.NetworkEvent;
import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
@@ -24,7 +25,7 @@
import android.os.PersistableBundle;
/**
- * Interface to the playback manager service.
+ * Interface to the media metrics manager service.
* @hide
*/
interface IMediaMetricsManager {
@@ -37,6 +38,8 @@
void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId);
+ void reportEditingEndedEvent(in String sessionId, in EditingEndedEvent event, int userId);
+
String getTranscodingSessionId(int userId);
String getEditingSessionId(int userId);
String getBundleSessionId(int userId);
diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java
index 0898874..622b0c1 100644
--- a/media/java/android/media/metrics/MediaMetricsManager.java
+++ b/media/java/android/media/metrics/MediaMetricsManager.java
@@ -193,4 +193,18 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Reports the event of an editing session ending.
+ *
+ * @hide
+ */
+ public void reportEditingEndedEvent(
+ @NonNull String sessionId, EditingEndedEvent editingEndedEvent) {
+ try {
+ mService.reportEditingEndedEvent(sessionId, editingEndedEvent, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index df612e6..bbe6d3a 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -20,6 +20,7 @@
import android.content.pm.PackageManager;
import android.media.MediaMetrics;
import android.media.metrics.BundleSession;
+import android.media.metrics.EditingEndedEvent;
import android.media.metrics.IMediaMetricsManager;
import android.media.metrics.NetworkEvent;
import android.media.metrics.PlaybackErrorEvent;
@@ -346,6 +347,24 @@
StatsLog.write(statsEvent);
}
+ @Override
+ public void reportEditingEndedEvent(String sessionId, EditingEndedEvent event, int userId) {
+ int level = loggingLevel();
+ if (level == LOGGING_LEVEL_BLOCKED) {
+ return;
+ }
+ StatsEvent statsEvent =
+ StatsEvent.newBuilder()
+ .setAtomId(798)
+ .writeString(sessionId)
+ .writeInt(event.getFinalState())
+ .writeInt(event.getErrorCode())
+ .writeLong(event.getTimeSinceCreatedMillis())
+ .usePooledBuffer()
+ .build();
+ StatsLog.write(statsEvent);
+ }
+
private int loggingLevel() {
synchronized (mLock) {
int uid = Binder.getCallingUid();