Merge changes I962a8884,If095b33c into main

* changes:
  [Media Quality] Add Temp Id Map to Sound Profile and refactor
  [Media Quality] Implement Remove Picture Profile API & temp map functionality
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 0820615..ffa259b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -221,6 +221,7 @@
         "securebox",
         "apache-commons-math",
         "battery_saver_flag_lib",
+        "guava",
         "notification_flags_lib",
         "power_hint_flags_lib",
         "biometrics_flags_lib",
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 65d0ab3..14eeb3dc 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -35,6 +35,9 @@
 
 import com.android.server.SystemService;
 
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -43,6 +46,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.stream.Collectors;
+import java.util.UUID;
 
 /**
  * This service manage picture profile and sound profile for TV setting. Also communicates with the
@@ -54,10 +58,14 @@
     private static final String TAG = "MediaQualityService";
     private final Context mContext;
     private final MediaQualityDbHelper mMediaQualityDbHelper;
+    private final BiMap<Long, String> mPictureProfileTempIdMap;
+    private final BiMap<Long, String> mSoundProfileTempIdMap;
 
     public MediaQualityService(Context context) {
         super(context);
         mContext = context;
+        mPictureProfileTempIdMap = HashBiMap.create();
+        mSoundProfileTempIdMap = HashBiMap.create();
         mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
         mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true);
         mMediaQualityDbHelper.setIdleConnectionTimeout(30);
@@ -80,11 +88,14 @@
             values.put(BaseParameters.PARAMETER_NAME, pp.getName());
             values.put(BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
             values.put(BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
-            values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters()));
+            values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(pp.getParameters()));
 
             // id is auto-generated by SQLite upon successful insertion of row
-            long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, null, values);
-            return new PictureProfile.Builder(pp).setProfileId(Long.toString(id)).build();
+            Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+                    null, values);
+            populateTempIdMap(mPictureProfileTempIdMap, id);
+            pp.setProfileId(mPictureProfileTempIdMap.get(id));
+            return pp;
         }
 
         @Override
@@ -94,26 +105,27 @@
 
         @Override
         public void removePictureProfile(String id, int userId) {
-            // TODO: implement
+            Long intId = mPictureProfileTempIdMap.inverse().get(id);
+            if (intId != null) {
+                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+                String selection = BaseParameters.PARAMETER_ID + " = ?";
+                String[] selectionArgs = {Long.toString(intId)};
+                db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
+                        selectionArgs);
+                mPictureProfileTempIdMap.remove(intId);
+            }
         }
 
         @Override
         public PictureProfile getPictureProfile(int type, String name, int userId) {
-            SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
-
             String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
                     + BaseParameters.PARAMETER_NAME + " = ?";
             String[] selectionArguments = {Integer.toString(type), name};
 
             try (
-                    Cursor cursor = db.query(
+                    Cursor cursor = getCursorAfterQuerying(
                             mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
-                            getAllPictureProfileColumns(),
-                            selection,
-                            selectionArguments,
-                            /*groupBy=*/ null,
-                            /*having=*/ null,
-                            /*orderBy=*/ null)
+                            getAllMediaProfileColumns(), selection, selectionArguments)
             ) {
                 int count = cursor.getCount();
                 if (count == 0) {
@@ -122,93 +134,19 @@
                 if (count > 1) {
                     Log.wtf(TAG, String.format(Locale.US, "%d entries found for type=%d and name=%s"
                                     + " in %s. Should only ever be 0 or 1.", count, type, name,
-                                    mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
+                            mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
                     return null;
                 }
                 cursor.moveToFirst();
-                return getPictureProfileFromCursor(cursor);
+                return getPictureProfileWithTempIdFromCursor(cursor);
             }
         }
 
-        private String bundleToJson(PersistableBundle bundle) {
-            JSONObject jsonObject = new JSONObject();
-            if (bundle == null) {
-                return jsonObject.toString();
-            }
-            for (String key : bundle.keySet()) {
-                try {
-                    jsonObject.put(key, bundle.getString(key));
-                } catch (JSONException e) {
-                    Log.e(TAG, "Unable to serialize ", e);
-                }
-            }
-            return jsonObject.toString();
-        }
-
-        private PersistableBundle jsonToBundle(String jsonString) {
-            JSONObject jsonObject = null;
-            PersistableBundle bundle = new PersistableBundle();
-
-            try {
-                jsonObject = new JSONObject(jsonString);
-
-                Iterator<String> keys = jsonObject.keys();
-                while (keys.hasNext()) {
-                    String key = keys.next();
-                    Object value = jsonObject.get(key);
-
-                    if (value instanceof String) {
-                        bundle.putString(key, (String) value);
-                    } else if (value instanceof Integer) {
-                        bundle.putInt(key, (Integer) value);
-                    } else if (value instanceof Boolean) {
-                        bundle.putBoolean(key, (Boolean) value);
-                    } else if (value instanceof Double) {
-                        bundle.putDouble(key, (Double) value);
-                    } else if (value instanceof Long) {
-                        bundle.putLong(key, (Long) value);
-                    }
-                }
-            } catch (JSONException e) {
-                throw new RuntimeException(e);
-            }
-
-            return bundle;
-        }
-
-        private String[] getAllPictureProfileColumns() {
-            return new String[]{
-                    BaseParameters.PARAMETER_ID,
-                    BaseParameters.PARAMETER_TYPE,
-                    BaseParameters.PARAMETER_NAME,
-                    BaseParameters.PARAMETER_INPUT_ID,
-                    BaseParameters.PARAMETER_PACKAGE,
-                    mMediaQualityDbHelper.SETTINGS
-            };
-        }
-
-        private PictureProfile getPictureProfileFromCursor(Cursor cursor) {
-            String returnId = cursor.getString(cursor.getColumnIndexOrThrow(
-                    BaseParameters.PARAMETER_ID));
-            int type = cursor.getInt(cursor.getColumnIndexOrThrow(
-                    BaseParameters.PARAMETER_TYPE));
-            String name = cursor.getString(cursor.getColumnIndexOrThrow(
-                    BaseParameters.PARAMETER_NAME));
-            String inputId = cursor.getString(cursor.getColumnIndexOrThrow(
-                    BaseParameters.PARAMETER_INPUT_ID));
-            String packageName = cursor.getString(cursor.getColumnIndexOrThrow(
-                    BaseParameters.PARAMETER_PACKAGE));
-            String settings = cursor.getString(
-                    cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS));
-            return new PictureProfile(returnId, type, name, inputId,
-                    packageName, jsonToBundle(settings));
-        }
-
         @Override
         public List<PictureProfile> getPictureProfilesByPackage(String packageName, int userId) {
             String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {packageName};
-            return getPictureProfilesBasedOnConditions(getAllPictureProfileColumns(), selection,
+            return getPictureProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
                     selectionArguments);
         }
 
@@ -219,36 +157,15 @@
 
         @Override
         public List<String> getPictureProfilePackageNames(int userId) {
-            String [] column = {BaseParameters.PARAMETER_NAME};
+            String [] column = {BaseParameters.PARAMETER_PACKAGE};
             List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
                     null, null);
             return pictureProfiles.stream()
-                    .map(PictureProfile::getName)
+                    .map(PictureProfile::getPackageName)
+                    .distinct()
                     .collect(Collectors.toList());
         }
 
