[SettingsProvider] update ringtone cache only after setting is update

Previously RingtoneManager may override the ringtone cache even when the
ringtone setting is not updated. This CL moves the cache update logic to
SettingsProvider, and only updates the cache if the setting itself has
been updated.

BUG: 280219393
Test: manual with work profile creation
Merged-In: I63ce56ef0cbfb8862ffbb56d7137a0f0223110a2
Change-Id: I63ce56ef0cbfb8862ffbb56d7137a0f0223110a2
(cherry picked from commit cc0de51a7d14d10e6d13e0c234b25e301bcbbdd2)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1cf41cf..d66ffce 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5164,7 +5164,6 @@
         public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE);
 
         /** {@hide} */
-        @Readable
         public static final String RINGTONE_CACHE = "ringtone_cache";
         /** {@hide} */
         public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE);
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 43acdd5..ff369c8 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -40,7 +40,6 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -934,18 +933,6 @@
 
         Settings.System.putStringForUser(resolver, setting,
                 ringtoneUri != null ? ringtoneUri.toString() : null, context.getUserId());
-
-        // Stream selected ringtone into cache so it's available for playback
-        // when CE storage is still locked
-        if (ringtoneUri != null) {
-            final Uri cacheUri = getCacheForType(type, context.getUserId());
-            try (InputStream in = openRingtone(context, ringtoneUri);
-                    OutputStream out = resolver.openOutputStream(cacheUri, "wt")) {
-                FileUtils.copy(in, out);
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to cache ringtone: " + e);
-            }
-        }
     }
 
     private static boolean isInternalRingtoneUri(Uri uri) {
@@ -1041,28 +1028,6 @@
         }
     }
 
-    /**
-     * Try opening the given ringtone locally first, but failover to
-     * {@link IRingtonePlayer} if we can't access it directly. Typically happens
-     * when process doesn't hold
-     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
-     */
-    private static InputStream openRingtone(Context context, Uri uri) throws IOException {
-        final ContentResolver resolver = context.getContentResolver();
-        try {
-            return resolver.openInputStream(uri);
-        } catch (SecurityException | IOException e) {
-            Log.w(TAG, "Failed to open directly; attempting failover: " + e);
-            final IRingtonePlayer player = context.getSystemService(AudioManager.class)
-                    .getRingtonePlayer();
-            try {
-                return new ParcelFileDescriptor.AutoCloseInputStream(player.openRingtone(uri));
-            } catch (Exception e2) {
-                throw new IOException(e2);
-            }
-        }
-    }
-
     private static String getSettingForType(int type) {
         if ((type & TYPE_RINGTONE) != 0) {
             return Settings.System.RINGTONE;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 765edd7..e45913c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -58,6 +58,7 @@
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -76,6 +77,7 @@
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.hardware.camera2.utils.ArrayUtils;
 import android.media.AudioManager;
+import android.media.IRingtonePlayer;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -110,6 +112,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -129,7 +132,10 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteBuffer;
 import java.security.InvalidKeyException;
@@ -841,29 +847,68 @@
         uri = ContentProvider.getUriWithoutUserId(uri);
 
         final String cacheRingtoneSetting;
-        final String cacheName;
         if (Settings.System.RINGTONE_CACHE_URI.equals(uri)) {
             cacheRingtoneSetting = Settings.System.RINGTONE;
-            cacheName = Settings.System.RINGTONE_CACHE;
         } else if (Settings.System.NOTIFICATION_SOUND_CACHE_URI.equals(uri)) {
             cacheRingtoneSetting = Settings.System.NOTIFICATION_SOUND;
-            cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
         } else if (Settings.System.ALARM_ALERT_CACHE_URI.equals(uri)) {
             cacheRingtoneSetting = Settings.System.ALARM_ALERT;
-            cacheName = Settings.System.ALARM_ALERT_CACHE;
         } else {
             throw new FileNotFoundException("Direct file access no longer supported; "
                     + "ringtone playback is available through android.media.Ringtone");
         }
 
+        final File cacheFile = getCacheFile(cacheRingtoneSetting, userId);
+        return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode));
+    }
+
+    @Nullable
+    private String getCacheName(String setting) {
+        if (Settings.System.RINGTONE.equals(setting)) {
+            return Settings.System.RINGTONE_CACHE;
+        } else if (Settings.System.NOTIFICATION_SOUND.equals(setting)) {
+            return Settings.System.NOTIFICATION_SOUND_CACHE;
+        } else if (Settings.System.ALARM_ALERT.equals(setting)) {
+            return Settings.System.ALARM_ALERT_CACHE;
+        }
+        return null;
+    }
+
+    @Nullable
+    private File getCacheFile(String setting, int userId) {
         int actualCacheOwner;
         // Redirect cache to parent if ringtone setting is owned by profile parent
         synchronized (mLock) {
-            actualCacheOwner = resolveOwningUserIdForSystemSettingLocked(userId,
-                    cacheRingtoneSetting);
+            actualCacheOwner = resolveOwningUserIdForSystemSettingLocked(userId, setting);
+        }
+        final String cacheName = getCacheName(setting);
+        if (cacheName == null) {
+            return null;
         }
         final File cacheFile = new File(getRingtoneCacheDir(actualCacheOwner), cacheName);
-        return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode));
+        return cacheFile;
+    }
+
+
+    /**
+     * Try opening the given ringtone locally first, but failover to
+     * {@link IRingtonePlayer} if we can't access it directly. Typically, happens
+     * when process doesn't hold {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
+     */
+    private static InputStream openRingtone(Context context, Uri uri) throws IOException {
+        final ContentResolver resolver = context.getContentResolver();
+        try {
+            return resolver.openInputStream(uri);
+        } catch (SecurityException | IOException e) {
+            Log.w(LOG_TAG, "Failed to open directly; attempting failover: " + e);
+            final IRingtonePlayer player = context.getSystemService(AudioManager.class)
+                    .getRingtonePlayer();
+            try {
+                return new ParcelFileDescriptor.AutoCloseInputStream(player.openRingtone(uri));
+            } catch (Exception e2) {
+                throw new IOException(e2);
+            }
+        }
     }
 
     private File getRingtoneCacheDir(int userId) {
@@ -1938,49 +1983,65 @@
             return false;
         }
 
