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

Commit 7ce605eb authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Updated onSharedPreferenceChanged listener behavior."

parents e303b217 5d2049f2
Loading
Loading
Loading
Loading
+0 −6
Original line number Diff line number Diff line
@@ -10875,9 +10875,7 @@ package android.content {
    method @Nullable public String getString(String, @Nullable String);
    method @Nullable public java.util.Set<java.lang.String> getStringSet(String, @Nullable java.util.Set<java.lang.String>);
    method public void registerOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener);
    method public default void registerOnSharedPreferencesClearListener(@NonNull android.content.SharedPreferences.OnSharedPreferencesClearListener);
    method public void unregisterOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener);
    method public default void unregisterOnSharedPreferencesClearListener(@NonNull android.content.SharedPreferences.OnSharedPreferencesClearListener);
  }
  public static interface SharedPreferences.Editor {
@@ -10897,10 +10895,6 @@ package android.content {
    method public void onSharedPreferenceChanged(android.content.SharedPreferences, String);
  }
  public static interface SharedPreferences.OnSharedPreferencesClearListener {
    method public void onSharedPreferencesClear(@NonNull android.content.SharedPreferences, @NonNull java.util.Set<java.lang.String>);
  }
  public class SyncAdapterType implements android.os.Parcelable {
    ctor public SyncAdapterType(String, String, boolean, boolean);
    ctor public SyncAdapterType(android.os.Parcel);
+29 −69
Original line number Diff line number Diff line
@@ -16,17 +16,19 @@

package android.app;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.FileUtils;
import android.os.Looper;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
import android.system.StructTimespec;
import android.util.ArraySet;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
@@ -62,6 +64,15 @@ final class SharedPreferencesImpl implements SharedPreferences {
    /** If a fsync takes more than {@value #MAX_FSYNC_DURATION_MILLIS} ms, warn */
    private static final long MAX_FSYNC_DURATION_MILLIS = 256;

    /**
     * There will now be a callback to {@link
     * OnSharedPreferenceChangeListener#onSharedPreferenceChanged(SharedPreferences, String)} with
     * a {@code null} key on {@link Editor#clear()}.
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
    private static final long CALLBACK_ON_CLEAR_CHANGE = 119147584L;

    // Lock ordering rules:
    //  - acquire SharedPreferencesImpl.mLock before EditorImpl.mLock
    //  - acquire mWritingToDiskLock before EditorImpl.mLock
@@ -94,10 +105,6 @@ final class SharedPreferencesImpl implements SharedPreferences {
    private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
            new WeakHashMap<OnSharedPreferenceChangeListener, Object>();

    @GuardedBy("mLock")
    private final WeakHashMap<OnSharedPreferencesClearListener, Object> mClearListeners =
            new WeakHashMap<>();

    /** Current memory state (always increasing) */
    @GuardedBy("this")
    private long mCurrentMemoryStateGeneration;
@@ -258,28 +265,6 @@ final class SharedPreferencesImpl implements SharedPreferences {
        }
    }

    @Override
    public void registerOnSharedPreferencesClearListener(
            @NonNull OnSharedPreferencesClearListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener cannot be null.");
        }
        synchronized (mLock) {
            mClearListeners.put(listener, CONTENT);
        }
    }

    @Override
    public void unregisterOnSharedPreferencesClearListener(
            @NonNull OnSharedPreferencesClearListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("listener cannot be null.");
        }
        synchronized (mLock) {
            mClearListeners.remove(listener);
        }
    }

    @GuardedBy("mLock")
    private void awaitLoadedLocked() {
        if (!mLoaded) {
@@ -388,10 +373,9 @@ final class SharedPreferencesImpl implements SharedPreferences {
    // Return value from EditorImpl#commitToMemory()
    private static class MemoryCommitResult {
        final long memoryStateGeneration;
        final boolean keysCleared;
        @Nullable final List<String> keysModified;
        @Nullable final Set<String> keysCleared;
        @Nullable final Set<OnSharedPreferenceChangeListener> listeners;
        @Nullable final Set<OnSharedPreferencesClearListener> clearListeners;
        final Map<String, Object> mapToWriteToDisk;
        final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);

@@ -399,16 +383,14 @@ final class SharedPreferencesImpl implements SharedPreferences {
        volatile boolean writeToDiskResult = false;
        boolean wasWritten = false;

        private MemoryCommitResult(long memoryStateGeneration, @Nullable List<String> keysModified,
        private MemoryCommitResult(long memoryStateGeneration, boolean keysCleared,
                @Nullable List<String> keysModified,
                @Nullable Set<OnSharedPreferenceChangeListener> listeners,
                @Nullable Set<String> keysCleared,
                @Nullable Set<OnSharedPreferencesClearListener> clearListeners,
                Map<String, Object> mapToWriteToDisk) {
            this.memoryStateGeneration = memoryStateGeneration;
            this.keysCleared = keysCleared;
            this.keysModified = keysModified;
            this.listeners = listeners;
            this.keysCleared = keysCleared;
            this.clearListeners = clearListeners;
            this.mapToWriteToDisk = mapToWriteToDisk;
        }

@@ -526,16 +508,14 @@ final class SharedPreferencesImpl implements SharedPreferences {
            // SharedPreferences instance back, which has the
            // changes reflected in memory.
            notifyListeners(mcr);
            notifyClearListeners(mcr);
        }

        // Returns true if any changes were made
        private MemoryCommitResult commitToMemory() {
            long memoryStateGeneration;
            boolean keysCleared = false;
            List<String> keysModified = null;
            Set<String> keysCleared = null;
            Set<OnSharedPreferenceChangeListener> listeners = null;
            Set<OnSharedPreferencesClearListener> clearListeners = null;
            Map<String, Object> mapToWriteToDisk;

            synchronized (SharedPreferencesImpl.this.mLock) {
@@ -557,23 +537,16 @@ final class SharedPreferencesImpl implements SharedPreferences {
                    keysModified = new ArrayList<String>();
                    listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
                }
                boolean hasClearListeners = !mClearListeners.isEmpty();
                if (hasClearListeners) {
                    keysCleared = new ArraySet<>();
                    clearListeners = new HashSet<>(mClearListeners.keySet());
                }

                synchronized (mEditorLock) {
                    boolean changesMade = false;

                    if (mClear) {
                        if (!mapToWriteToDisk.isEmpty()) {
                            if (hasClearListeners) {
                                keysCleared.addAll(mapToWriteToDisk.keySet());
                            }
                            changesMade = true;
                            mapToWriteToDisk.clear();
                        }
                        keysCleared = true;
                        mClear = false;
                    }

@@ -613,8 +586,8 @@ final class SharedPreferencesImpl implements SharedPreferences {
                    memoryStateGeneration = mCurrentMemoryStateGeneration;
                }
            }
            return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
                    keysCleared, clearListeners, mapToWriteToDisk);
            return new MemoryCommitResult(memoryStateGeneration, keysCleared, keysModified,
                    listeners, mapToWriteToDisk);
        }

        @Override
@@ -641,16 +614,21 @@ final class SharedPreferencesImpl implements SharedPreferences {
                }
            }
            notifyListeners(mcr);
            notifyClearListeners(mcr);
            return mcr.writeToDiskResult;
        }

        private void notifyListeners(final MemoryCommitResult mcr) {
            if (mcr.listeners == null || mcr.keysModified == null ||
                mcr.keysModified.size() == 0) {
            if (mcr.listeners == null || (mcr.keysModified == null && !mcr.keysCleared)) {
                return;
            }
            if (Looper.myLooper() == Looper.getMainLooper()) {
                if (mcr.keysCleared && Compatibility.isChangeEnabled(CALLBACK_ON_CLEAR_CHANGE)) {
                    for (OnSharedPreferenceChangeListener listener : mcr.listeners) {
                        if (listener != null) {
                            listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, null);
                        }
                    }
                }
                for (int i = mcr.keysModified.size() - 1; i >= 0; i--) {
                    final String key = mcr.keysModified.get(i);
                    for (OnSharedPreferenceChangeListener listener : mcr.listeners) {
@@ -664,24 +642,6 @@ final class SharedPreferencesImpl implements SharedPreferences {
                ActivityThread.sMainThreadHandler.post(() -> notifyListeners(mcr));
            }
        }

        private void notifyClearListeners(final MemoryCommitResult mcr) {
            if (mcr.clearListeners == null || mcr.keysCleared == null
                    || mcr.keysCleared.isEmpty()) {
                return;
            }
            if (Looper.myLooper() == Looper.getMainLooper()) {
                for (OnSharedPreferencesClearListener listener : mcr.clearListeners) {
                    if (listener != null) {
                        listener.onSharedPreferencesClear(SharedPreferencesImpl.this,
                                mcr.keysCleared);
                    }
                }
            } else {
                // Run this function on the main thread.
                ActivityThread.sMainThreadHandler.post(() -> notifyClearListeners(mcr));
            }
        }
    }

    /**
+9 −56
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.content;

import android.annotation.NonNull;
import android.annotation.Nullable;

import java.util.Map;
@@ -58,33 +57,18 @@ public interface SharedPreferences {
         *
         * <p>This callback will be run on your main thread.
         *
         * <p><em>Note: This callback will not be triggered when preferences are cleared via
         * {@link Editor#clear()}. However, from {@link android.os.Build.VERSION_CODES#R Android R}
         * onwards, you can use {@link OnSharedPreferencesClearListener} to register for
         * {@link Editor#clear()} callbacks.</em>
         *
         * @param sharedPreferences The {@link SharedPreferences} that received
         *            the change.
         * @param key The key of the preference that was changed, added, or
         *            removed.
         */
        void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
    }

    /**
     * Interface definition for a callback to be invoked when shared preferences are cleared.
     */
    public interface OnSharedPreferencesClearListener {
        /**
         * Called when shared preferences are cleared via {@link Editor#clear()}.
         *
         * <p>This callback will be run on your main thread.
         * <p><em>Note: This callback will not be triggered when preferences are cleared
         * via {@link Editor#clear()}, unless targeting {@link android.os.Build.VERSION_CODES#R}
         * on devices running OS versions {@link android.os.Build.VERSION_CODES#R Android R}
         * or later.</em>
         *
         * @param sharedPreferences The {@link SharedPreferences} that received the change.
         * @param keys The set of keys that were cleared.
         * @param key The key of the preference that was changed, added, or removed. Apps targeting
         *            {@link android.os.Build.VERSION_CODES#R} on devices running OS versions
         *            {@link android.os.Build.VERSION_CODES#R Android R} or later, will receive
         *            a {@code null} value when preferences are cleared.
         */
        void onSharedPreferencesClear(@NonNull SharedPreferences sharedPreferences,
                @NonNull Set<String> keys);
        void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
    }

    /**
@@ -405,35 +389,4 @@ public interface SharedPreferences {
     * @see #registerOnSharedPreferenceChangeListener
     */
    void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);

    /**
     * Registers a callback to be invoked when preferences are cleared via {@link Editor#clear()}.
     *
     * <p class="caution"><strong>Caution:</strong> The preference manager does
     * not currently store a strong reference to the listener. You must store a
     * strong reference to the listener, or it will be susceptible to garbage
     * collection. We recommend you keep a reference to the listener in the
     * instance data of an object that will exist as long as you need the
     * listener.</p>
     *
     * @param listener The callback that will run.
     * @see #unregisterOnSharedPreferencesClearListener
     */
    default void registerOnSharedPreferencesClearListener(
            @NonNull OnSharedPreferencesClearListener listener) {
        throw new UnsupportedOperationException(
                "registerOnSharedPreferencesClearListener not implemented");
    }

    /**
     * Unregisters a previous callback for {@link Editor#clear()}.
     *
     * @param listener The callback that should be unregistered.
     * @see #registerOnSharedPreferencesClearListener
     */
    default void unregisterOnSharedPreferencesClearListener(
            @NonNull OnSharedPreferencesClearListener listener) {
        throw new UnsupportedOperationException(
                "unregisterOnSharedPreferencesClearListener not implemented");
    }
}