-        private List<PictureProfile> getPictureProfilesBasedOnConditions(String[] columns,
-                String selection, String[] selectionArguments) {
-            SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
-
-            try (
-                    Cursor cursor = db.query(
-                            mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
-                            columns,
-                            selection,
-                            selectionArguments,
-                            /*groupBy=*/ null,
-                            /*having=*/ null,
-                            /*orderBy=*/ null)
-            ) {
-                List<PictureProfile> pictureProfiles = new ArrayList<>();
-                while (cursor.moveToNext()) {
-                    pictureProfiles.add(getPictureProfileFromCursor(cursor));
-                }
-                return pictureProfiles;
-            }
-        }
-
         @Override
         public PictureProfileHandle getPictureProfileHandle(String id, int userId) {
             return null;
@@ -259,13 +176,18 @@
             SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
             ContentValues values = new ContentValues();
+            values.put(BaseParameters.PARAMETER_TYPE, sp.getProfileType());
             values.put(BaseParameters.PARAMETER_NAME, sp.getName());
             values.put(BaseParameters.PARAMETER_PACKAGE, sp.getPackageName());
             values.put(BaseParameters.PARAMETER_INPUT_ID, sp.getInputId());
-            values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(sp.getParameters()));
+            values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(sp.getParameters()));
 
-            long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
-            return new SoundProfile.Builder(sp).setProfileId(Long.toString(id)).build();
+            // id is auto-generated by SQLite upon successful insertion of row
+            Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+                    null, values);
+            populateTempIdMap(mSoundProfileTempIdMap, id);
+            sp.setProfileId(mSoundProfileTempIdMap.get(id));
+            return sp;
         }
 
         @Override
