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

Commit 68528911 authored by Marco Nelissen's avatar Marco Nelissen Committed by Ian Pedowitz
Browse files

Fix race condition when setting default ringtones

If the device was powered off during first boot, after media scanner
inserted some entries but before the default ringtone settings were
set (or committed to disk), the default settings would not be set
on subsequent boots.

Bug: 18625739
Bug: 22349910
Bug: 25633323
Change-Id: I8ff5d3c4f842297d0675e1f5cbe17c0709a14158
parent 051782fd
Loading
Loading
Loading
Loading
+65 −30
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package android.media;
package android.media;


import android.content.ContentProviderClient;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.ContentValues;
import android.content.Context;
import android.content.Context;
@@ -37,6 +38,7 @@ import android.provider.MediaStore.Files.FileColumns;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Video;
import android.provider.Settings;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.sax.Element;
import android.sax.Element;
import android.sax.ElementListener;
import android.sax.ElementListener;
import android.sax.RootElement;
import android.sax.RootElement;
@@ -328,8 +330,6 @@ public class MediaScanner implements AutoCloseable {
    // used when scanning the image database so we know whether we have to prune
    // used when scanning the image database so we know whether we have to prune
    // old thumbnail files
    // old thumbnail files
    private int mOriginalCount;
    private int mOriginalCount;
    /** Whether the database had any entries in it before the scan started */
    private boolean mWasEmptyPriorToScan = false;
    /** Whether the scanner has set a default sound for the ringer ringtone. */
    /** Whether the scanner has set a default sound for the ringer ringtone. */
    private boolean mDefaultRingtoneSet;
    private boolean mDefaultRingtoneSet;
    /** Whether the scanner has set a default sound for the notification ringtone. */
    /** Whether the scanner has set a default sound for the notification ringtone. */
@@ -562,12 +562,29 @@ public class MediaScanner implements AutoCloseable {
                FileEntry entry = beginFile(path, mimeType, lastModified,
                FileEntry entry = beginFile(path, mimeType, lastModified,
                        fileSize, isDirectory, noMedia);
                        fileSize, isDirectory, noMedia);


                if (entry == null) {
                    return null;
                }

                // if this file was just inserted via mtp, set the rowid to zero
                // if this file was just inserted via mtp, set the rowid to zero
                // (even though it already exists in the database), to trigger
                // (even though it already exists in the database), to trigger
                // the correct code path for updating its entry
                // the correct code path for updating its entry
                if (mMtpObjectHandle != 0) {
                if (mMtpObjectHandle != 0) {
                    entry.mRowId = 0;
                    entry.mRowId = 0;
                }
                }

                if (entry.mPath != null &&
                        ((!mDefaultNotificationSet &&
                                doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
                        || (!mDefaultRingtoneSet &&
                                doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
                        || (!mDefaultAlarmSet &&
                                doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
                    Log.w(TAG, "forcing rescan of " + entry.mPath +
                            "since ringtone setting didn't finish");
                    scanAlways = true;
                }

                // rescan for metadata if file was modified since last scan
                // rescan for metadata if file was modified since last scan
                if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
                if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
                    if (noMedia) {
                    if (noMedia) {
@@ -947,21 +964,9 @@ public class MediaScanner implements AutoCloseable {
            }
            }
            Uri result = null;
            Uri result = null;
            boolean needToSetSettings = false;
            boolean needToSetSettings = false;
            if (rowId == 0) {
                if (mMtpObjectHandle != 0) {
                    values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
                }
                if (tableUri == mFilesUri) {
                    int format = entry.mFormat;
                    if (format == 0) {
                        format = MediaFile.getFormatCode(entry.mPath, mMimeType);
                    }
                    values.put(Files.FileColumns.FORMAT, format);
                }
            // Setting a flag in order not to use bulk insert for the file related with
            // Setting a flag in order not to use bulk insert for the file related with
            // notifications, ringtones, and alarms, because the rowId of the inserted file is
            // notifications, ringtones, and alarms, because the rowId of the inserted file is
            // needed.
            // needed.
                if (mWasEmptyPriorToScan) {
            if (notifications && !mDefaultNotificationSet) {
            if (notifications && !mDefaultNotificationSet) {
                if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
                if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
                        doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
@@ -978,8 +983,18 @@ public class MediaScanner implements AutoCloseable {
                    needToSetSettings = true;
                    needToSetSettings = true;
                }
                }
            }
            }
                }


            if (rowId == 0) {
                if (mMtpObjectHandle != 0) {
                    values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
                }
                if (tableUri == mFilesUri) {
                    int format = entry.mFormat;
                    if (format == 0) {
                        format = MediaFile.getFormatCode(entry.mPath, mMimeType);
                    }
                    values.put(Files.FileColumns.FORMAT, format);
                }
                // New file, insert it.
                // New file, insert it.
                // Directories need to be inserted before the files they contain, so they
                // Directories need to be inserted before the files they contain, so they
                // get priority when bulk inserting.
                // get priority when bulk inserting.
@@ -1049,14 +1064,18 @@ public class MediaScanner implements AutoCloseable {


        private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
        private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {


            String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
            if(wasSettingAlreadySet(settingName)) {
                    settingName);
                return;
            }


            ContentResolver cr = mContext.getContentResolver();
            String existingSettingValue = Settings.System.getString(cr, settingName);
            if (TextUtils.isEmpty(existingSettingValue)) {
            if (TextUtils.isEmpty(existingSettingValue)) {
                // Set the setting to the given URI
                // Set the setting to the given URI
                Settings.System.putString(mContext.getContentResolver(), settingName,
                Settings.System.putString(cr, settingName,
                        ContentUris.withAppendedId(uri, rowId).toString());
                        ContentUris.withAppendedId(uri, rowId).toString());
            }
            }
            Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
        }
        }


        private int getFileTypeFromDrm(String path) {
        private int getFileTypeFromDrm(String path) {
@@ -1083,6 +1102,20 @@ public class MediaScanner implements AutoCloseable {


    }; // end of anonymous MediaScannerClient instance
    }; // end of anonymous MediaScannerClient instance


    private String settingSetIndicatorName(String base) {
        return base + "_set";
    }

    private boolean wasSettingAlreadySet(String name) {
        ContentResolver cr = mContext.getContentResolver();
        String indicatorName = settingSetIndicatorName(name);
        try {
            return Settings.System.getInt(cr, indicatorName) != 0;
        } catch (SettingNotFoundException e) {
            return false;
        }
    }

    private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
    private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
        Cursor c = null;
        Cursor c = null;
        String where = null;
        String where = null;
@@ -1100,6 +1133,10 @@ public class MediaScanner implements AutoCloseable {
            selectionArgs = new String[] { "" };
            selectionArgs = new String[] { "" };
        }
        }


        mDefaultRingtoneSet = wasSettingAlreadySet(Settings.System.RINGTONE);
        mDefaultNotificationSet = wasSettingAlreadySet(Settings.System.NOTIFICATION_SOUND);
        mDefaultAlarmSet = wasSettingAlreadySet(Settings.System.ALARM_ALERT);

        // Tell the provider to not delete the file.
        // Tell the provider to not delete the file.
        // If the file is truly gone the delete is unnecessary, and we want to avoid
        // If the file is truly gone the delete is unnecessary, and we want to avoid
        // accidentally deleting files that are really there (this may happen if the
        // accidentally deleting files that are really there (this may happen if the
@@ -1117,7 +1154,6 @@ public class MediaScanner implements AutoCloseable {
                // with CursorWindow positioning.
                // with CursorWindow positioning.
                long lastId = Long.MIN_VALUE;
                long lastId = Long.MIN_VALUE;
                Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
                Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
                mWasEmptyPriorToScan = true;


                while (true) {
                while (true) {
                    selectionArgs[0] = "" + lastId;
                    selectionArgs[0] = "" + lastId;
@@ -1136,7 +1172,6 @@ public class MediaScanner implements AutoCloseable {
                    if (num == 0) {
                    if (num == 0) {
                        break;
                        break;
                    }
                    }
                    mWasEmptyPriorToScan = false;
                    while (c.moveToNext()) {
                    while (c.moveToNext()) {
                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
@@ -1284,7 +1319,7 @@ public class MediaScanner implements AutoCloseable {
        }
        }
    }
    }


    private void postscan(String[] directories) throws RemoteException {
    private void postscan(final String[] directories) throws RemoteException {


        // handle playlists last, after we know what media files are on the storage.
        // handle playlists last, after we know what media files are on the storage.
        if (mProcessPlaylists) {
        if (mProcessPlaylists) {