Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cc0de51a authored by Songchun Fan's avatar Songchun Fan Committed by Song Chun Fan
Browse files

[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
Change-Id: I63ce56ef0cbfb8862ffbb56d7137a0f0223110a2
parent 24267452
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -5311,7 +5311,6 @@ public final class Settings {
        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);
+0 −35
Original line number Diff line number Diff line
@@ -38,7 +38,6 @@ import android.database.StaleDataException;
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 @@ public class RingtoneManager {

        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 @@ public class RingtoneManager {
        }
    }

    /**
     * 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;
+95 −29
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import android.compat.annotation.EnabledSince;
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.SQLiteDatabase;
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.provider.settings.validators.Validator;
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 libcore.util.HexEncoding;
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 @@ public class SettingsProvider extends ContentProvider {
        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 @@ public class SettingsProvider extends ContentProvider {
            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);
            }
        }

        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 @@ public class SettingsProvider extends ContentProvider {
            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 @@ public class SettingsProvider extends ContentProvider {
                        }
                        if (someSettingChanged) {
                            settingsState.persistSyncLocked();
                            success = true;
                        }
                    }
                } break;
@@ -3343,6 +3405,7 @@ public class SettingsProvider extends ContentProvider {
                        }
                        if (someSettingChanged) {
                            settingsState.persistSyncLocked();
                            success = true;
                        }
                    }
                } break;
@@ -3370,6 +3433,7 @@ public class SettingsProvider extends ContentProvider {
                        }
                        if (someSettingChanged) {
                            settingsState.persistSyncLocked();
                            success = true;
                        }
                    }
                } break;
@@ -3394,10 +3458,12 @@ public class SettingsProvider extends ContentProvider {
                        }
                        if (someSettingChanged) {
                            settingsState.persistSyncLocked();
                            success = true;
                        }
                    }
                } break;
            }
            return success;
        }

        public void removeSettingsForPackageLocked(String packageName, int userId) {