[MQ] Add implementation for ambient light
Test: mmm
Flag: android.media.tv.flags.media_quality_fw
Bug: 377079429
Change-Id: I9a958800de107a6880bdbffadd264e2f5229fbb4
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index aaedf21..7ff0ff4 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -53,6 +53,8 @@
void registerSoundProfileCallback(in ISoundProfileCallback cb);
void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb);
+ void unregisterAmbientBacklightCallback(in IAmbientBacklightCallback cb);
+
List<ParamCapability> getParamCapabilities(in List<String> names);
boolean isSupported();
diff --git a/services/core/Android.bp b/services/core/Android.bp
index aea16b0..b13277e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -131,6 +131,7 @@
srcs: [
":android.hardware.tv.hdmi.connection-V1-java-source",
":android.hardware.tv.hdmi.earc-V1-java-source",
+ ":android.hardware.tv.mediaquality-V1-java-source",
":statslog-art-java-gen",
":statslog-contexthub-java-gen",
":services.core-aidl-sources",
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 1f8a200..aa0da5d 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -16,8 +16,17 @@
package com.android.server.media.quality;
+import static android.media.quality.AmbientBacklightEvent.AMBIENT_BACKLIGHT_EVENT_ENABLED;
+import static android.media.quality.AmbientBacklightEvent.AMBIENT_BACKLIGHT_EVENT_DISABLED;
+import static android.media.quality.AmbientBacklightEvent.AMBIENT_BACKLIGHT_EVENT_METADATA;
+import static android.media.quality.AmbientBacklightEvent.AMBIENT_BACKLIGHT_EVENT_INTERRUPTED;
+
+import android.annotation.NonNull;
import android.content.ContentValues;
import android.content.Context;
+import android.hardware.tv.mediaquality.IMediaQuality;
+import android.media.quality.AmbientBacklightEvent;
+import android.media.quality.AmbientBacklightMetadata;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.media.quality.AmbientBacklightSettings;
@@ -29,17 +38,27 @@
import android.media.quality.ParamCapability;
import android.media.quality.PictureProfile;
import android.media.quality.SoundProfile;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Slog;
import android.os.Bundle;
import android.util.Log;
import com.android.server.SystemService;
+import com.android.server.utils.Slogf;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Locale;
/**
@@ -51,11 +70,16 @@
private static final boolean DEBUG = false;
private static final String TAG = "MediaQualityService";
private final Context mContext;
+ private IMediaQuality mMediaQuality;
+ private final Object mLock = new Object();
+ private final HalAmbientBacklightCallback mHalAmbientBacklightCallback;
+ private final Map<String, AmbientBacklightCallbackRecord> mCallbackRecords = new HashMap<>();
private final MediaQualityDbHelper mMediaQualityDbHelper;
public MediaQualityService(Context context) {
super(context);
mContext = context;
+ mHalAmbientBacklightCallback = new HalAmbientBacklightCallback();
mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
mMediaQualityDbHelper.setIdleConnectionTimeout(30);
@@ -63,6 +87,18 @@
@Override
public void onStart() {
+ IBinder binder = ServiceManager.getService(IMediaQuality.DESCRIPTOR + "/default");
+ if (binder != null) {
+ Slogf.d(TAG, "binder is not null");
+ mMediaQuality = IMediaQuality.Stub.asInterface(binder);
+ if (mMediaQuality != null) {
+ try {
+ mMediaQuality.setCallback(mHalAmbientBacklightCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set ambient backlight detector callback", e);
+ }
+ }
+ }
publishBinderService(Context.MEDIA_QUALITY_SERVICE, new BinderService());
}
@@ -288,14 +324,89 @@
@Override
public void registerAmbientBacklightCallback(IAmbientBacklightCallback callback) {
+ if (DEBUG) {
+ Slogf.d(TAG, "registerAmbientBacklightCallback");
+ }
+
+ String callingPackageName = getCallingPackageName();
+
+ synchronized (mCallbackRecords) {
+ AmbientBacklightCallbackRecord record = mCallbackRecords.get(callingPackageName);
+ if (record != null) {
+ if (record.mCallback.asBinder().equals(callback.asBinder())) {
+ Slog.w(TAG, "AmbientBacklight Callback already registered");
+ return;
+ }
+ record.release();
+ mCallbackRecords.remove(callingPackageName);
+ }
+ mCallbackRecords.put(callingPackageName,
+ new AmbientBacklightCallbackRecord(callingPackageName, callback));
+ }
+ }
+
+ @Override
+ public void unregisterAmbientBacklightCallback(IAmbientBacklightCallback callback) {
+ if (DEBUG) {
+ Slogf.d(TAG, "unregisterAmbientBacklightCallback");
+ }
+
+ synchronized (mCallbackRecords) {
+ for (AmbientBacklightCallbackRecord record : mCallbackRecords.values()) {
+ if (record.mCallback.asBinder().equals(callback.asBinder())) {
+ record.release();
+ mCallbackRecords.remove(record.mPackageName);
+ return;
+ }
+ }
+ }
}
@Override
public void setAmbientBacklightSettings(AmbientBacklightSettings settings) {
+ if (DEBUG) {
+ Slogf.d(TAG, "setAmbientBacklightSettings " + settings);
+ }
+
+ try {
+ if (mMediaQuality != null) {
+ android.hardware.tv.mediaquality.AmbientBacklightSettings halSettings =
+ new android.hardware.tv.mediaquality.AmbientBacklightSettings();
+ halSettings.packageName = getCallingPackageName();
+ halSettings.source = (byte) settings.getSource();
+ halSettings.maxFramerate = settings.getMaxFps();
+ halSettings.colorFormat = (byte) settings.getColorFormat();
+ halSettings.hZonesNumber = settings.getHorizontalZonesNumber();
+ halSettings.vZonesNumber = settings.getVerticalZonesNumber();
+ halSettings.hasLetterbox = settings.isLetterboxOmitted();
+ halSettings.threshold = settings.getThreshold();
+
+ mMediaQuality.setAmbientBacklightDetector(halSettings);
+
+ mHalAmbientBacklightCallback.setAmbientBacklightClientPackageName(
+ getCallingPackageName());
+
+ if (DEBUG) {
+ Slogf.d(TAG, "set ambient settings package: " + halSettings.packageName);
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set ambient backlight settings", e);
+ }
}
@Override
public void setAmbientBacklightEnabled(boolean enabled) {
+ if (DEBUG) {
+ Slogf.d(TAG, "setAmbientBacklightEnabled " + enabled);
+ }
+ try {
+ if (mMediaQuality != null) {
+ mMediaQuality.setAmbientBacklightDetectionEnabled(enabled);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set ambient backlight enabled", e);
+ }
}
@Override
@@ -353,4 +464,168 @@
return false;
}
}
+
+ private final class AmbientBacklightCallbackRecord implements IBinder.DeathRecipient {
+ final String mPackageName;
+ final IAmbientBacklightCallback mCallback;
+
+ AmbientBacklightCallbackRecord(@NonNull String pkgName,
+ @NonNull IAmbientBacklightCallback cb) {
+ mPackageName = pkgName;
+ mCallback = cb;
+ try {
+ mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to link to death", e);
+ }
+ }
+
+ void release() {
+ try {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Slog.e(TAG, "Failed to unlink to death", e);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mCallbackRecords) {
+ mCallbackRecords.remove(mPackageName);
+ }
+ }
+ }
+
+ private final class HalAmbientBacklightCallback
+ extends android.hardware.tv.mediaquality.IMediaQualityCallback.Stub {
+ private final Object mLock = new Object();
+ private String mAmbientBacklightClientPackageName;
+
+ void setAmbientBacklightClientPackageName(@NonNull String packageName) {
+ synchronized (mLock) {
+ if (TextUtils.equals(mAmbientBacklightClientPackageName, packageName)) {
+ return;
+ }
+ handleAmbientBacklightInterrupted();
+ mAmbientBacklightClientPackageName = packageName;
+ }
+ }
+
+ void handleAmbientBacklightInterrupted() {
+ synchronized (mCallbackRecords) {
+ if (mAmbientBacklightClientPackageName == null) {
+ Slog.e(TAG, "Invalid package name in interrupted event");
+ return;
+ }
+ AmbientBacklightCallbackRecord record = mCallbackRecords.get(
+ mAmbientBacklightClientPackageName);
+ if (record == null) {
+ Slog.e(TAG, "Callback record not found for ambient backlight");
+ return;
+ }
+ AmbientBacklightEvent event =
+ new AmbientBacklightEvent(
+ AMBIENT_BACKLIGHT_EVENT_INTERRUPTED, null);
+ try {
+ record.mCallback.onAmbientBacklightEvent(event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Deliver ambient backlight interrupted event failed", e);
+ }
+ }
+ }
+
+ void handleAmbientBacklightEnabled(boolean enabled) {
+ AmbientBacklightEvent event =
+ new AmbientBacklightEvent(
+ enabled ? AMBIENT_BACKLIGHT_EVENT_ENABLED :
+ AMBIENT_BACKLIGHT_EVENT_DISABLED, null);
+ synchronized (mCallbackRecords) {
+ for (AmbientBacklightCallbackRecord record : mCallbackRecords.values()) {
+ try {
+ record.mCallback.onAmbientBacklightEvent(event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Deliver ambient backlight enabled event failed", e);
+ }
+ }
+ }
+ }
+
+ void handleAmbientBacklightMetadataEvent(
+ @NonNull android.hardware.tv.mediaquality.AmbientBacklightMetadata
+ halMetadata) {
+ if (!TextUtils.equals(mAmbientBacklightClientPackageName,
+ halMetadata.settings.packageName)) {
+ Slog.e(TAG, "Invalid package name in metadata event");
+ return;
+ }
+
+ AmbientBacklightMetadata metadata =
+ new AmbientBacklightMetadata(
+ halMetadata.settings.packageName,
+ halMetadata.compressAlgorithm,
+ halMetadata.settings.source,
+ halMetadata.settings.colorFormat,
+ halMetadata.settings.hZonesNumber,
+ halMetadata.settings.vZonesNumber,
+ halMetadata.zonesColors);
+ AmbientBacklightEvent event =
+ new AmbientBacklightEvent(
+ AMBIENT_BACKLIGHT_EVENT_METADATA, metadata);
+
+ synchronized (mCallbackRecords) {
+ AmbientBacklightCallbackRecord record = mCallbackRecords
+ .get(halMetadata.settings.packageName);
+ if (record == null) {
+ Slog.e(TAG, "Callback record not found for ambient backlight metadata");
+ return;
+ }
+
+ try {
+ record.mCallback.onAmbientBacklightEvent(event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Deliver ambient backlight metadata event failed", e);
+ }
+ }
+ }
+
+ @Override
+ public void notifyAmbientBacklightEvent(
+ android.hardware.tv.mediaquality.AmbientBacklightEvent halEvent) {
+ synchronized (mLock) {
+ if (halEvent.getTag() == android.hardware.tv.mediaquality
+ .AmbientBacklightEvent.Tag.enabled) {
+ boolean enabled = halEvent.getEnabled();
+ if (enabled) {
+ handleAmbientBacklightEnabled(true);
+ } else {
+ handleAmbientBacklightEnabled(false);
+ }
+ } else if (halEvent.getTag() == android.hardware.tv.mediaquality
+ .AmbientBacklightEvent.Tag.metadata) {
+ handleAmbientBacklightMetadataEvent(halEvent.getMetadata());
+ } else {
+ Slog.e(TAG, "Invalid event type in ambient backlight event");
+ }
+ }
+ }
+
+ @Override
+ public synchronized String getInterfaceHash() throws android.os.RemoteException {
+ return android.hardware.tv.mediaquality.IMediaQualityCallback.Stub.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() throws android.os.RemoteException {
+ return android.hardware.tv.mediaquality.IMediaQualityCallback.Stub.VERSION;
+ }
+ }
+
+ private String getCallingPackageName() {
+ final String[] packages = mContext.getPackageManager().getPackagesForUid(
+ Binder.getCallingUid());
+ if (packages != null && packages.length > 0) {
+ return packages[0];
+ }
+ return "unknown";
+ }
}