-        // Invalidate any relevant cache files
-        String cacheName = null;
-        if (Settings.System.RINGTONE.equals(name)) {
-            cacheName = Settings.System.RINGTONE_CACHE;
-        } else if (Settings.System.NOTIFICATION_SOUND.equals(name)) {
-            cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
-        } else if (Settings.System.ALARM_ALERT.equals(name)) {
-            cacheName = Settings.System.ALARM_ALERT_CACHE;
-        }
-        if (cacheName != null) {
+        File cacheFile = getCacheFile(name, callingUserId);
+        if (cacheFile != null) {
             if (!isValidAudioUri(name, value)) {
                 return false;
             }
-            final File cacheFile = new File(
-                    getRingtoneCacheDir(owningUserId), cacheName);
+            // Invalidate any relevant cache files
             cacheFile.delete();
         }
 
+        final boolean success;
         // Mutate the value.
         synchronized (mLock) {
             switch (operation) {
                 case MUTATION_OPERATION_INSERT: {
                     validateSystemSettingValue(name, value);
-                    return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
+                    success = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
                             owningUserId, name, value, null, false, callingPackage,
                             false, null, overrideableByRestore);
+                    break;
                 }
 
                 case MUTATION_OPERATION_DELETE: {
-                    return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
+                    success = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
                             owningUserId, name, false, null);
+                    break;
                 }
 
                 case MUTATION_OPERATION_UPDATE: {
                     validateSystemSettingValue(name, value);
-                    return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
+                    success = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
                             owningUserId, name, value, null, false, callingPackage,
                             false, null);
+                    break;
+                }
+
+                default: {
+                    success = false;
+                    Slog.e(LOG_TAG, "Unknown operation code: " + operation);
                 }
             }
-            Slog.e(LOG_TAG, "Unknown operation code: " + operation);
+        }
+
+        if (!success) {
             return false;
         }
+
+        if ((operation == MUTATION_OPERATION_INSERT || operation == MUTATION_OPERATION_UPDATE)
+                && cacheFile != null && value != null) {
+            final Uri ringtoneUri = Uri.parse(value);
+            // Stream selected ringtone into cache, so it's available for playback
+            // when CE storage is still locked
+            try (InputStream in = openRingtone(getContext(), ringtoneUri);
+                 OutputStream out = new FileOutputStream(cacheFile)) {
+                FileUtils.copy(in, out);
+            } catch (IOException e) {
+                Slog.w(LOG_TAG, "Failed to cache ringtone: " + e);
+            }
+        }
+        return true;
     }
 
     private boolean isValidAudioUri(String name, String uri) {
@@ -3267,20 +3328,21 @@
             return Global.SECURE_FRP_MODE.equals(setting.getName());
         }
 
-        public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+        public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
                 String tag) {
-            resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
+            return resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
                     null);
         }
 
-        public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+        public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
                 String tag, @Nullable String prefix) {
             final int key = makeKey(type, userId);
             SettingsState settingsState = peekSettingsStateLocked(key);
             if (settingsState == null) {
-                return;
+                return false;
             }
 
+            boolean success = false;
             banConfigurationIfNecessary(type, prefix, settingsState);
             switch (mode) {
                 case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
@@ -3300,6 +3362,7 @@
                         }
                         if (someSettingChanged) {
                             settingsState.persistSyncLocked();
+                            success = true;
                         }
                     }
                 } break;
@@ -3321,6 +3384,7 @@
                         }
                         if (someSettingChanged) {
                             settingsState.persistSyncLocked();
+                            success = true;
                         }
                     }
                 } break;
@@ -3348,6 +3412,7 @@
                         }
                         if (someSettingChanged) {
                             settingsState.persistSyncLocked();
+                            success = true;
                         }
                     }
                 } break;
@@ -3372,10 +3437,12 @@
                         }
                         if (someSettingChanged) {
                             settingsState.persistSyncLocked();
+                            success = true;
                         }
                     }
                 } break;
             }
+            return success;
         }
 
         public void removeSettingsForPackageLocked(String packageName, int userId) {