@@ -275,28 +197,27 @@
 
         @Override
         public void removeSoundProfile(String id, int userId) {
-            SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
-            String selection = BaseParameters.PARAMETER_ID + " = ?";
-            String[] selectionArgs = {id};
-            db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, selectionArgs);
+            Long intId = mSoundProfileTempIdMap.inverse().get(id);
+            if (intId != null) {
+                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+                String selection = BaseParameters.PARAMETER_ID + " = ?";
+                String[] selectionArgs = {Long.toString(intId)};
+                db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
+                        selectionArgs);
+                mSoundProfileTempIdMap.remove(intId);
+            }
         }
 
         @Override
         public SoundProfile getSoundProfile(int type, String id, int userId) {
-            SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
-
-            String selection = BaseParameters.PARAMETER_ID + " = ?";
-            String[] selectionArguments = {id};
+            String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+                    + BaseParameters.PARAMETER_NAME + " = ?";
+            String[] selectionArguments = {String.valueOf(type), id};
 
             try (
-                    Cursor cursor = db.query(
+                    Cursor cursor = getCursorAfterQuerying(
                             mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
-                            getAllSoundProfileColumns(),
-                            selection,
-                            selectionArguments,
-                            /*groupBy=*/ null,
-                            /*having=*/ null,
-                            /*orderBy=*/ null)
+                            getAllMediaProfileColumns(), selection, selectionArguments)
             ) {
                 int count = cursor.getCount();
                 if (count == 0) {
@@ -309,7 +230,7 @@
                     return null;
                 }
                 cursor.moveToFirst();
-                return getSoundProfileFromCursor(cursor);
+                return getSoundProfileWithTempIdFromCursor(cursor);
             }
         }
 
@@ -317,7 +238,7 @@
         public List<SoundProfile> getSoundProfilesByPackage(String packageName, int userId) {
             String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {packageName};
-            return getSoundProfilesBasedOnConditions(getAllSoundProfileColumns(), selection,
+            return getSoundProfilesBasedOnConditions(getAllMediaProfileColumns(), selection,
                     selectionArguments);
         }
 
@@ -332,13 +253,79 @@
             List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
                     null, null);
             return soundProfiles.stream()
-                    .map(SoundProfile::getName)
+                    .map(SoundProfile::getPackageName)
+                    .distinct()
                     .collect(Collectors.toList());
         }
 
