Avoid unnecessary SharedPrefences disk writes.

Apps commonly edit + commit redundant changes to their
SharedPreferences, not checking the existing values.  Rather than
force all apps to double-check that their settings writes aren't
redundant, we should just make .commit() faster (avoiding the disk
write) when the file already exists on disk and no effective changes
were made.

Change-Id: I7edbd0d3ace5b69b7af6d12c39797c8b7f86230b
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e99d4b4..2870c50 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2844,6 +2844,7 @@
                 boolean returnValue;
 
                 boolean hasListeners;
+                boolean changesMade = false;
                 List<String> keysModified = null;
                 Set<OnSharedPreferenceChangeListener> listeners = null;
 
@@ -2857,17 +2858,31 @@
 
                     synchronized (this) {
                         if (mClear) {
-                            mMap.clear();
+                            if (!mMap.isEmpty()) {
+                                changesMade = true;
+                                mMap.clear();
+                            }
                             mClear = false;
                         }
 
                         for (Entry<String, Object> e : mModified.entrySet()) {
                             String k = e.getKey();
                             Object v = e.getValue();
-                            if (v == this) {
-                                mMap.remove(k);
+                            if (v == this) {  // magic value for a removal mutation
+                                if (mMap.containsKey(k)) {
+                                    mMap.remove(k);
+                                    changesMade = true;
+                                }
                             } else {
-                                mMap.put(k, v);
+                                boolean isSame = false;
+                                if (mMap.containsKey(k)) {
+                                    Object existingValue = mMap.get(k);
+                                    isSame = existingValue != null && existingValue.equals(v);
+                                }
+                                if (!isSame) {
+                                    mMap.put(k, v);
+                                    changesMade = true;
+                                }
                             }
 
                             if (hasListeners) {
@@ -2878,7 +2893,7 @@
                         mModified.clear();
                     }
 
-                    returnValue = writeFileLocked();
+                    returnValue = writeFileLocked(changesMade);
                 }
 
                 if (hasListeners) {
@@ -2923,9 +2938,16 @@
             return str;
         }
 
-        private boolean writeFileLocked() {
+        private boolean writeFileLocked(boolean changesMade) {
             // Rename the current file so it may be used as a backup during the next read
             if (mFile.exists()) {
+                if (!changesMade) {
+                    // If the file already exists, but no changes were
+                    // made to the underlying map, it's wasteful to
+                    // re-write the file.  Return as if we wrote it
+                    // out.
+                    return true;
+                }
                 if (!mBackupFile.exists()) {
                     if (!mFile.renameTo(mBackupFile)) {
                         Log.e(TAG, "Couldn't rename file " + mFile