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

Commit ee6c7e49 authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change I2c5380a6 into eclair-mr2

* changes:
  Fix a bug in the Settings contract class (introduced in an ... iffy ... fix to a race condition) that breaks the caching of non-existent Settings/Secure/ Gservices values (causing lots of unnecessary database queries).
parents df7edf5d 799f721f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1897,7 +1897,7 @@ public class SQLiteDatabase extends SQLiteClosable {
            samplePercent = 100;
        } else {
            samplePercent = (int) (100 * nanos / QUERY_LOG_TIME_IN_NANOS) + 1;
            if (mRandom.nextInt(100) < samplePercent) return;
            if (mRandom.nextInt(100) >= samplePercent) return;
        }

        if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
+45 −44
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.os.*;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.Config;
import android.util.Log;

import java.net.URISyntaxException;
@@ -442,6 +443,7 @@ public final class Settings {
    public static final String AUTHORITY = "settings";

    private static final String TAG = "Settings";
    private static final boolean LOCAL_LOGV = Config.LOGV || false;

    public static class SettingNotFoundException extends AndroidException {
        public SettingNotFoundException(String msg) {
@@ -478,62 +480,61 @@ public final class Settings {

    private static class NameValueCache {
        private final String mVersionSystemProperty;
        // the following needs synchronization because this structure is accessed from different
        // threads and they could be performing clear(), get(), put() at the same time.
        private final Map<String, String> mValues =
            	Collections.synchronizedMap(new HashMap<String, String>());
        private long mValuesVersion = 0;
        private final Uri mUri;

        NameValueCache(String versionSystemProperty, Uri uri) {
        // Must synchronize(mValues) to access mValues and mValuesVersion.
        private final HashMap<String, String> mValues = new HashMap<String, String>();
        private long mValuesVersion = 0;

        public NameValueCache(String versionSystemProperty, Uri uri) {
            mVersionSystemProperty = versionSystemProperty;
            mUri = uri;
        }

        String getString(ContentResolver cr, String name) {
        public String getString(ContentResolver cr, String name) {
            long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0);

            synchronized (mValues) {
                if (mValuesVersion != newValuesVersion) {
                    if (LOCAL_LOGV) {
                        Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current " +
                                newValuesVersion + " != cached " + mValuesVersion);
                    }

                    mValues.clear();
                    mValuesVersion = newValuesVersion;
                }
            /*
             *  don't look for the key using containsKey() method because (key, object) mapping
             *  could be removed from mValues before get() is done like so:
             *
             *      say, mValues contains mapping for "foo"
             *      Thread# 1
             *          performs containsKey("foo")
             *          receives true
             *      Thread #2
             *          triggers mValues.clear()
             *      Thread#1
             *          since containsKey("foo") = true, performs get("foo")
             *          receives null
             *          thats incorrect!
             *
             *    to avoid the above, thread#1 should do get("foo") instead of containsKey("foo")
             *    since mValues is synchronized, get() will get a consistent value.
             *
             *    we don't want to make this method synchronized tho - because
             *    holding mutex is not desirable while a call could be made to database.
             */
            String value = mValues.get(name);
            if (value == null) {

                if (mValues.containsKey(name)) {
                    return mValues.get(name);  // Could be null, that's OK -- negative caching
                }
            }

            Cursor c = null;
            try {
                c = cr.query(mUri, new String[] { Settings.NameValueTable.VALUE },
                        Settings.NameValueTable.NAME + "=?", new String[]{name}, null);
                    if (c != null && c.moveToNext()) value = c.getString(0);
                if (c == null) {
                    Log.w(TAG, "Can't get key " + name + " from " + mUri);
                    return null;
                }

                String value = c.moveToNext() ? c.getString(0) : null;
                synchronized (mValues) {
                    mValues.put(name, value);
                }
                if (LOCAL_LOGV) {
                    Log.v(TAG, "cache miss [" + mUri.getLastPathSegment() + "]: " +
                            name + " = " + (value == null ? "(null)" : value));
                }
                return value;
            } catch (SQLException e) {
                    // SQL error: return null, but don't cache it.
                Log.w(TAG, "Can't get key " + name + " from " + mUri, e);
                return null;  // Return null, but don't cache it.
            } finally {
                if (c != null) c.close();
            }
        }
            return value;
        }
    }

    /**