Merge "[SettingsProvider] update ringtone cache only after setting is update" into main
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b9a6583..45b4935 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5311,7 +5311,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 9234479..c4f2159 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -38,7 +38,6 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.FileUtils;
-import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -993,18 +992,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) {
@@ -1100,28 +1087,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 1cde26b..c0d1671 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;
@@ -849,29 +855,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) {
@@ -1953,55 +1998,70 @@
             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;
                 }
 
                 case MUTATION_OPERATION_RESET: {
-                    mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SYSTEM,
+                    success = mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SYSTEM,
                             runAsUserId, callingPackage, mode, tag);
-                    return true;
+                    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) {
@@ -3289,20 +3349,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: {
@@ -3322,6 +3383,7 @@
                         }
                         if (someSettingChanged) {
                             settingsState.persistSyncLocked();
+                            success = true;
                         }
                     }
                 } break;
@@ -3343,6 +3405,7 @@
                         }
                         if (someSettingChanged) {
                             settingsState.persistSyncLocked();
+                            success = true;
                         }
                     }
                 } break;
@@ -3370,6 +3433,7 @@
                         }
                         if (someSettingChanged) {
                             settingsState.persistSyncLocked();
+                            success = true;
                         }
                     }
                 } break;
@@ -3394,10 +3458,12 @@
                         }
                         if (someSettingChanged) {
                             settingsState.persistSyncLocked();
+                            success = true;
                         }
                     }
                 } break;
             }
+            return success;
         }
 
         public void removeSettingsForPackageLocked(String packageName, int userId) {