-        private String[] getAllSoundProfileColumns() {
+        private void populateTempIdMap(BiMap<Long, String> map, Long id) {
+            if (id != null && map.get(id) == null) {
+                String uuid = UUID.randomUUID().toString();
+                while (map.inverse().containsKey(uuid)) {
+                    uuid = UUID.randomUUID().toString();
+                }
+                map.put(id, uuid);
+            }
+        }
+
+        private String persistableBundleToJson(PersistableBundle bundle) {
+            JSONObject json = new JSONObject();
+            for (String key : bundle.keySet()) {
+                Object value = bundle.get(key);
+                try {
+                    if (value instanceof String) {
+                        json.put(key, bundle.getString(key));
+                    } else if (value instanceof Integer) {
+                        json.put(key, bundle.getInt(key));
+                    } else if (value instanceof Long) {
+                        json.put(key, bundle.getLong(key));
+                    } else if (value instanceof Boolean) {
+                        json.put(key, bundle.getBoolean(key));
+                    } else if (value instanceof Double) {
+                        json.put(key, bundle.getDouble(key));
+                    }
+                } catch (JSONException e) {
+                    Log.e(TAG, "Unable to serialize ", e);
+                }
+            }
+            return json.toString();
+        }
+
+        private PersistableBundle jsonToBundle(String jsonString) {
+            PersistableBundle bundle = new PersistableBundle();
+            if (jsonString != null) {
+                JSONObject jsonObject = null;
+                try {
+                    jsonObject = new JSONObject(jsonString);
+
+                    Iterator<String> keys = jsonObject.keys();
+                    while (keys.hasNext()) {
+                        String key = keys.next();
+                        Object value = jsonObject.get(key);
+
+                        if (value instanceof String) {
+                            bundle.putString(key, (String) value);
+                        } else if (value instanceof Integer) {
+                            bundle.putInt(key, (Integer) value);
+                        } else if (value instanceof Boolean) {
+                            bundle.putBoolean(key, (Boolean) value);
+                        } else if (value instanceof Double) {
+                            bundle.putDouble(key, (Double) value);
+                        } else if (value instanceof Long) {
+                            bundle.putLong(key, (Long) value);
+                        }
+                    }
+                } catch (JSONException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            return bundle;
+        }
+
+        private String[] getAllMediaProfileColumns() {
             return new String[]{
                     BaseParameters.PARAMETER_ID,
+                    BaseParameters.PARAMETER_TYPE,
                     BaseParameters.PARAMETER_NAME,
                     BaseParameters.PARAMETER_INPUT_ID,
                     BaseParameters.PARAMETER_PACKAGE,
@@ -346,40 +333,92 @@
             };
         }
 
-        private SoundProfile getSoundProfileFromCursor(Cursor cursor) {
-            String returnId = cursor.getString(
-                    cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_ID));
-            int type = cursor.getInt(
-                    cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_TYPE));
-            String name = cursor.getString(
-                    cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_NAME));
-            String inputId = cursor.getString(
-                    cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_INPUT_ID));
-            String packageName = cursor.getString(
-                    cursor.getColumnIndexOrThrow(BaseParameters.PARAMETER_PACKAGE));
-            String settings = cursor.getString(
-                    cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS));
-            return new SoundProfile(returnId, type, name, inputId, packageName,
-                    jsonToBundle(settings));
+        private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) {
+            return new PictureProfile(
+                    getTempId(mPictureProfileTempIdMap, cursor),
+                    getType(cursor),
+                    getName(cursor),
+                    getInputId(cursor),
+                    getPackageName(cursor),
+                    jsonToBundle(getSettingsString(cursor))
+            );
+        }
+
+        private SoundProfile getSoundProfileWithTempIdFromCursor(Cursor cursor) {
+            return new SoundProfile(
+                    getTempId(mSoundProfileTempIdMap, cursor),
+                    getType(cursor),
+                    getName(cursor),
+                    getInputId(cursor),
+                    getPackageName(cursor),
+                    jsonToBundle(getSettingsString(cursor))
+            );
+        }
+
+        private String getTempId(BiMap<Long, String> map, Cursor cursor) {
+            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
+            Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
+            populateTempIdMap(map, dbId);
+            return map.get(dbId);
+        }
+
+        private int getType(Cursor cursor) {
+            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE);
+            return colIndex != -1 ? cursor.getInt(colIndex) : 0;
+        }
+
+        private String getName(Cursor cursor) {
+            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME);
+            return colIndex != -1 ? cursor.getString(colIndex) : null;
+        }
+
+        private String getInputId(Cursor cursor) {
+            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID);
+            return colIndex != -1 ? cursor.getString(colIndex) : null;
+        }
+
+        private String getPackageName(Cursor cursor) {
+            int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE);
+            return colIndex != -1 ? cursor.getString(colIndex) : null;
+        }
+
+        private String getSettingsString(Cursor cursor) {
+            int colIndex = cursor.getColumnIndex(mMediaQualityDbHelper.SETTINGS);
+            return colIndex != -1 ? cursor.getString(colIndex) : null;
+        }
+
+        private Cursor getCursorAfterQuerying(String table, String[] columns, String selection,
+                String[] selectionArgs) {
+            SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
+            return db.query(table, columns, selection, selectionArgs,
+                    /*groupBy=*/ null, /*having=*/ null, /*orderBy=*/ null);
+        }
+
+        private List<PictureProfile> getPictureProfilesBasedOnConditions(String[] columns,
+                String selection, String[] selectionArguments) {
+            try (
+                    Cursor cursor = getCursorAfterQuerying(
+                            mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, columns, selection,
+                            selectionArguments)
+            ) {
+                List<PictureProfile> pictureProfiles = new ArrayList<>();
+                while (cursor.moveToNext()) {
+                    pictureProfiles.add(getPictureProfileWithTempIdFromCursor(cursor));
+                }
+                return pictureProfiles;
+            }
         }
 
         private List<SoundProfile> getSoundProfilesBasedOnConditions(String[] columns,
                 String selection, String[] selectionArguments) {
-            SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
-
             try (
-                    Cursor cursor = db.query(
-                            mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
-                            columns,
-                            selection,
-                            selectionArguments,
-                            /*groupBy=*/ null,
-                            /*having=*/ null,
-                            /*orderBy=*/ null)
+                    Cursor cursor = getCursorAfterQuerying(
+                            mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, columns, selection,
+                            selectionArguments)
             ) {
                 List<SoundProfile> soundProfiles = new ArrayList<>();
                 while (cursor.moveToNext()) {
-                    soundProfiles.add(getSoundProfileFromCursor(cursor));
+                    soundProfiles.add(getSoundProfileWithTempIdFromCursor(cursor));
                 }
                 return soundProfiles;
             }