[MQ] Add more media profile methods

Test: mmm
Flag: android.media.tv.flags.media_quality_fw
Bug: 374849325
Change-Id: If6084856e7f7ee409ef7e38fb7b04c1f0db6dd53
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index e413a50..8c17236 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -16,7 +16,10 @@
 
 package android.media.quality;
 
+import android.media.quality.IPictureProfileCallback;
+import android.media.quality.ISoundProfileCallback;
 import android.media.quality.PictureProfile;
+import android.media.quality.SoundProfile;
 
 /**
  * Interface for Media Quality Manager
@@ -24,8 +27,21 @@
  */
 interface IMediaQualityManager {
     PictureProfile createPictureProfile(in PictureProfile pp);
+    void updatePictureProfile(in long id, in PictureProfile pp);
+    void removePictureProfile(in long id);
     PictureProfile getPictureProfileById(in long id);
     List<PictureProfile> getPictureProfilesByPackage(in String packageName);
     List<PictureProfile> getAvailablePictureProfiles();
-    List<PictureProfile> getAvailableAllPictureProfiles();
+    List<PictureProfile> getAllPictureProfiles();
+
+    SoundProfile createSoundProfile(in SoundProfile pp);
+    void updateSoundProfile(in long id, in SoundProfile pp);
+    void removeSoundProfile(in long id);
+    SoundProfile getSoundProfileById(in long id);
+    List<SoundProfile> getSoundProfilesByPackage(in String packageName);
+    List<SoundProfile> getAvailableSoundProfiles();
+    List<SoundProfile> getAllSoundProfiles();
+
+    void registerPictureProfileCallback(in IPictureProfileCallback cb);
+    void registerSoundProfileCallback(in ISoundProfileCallback cb);
 }
\ No newline at end of file
diff --git a/media/java/android/media/quality/IPictureProfileCallback.aidl b/media/java/android/media/quality/IPictureProfileCallback.aidl
index 5deb029..05441cd 100644
--- a/media/java/android/media/quality/IPictureProfileCallback.aidl
+++ b/media/java/android/media/quality/IPictureProfileCallback.aidl
@@ -26,4 +26,5 @@
 oneway interface IPictureProfileCallback {
     void onPictureProfileAdded(in long id, in PictureProfile p);
     void onPictureProfileUpdated(in long id, in PictureProfile p);
+    void onPictureProfileRemoved(in long id, in PictureProfile p);
 }
diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/ISoundProfileCallback.aidl
new file mode 100644
index 0000000..72d1524
--- /dev/null
+++ b/media/java/android/media/quality/ISoundProfileCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.quality;
+
+import android.media.quality.SoundProfile;
+
+/**
+ * Interface to receive callbacks from IMediaQuality.
+ * @hide
+ */
+oneway interface ISoundProfileCallback {
+    void onSoundProfileAdded(in long id, in SoundProfile p);
+    void onSoundProfileUpdated(in long id, in SoundProfile p);
+    void onSoundProfileRemoved(in long id, in SoundProfile p);
+}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 03dc24d..61600ed 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -48,6 +48,8 @@
     private final Object mLock = new Object();
     // @GuardedBy("mLock")
     private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
+    // @GuardedBy("mLock")
+    private final List<SoundProfileCallbackRecord> mSpCallbackRecords = new ArrayList<>();
 
     /**
      * @hide
@@ -55,7 +57,7 @@
     public MediaQualityManager(Context context, IMediaQualityManager service) {
         mContext = context;
         mService = service;
-        IPictureProfileCallback mqCallback = new IPictureProfileCallback.Stub() {
+        IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() {
             @Override
             public void onPictureProfileAdded(long profileId, PictureProfile profile) {
                 synchronized (mLock) {
@@ -74,14 +76,60 @@
                     }
                 }
             }
+            @Override
+            public void onPictureProfileRemoved(long profileId, PictureProfile profile) {
+                synchronized (mLock) {
+                    for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
+                        // TODO: filter callback record
+                        record.postPictureProfileRemoved(profileId, profile);
+                    }
+                }
+            }
         };
+        ISoundProfileCallback spCallback = new ISoundProfileCallback.Stub() {
+            @Override
+            public void onSoundProfileAdded(long profileId, SoundProfile profile) {
+                synchronized (mLock) {
+                    for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+                        // TODO: filter callback record
+                        record.postSoundProfileAdded(profileId, profile);
+                    }
+                }
+            }
+            @Override
+            public void onSoundProfileUpdated(long profileId, SoundProfile profile) {
+                synchronized (mLock) {
+                    for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+                        // TODO: filter callback record
+                        record.postSoundProfileUpdated(profileId, profile);
+                    }
+                }
+            }
+            @Override
+            public void onSoundProfileRemoved(long profileId, SoundProfile profile) {
+                synchronized (mLock) {
+                    for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
+                        // TODO: filter callback record
+                        record.postSoundProfileRemoved(profileId, profile);
+                    }
+                }
+            }
+        };
+        try {
+            if (mService != null) {
+                mService.registerPictureProfileCallback(ppCallback);
+                mService.registerSoundProfileCallback(spCallback);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
      * Registers a {@link PictureProfileCallback}.
      * @hide
      */
-    public void registerCallback(
+    public void registerPictureProfileCallback(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull PictureProfileCallback callback) {
         Preconditions.checkNotNull(callback);
@@ -95,7 +143,7 @@
      * Unregisters the existing {@link PictureProfileCallback}.
      * @hide
      */
-    public void unregisterCallback(@NonNull final PictureProfileCallback callback) {
+    public void unregisterPictureProfileCallback(@NonNull final PictureProfileCallback callback) {
         Preconditions.checkNotNull(callback);
         synchronized (mLock) {
             for (Iterator<PictureProfileCallbackRecord> it = mPpCallbackRecords.iterator();
@@ -143,11 +191,11 @@
         }
     }
 
-    /** @SystemApi all stored picture profiles */
+    /** @SystemApi all stored picture profiles of all packages */
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
-    public List<PictureProfile> getAvailableAllPictureProfiles() {
+    public List<PictureProfile> getAllPictureProfiles() {
         try {
-            return mService.getAvailableAllPictureProfiles();
+            return mService.getAllPictureProfiles();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -167,6 +215,144 @@
         }
     }
 
+
+    /**
+     * Updates an existing picture profile and store it in the system.
+     */
+    public void updatePictureProfile(long profileId, PictureProfile pp) {
+        try {
+            mService.updatePictureProfile(profileId, pp);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Removes a picture profile from the system.
+     */
+    public void removePictureProfile(long profileId) {
+        try {
+            mService.removePictureProfile(profileId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers a {@link SoundProfileCallback}.
+     * @hide
+     */
+    public void registerSoundProfileCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull SoundProfileCallback callback) {
+        Preconditions.checkNotNull(callback);
+        Preconditions.checkNotNull(executor);
+        synchronized (mLock) {
+            mSpCallbackRecords.add(new SoundProfileCallbackRecord(callback, executor));
+        }
+    }
+
+    /**
+     * Unregisters the existing {@link SoundProfileCallback}.
+     * @hide
+     */
+    public void unregisterSoundProfileCallback(@NonNull final SoundProfileCallback callback) {
+        Preconditions.checkNotNull(callback);
+        synchronized (mLock) {
+            for (Iterator<SoundProfileCallbackRecord> it = mSpCallbackRecords.iterator();
+                    it.hasNext(); ) {
+                SoundProfileCallbackRecord record = it.next();
+                if (record.getCallback() == callback) {
+                    it.remove();
+                    break;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Gets sound profile by given profile ID.
+     * @return the corresponding sound profile if available; {@code null} if the ID doesn't
+     *         exist or the profile is not accessible to the caller.
+     */
+    public SoundProfile getSoundProfileById(long profileId) {
+        try {
+            return mService.getSoundProfileById(profileId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /** @SystemApi gets profiles that available to the given package */
+    @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+    public List<SoundProfile> getSoundProfilesByPackage(String packageName) {
+        try {
+            return mService.getSoundProfilesByPackage(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Gets profiles that available to the caller package */
+    public List<SoundProfile> getAvailableSoundProfiles() {
+        try {
+            return mService.getAvailableSoundProfiles();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @SystemApi all stored sound profiles of all packages */
+    @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
+    public List<SoundProfile> getAllSoundProfiles() {
+        try {
+            return mService.getAllSoundProfiles();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Creates a sound profile and store it in the system.
+     *
+     * @return the stored profile with an assigned profile ID.
+     */
+    public SoundProfile createSoundProfile(SoundProfile sp) {
+        try {
+            return mService.createSoundProfile(sp);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Updates an existing sound profile and store it in the system.
+     */
+    public void updateSoundProfile(long profileId, SoundProfile sp) {
+        try {
+            mService.updateSoundProfile(profileId, sp);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * Removes a sound profile from the system.
+     */
+    public void removeSoundProfile(long profileId) {
+        try {
+            mService.removeSoundProfile(profileId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private static final class PictureProfileCallbackRecord {
         private final PictureProfileCallback mCallback;
         private final Executor mExecutor;
@@ -198,6 +384,57 @@
                 }
             });
         }
+
+        public void postPictureProfileRemoved(final long id, PictureProfile profile) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onPictureProfileRemoved(id, profile);
+                }
+            });
+        }
+    }
+
+    private static final class SoundProfileCallbackRecord {
+        private final SoundProfileCallback mCallback;
+        private final Executor mExecutor;
+
+        SoundProfileCallbackRecord(SoundProfileCallback callback, Executor executor) {
+            mCallback = callback;
+            mExecutor = executor;
+        }
+
+        public SoundProfileCallback getCallback() {
+            return mCallback;
+        }
+
+        public void postSoundProfileAdded(final long id, SoundProfile profile) {
+
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onSoundProfileAdded(id, profile);
+                }
+            });
+        }
+
+        public void postSoundProfileUpdated(final long id, SoundProfile profile) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onSoundProfileUpdated(id, profile);
+                }
+            });
+        }
+
+        public void postSoundProfileRemoved(final long id, SoundProfile profile) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onSoundProfileRemoved(id, profile);
+                }
+            });
+        }
     }
 
     /**
@@ -215,5 +452,42 @@
          */
         public void onPictureProfileUpdated(long id, PictureProfile profile) {
         }
+        /**
+         * @hide
+         */
+        public void onPictureProfileRemoved(long id, PictureProfile profile) {
+        }
+        /**
+         * @hide
+         */
+        public void onError(int errorCode) {
+        }
+    }
+
+    /**
+     * Callback used to monitor status of sound profiles.
+     * @hide
+     */
+    public abstract static class SoundProfileCallback {
+        /**
+         * @hide
+         */
+        public void onSoundProfileAdded(long id, SoundProfile profile) {
+        }
+        /**
+         * @hide
+         */
+        public void onSoundProfileUpdated(long id, SoundProfile profile) {
+        }
+        /**
+         * @hide
+         */
+        public void onSoundProfileRemoved(long id, SoundProfile profile) {
+        }
+        /**
+         * @hide
+         */
+        public void onError(int errorCode) {
+        }
     }
 }
diff --git a/media/java/android/media/quality/SoundProfile.java b/media/java/android/media/quality/SoundProfile.java
index e0fcf9d..20d117b 100644
--- a/media/java/android/media/quality/SoundProfile.java
+++ b/media/java/android/media/quality/SoundProfile.java
@@ -18,6 +18,7 @@
 
 import android.annotation.FlaggedApi;
 import android.media.tv.flags.Flags;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -37,6 +38,8 @@
     private final String mInputId;
     @Nullable
     private final String mPackageName;
+    @NonNull
+    private final Bundle mParams;
 
     protected SoundProfile(Parcel in) {
         if (in.readByte() == 0) {
@@ -47,6 +50,7 @@
         mName = in.readString();
         mInputId = in.readString();
         mPackageName = in.readString();
+        mParams = in.readBundle();
     }
 
     @Override
@@ -60,6 +64,7 @@
         dest.writeString(mName);
         dest.writeString(mInputId);
         dest.writeString(mPackageName);
+        dest.writeBundle(mParams);
     }
 
     @Override
@@ -89,12 +94,14 @@
             @Nullable Long id,
             @NonNull String name,
             @Nullable String inputId,
-            @Nullable String packageName) {
+            @Nullable String packageName,
+            @NonNull Bundle params) {
         this.mId = id;
         this.mName = name;
         com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, name);
         this.mInputId = inputId;
         this.mPackageName = packageName;
+        this.mParams = params;
     }
 
     @Nullable
@@ -116,6 +123,10 @@
     public String getPackageName() {
         return mPackageName;
     }
+    @NonNull
+    public Bundle getParameters() {
+        return new Bundle(mParams);
+    }
 
     /**
      * A builder for {@link SoundProfile}
@@ -129,6 +140,8 @@
         private String mInputId;
         @Nullable
         private String mPackageName;
+        @NonNull
+        private Bundle mParams;
 
         /**
          * Creates a new Builder.
@@ -181,6 +194,14 @@
         }
 
         /**
+         * Sets profile parameters.
+         */
+        @NonNull
+        public Builder setParameters(@NonNull Bundle params) {
+            mParams = new Bundle(params);
+            return this;
+        }
+        /**
          * Builds the instance.
          */
         @NonNull
@@ -190,7 +211,8 @@
                     mId,
                     mName,
                     mInputId,
-                    mPackageName);
+                    mPackageName,
+                    mParams);
             return o;
         }
     }
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 d265b6a..57c9f51 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -18,7 +18,10 @@
 
 import android.content.Context;
 import android.media.quality.IMediaQualityManager;
+import android.media.quality.IPictureProfileCallback;
+import android.media.quality.ISoundProfileCallback;
 import android.media.quality.PictureProfile;
+import android.media.quality.SoundProfile;
 
 import com.android.server.SystemService;
 
@@ -53,6 +56,14 @@
             return pp;
         }
         @Override
+        public void updatePictureProfile(long id, PictureProfile pp) {
+            // TODO: implement
+        }
+        @Override
+        public void removePictureProfile(long id) {
+            // TODO: implement
+        }
+        @Override
         public PictureProfile getPictureProfileById(long id) {
             return null;
         }
@@ -65,8 +76,46 @@
             return new ArrayList<>();
         }
         @Override
-        public List<PictureProfile> getAvailableAllPictureProfiles() {
+        public List<PictureProfile> getAllPictureProfiles() {
             return new ArrayList<>();
         }
+
+        @Override
+        public SoundProfile createSoundProfile(SoundProfile pp) {
+            // TODO: implement
+            return pp;
+        }
+        @Override
+        public void updateSoundProfile(long id, SoundProfile pp) {
+            // TODO: implement
+        }
+        @Override
+        public void removeSoundProfile(long id) {
+            // TODO: implement
+        }
+        @Override
+        public SoundProfile getSoundProfileById(long id) {
+            return null;
+        }
+        @Override
+        public List<SoundProfile> getSoundProfilesByPackage(String packageName) {
+            return new ArrayList<>();
+        }
+        @Override
+        public List<SoundProfile> getAvailableSoundProfiles() {
+            return new ArrayList<>();
+        }
+        @Override
+        public List<SoundProfile> getAllSoundProfiles() {
+            return new ArrayList<>();
+        }
+
+
+        @Override
+        public void registerPictureProfileCallback(final IPictureProfileCallback callback) {
+        }
+        @Override
+        public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
+        }
     }
 }