AudioService: Add settings adapter for tests

AudioService currently calls many static methods to read/write settings,
which fail if triggered during unit tests. This CL introduces an adapter
for these methods, allowing more parts of AudioService to be tested.

This CL also grants the MODIFY_AUDIO_ROUTING permission to the tests, as
AudioServiceTest is currently failing without it. It is also required
for tests added in follow-up CLs.

Bug: 201781907
Test: make; atest AudioServiceTest
Change-Id: Ie438495309d14821824a88e6b68f7e6d9692b06b
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d01be58..d80a07a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -225,6 +225,7 @@
 
     private final AudioSystemAdapter mAudioSystem;
     private final SystemServerAdapter mSystemServer;
+    private final SettingsAdapter mSettings;
 
     /** Debug audio mode */
     protected static final boolean DEBUG_MODE = false;
@@ -875,11 +876,12 @@
     /** @hide */
     public AudioService(Context context) {
         this(context, AudioSystemAdapter.getDefaultAdapter(),
-                SystemServerAdapter.getDefaultAdapter(context));
+                SystemServerAdapter.getDefaultAdapter(context),
+                SettingsAdapter.getDefaultAdapter());
     }
 
     public AudioService(Context context, AudioSystemAdapter audioSystem,
-            SystemServerAdapter systemServer) {
+            SystemServerAdapter systemServer, SettingsAdapter settings) {
         sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
         mContext = context;
         mContentResolver = context.getContentResolver();
@@ -887,6 +889,7 @@
 
         mAudioSystem = audioSystem;
         mSystemServer = systemServer;
+        mSettings = settings;
 
         mPlatformType = AudioSystem.getPlatformType(context);
 
@@ -1023,7 +1026,7 @@
                 new String("AudioService ctor"),
                 0);
 
-        mSafeMediaVolumeState = Settings.Global.getInt(mContentResolver,
+        mSafeMediaVolumeState = mSettings.getGlobalInt(mContentResolver,
                                             Settings.Global.AUDIO_SAFE_VOLUME_STATE,
                                             SAFE_MEDIA_VOLUME_NOT_CONFIGURED);
         // The default safe volume index read here will be replaced by the actual value when
@@ -2008,7 +2011,7 @@
 
     private void readDockAudioSettings(ContentResolver cr)
     {
-        mDockAudioMediaEnabled = Settings.Global.getInt(
+        mDockAudioMediaEnabled = mSettings.getGlobalInt(
                                         cr, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0) == 1;
 
         sendMsg(mAudioHandler,
@@ -2024,7 +2027,7 @@
 
     private void updateMasterMono(ContentResolver cr)
     {
-        final boolean masterMono = System.getIntForUser(
+        final boolean masterMono = mSettings.getSystemIntForUser(
                 cr, System.MASTER_MONO, 0 /* default */, UserHandle.USER_CURRENT) == 1;
         if (DEBUG_VOL) {
             Log.d(TAG, String.format("Master mono %b", masterMono));
@@ -2045,7 +2048,7 @@
 
     private void sendEncodedSurroundMode(ContentResolver cr, String eventSource)
     {
-        final int encodedSurroundMode = Settings.Global.getInt(
+        final int encodedSurroundMode = mSettings.getGlobalInt(
                 cr, Settings.Global.ENCODED_SURROUND_OUTPUT,
                 Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
         sendEncodedSurroundMode(encodedSurroundMode, eventSource);
@@ -2161,7 +2164,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mSettingsLock) {
-                Settings.Global.putString(mContentResolver,
+                mSettings.putGlobalString(mContentResolver,
                         Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
                         TextUtils.join(",", enabledFormats));
             }
@@ -2182,7 +2185,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mSettingsLock) {
-                Settings.Global.putInt(mContentResolver,
+                mSettings.putGlobalInt(mContentResolver,
                         Settings.Global.ENCODED_SURROUND_OUTPUT,
                         toEncodedSurroundSetting(mode));
             }
@@ -2203,7 +2206,7 @@
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mSettingsLock) {
-                int encodedSurroundSetting = Settings.Global.getInt(mContentResolver,
+                int encodedSurroundSetting = mSettings.getGlobalInt(mContentResolver,
                         Settings.Global.ENCODED_SURROUND_OUTPUT,
                         AudioManager.ENCODED_SURROUND_OUTPUT_AUTO);
                 return toEncodedSurroundOutputMode(encodedSurroundSetting, targetSdkVersion);
@@ -2216,7 +2219,7 @@
     /** @return the formats that are enabled in global settings */
     private HashSet<Integer> getEnabledFormats() {
         HashSet<Integer> formats = new HashSet<>();
-        String enabledFormats = Settings.Global.getString(mContentResolver,
+        String enabledFormats = mSettings.getGlobalString(mContentResolver,
                 Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
         if (enabledFormats != null) {
             try {
@@ -2279,7 +2282,7 @@
             // Manually enable surround formats only when the setting is in manual mode.
             return;
         }
-        String enabledSurroundFormats = Settings.Global.getString(
+        String enabledSurroundFormats = mSettings.getGlobalString(
                 cr, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
         if (enabledSurroundFormats == null) {
             // Never allow enabledSurroundFormats as a null, which could happen when
@@ -2307,7 +2310,7 @@
         }
         // Set filtered surround formats to settings DB in case
         // there are invalid surround formats in original settings.
-        Settings.Global.putString(mContext.getContentResolver(),
+        mSettings.putGlobalString(mContext.getContentResolver(),
                 Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
                 TextUtils.join(",", formats));
         sendMsg(mAudioHandler, MSG_ENABLE_SURROUND_FORMATS, SENDMSG_QUEUE, 0, 0, formats, 0);
@@ -2335,11 +2338,11 @@
             packageName = mRoleObserver.getAssistantRoleHolder();
         }
         if (TextUtils.isEmpty(packageName)) {
-            String assistantName = Settings.Secure.getStringForUser(
+            String assistantName = mSettings.getSecureStringForUser(
                             mContentResolver,
                             Settings.Secure.VOICE_INTERACTION_SERVICE, UserHandle.USER_CURRENT);
             if (TextUtils.isEmpty(assistantName)) {
-                assistantName = Settings.Secure.getStringForUser(
+                assistantName = mSettings.getSecureStringForUser(
                         mContentResolver,
                         Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT);
             }
@@ -2382,7 +2385,7 @@
         final ContentResolver cr = mContentResolver;
 
         int ringerModeFromSettings =
-                Settings.Global.getInt(
+                mSettings.getGlobalInt(
                         cr, Settings.Global.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
         int ringerMode = ringerModeFromSettings;
         // validity check in case the settings are restored from a device with incompatible
@@ -2394,7 +2397,7 @@
             ringerMode = AudioManager.RINGER_MODE_SILENT;
         }
         if (ringerMode != ringerModeFromSettings) {
-            Settings.Global.putInt(cr, Settings.Global.MODE_RINGER, ringerMode);
+            mSettings.putGlobalInt(cr, Settings.Global.MODE_RINGER, ringerMode);
         }
         if (mUseFixedVolume || mIsSingleVolume) {
             ringerMode = AudioManager.RINGER_MODE_NORMAL;
@@ -2426,7 +2429,7 @@
             AudioSystem.setRttEnabled(mRttEnabled);
         }
 
-        mMuteAffectedStreams = System.getIntForUser(cr,
+        mMuteAffectedStreams = mSettings.getSystemIntForUser(cr,
                 System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED,
                 UserHandle.USER_CURRENT);
 
@@ -4479,7 +4482,7 @@
         int silenceRingerSetting = Settings.Secure.VOLUME_HUSH_OFF;
         if (mContext.getResources()
                 .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
-            silenceRingerSetting = Settings.Secure.getIntForUser(mContentResolver,
+            silenceRingerSetting = mSettings.getSecureIntForUser(mContentResolver,
                     Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
                     UserHandle.USER_CURRENT);
         }
@@ -5240,7 +5243,7 @@
      * Settings has an in memory cache, so this is fast.
      */
     private boolean querySoundEffectsEnabled(int user) {
-        return Settings.System.getIntForUser(getContentResolver(),
+        return mSettings.getSystemIntForUser(getContentResolver(),
                 Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
     }
 
@@ -5325,7 +5328,7 @@
         checkMuteAffectedStreams();
 
         synchronized (mSafeMediaVolumeStateLock) {
-            mMusicActiveMs = MathUtils.constrain(Settings.Secure.getIntForUser(mContentResolver,
+            mMusicActiveMs = MathUtils.constrain(mSettings.getSecureIntForUser(mContentResolver,
                     Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, 0, UserHandle.USER_CURRENT),
                     0, UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX);
             if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) {
@@ -5967,7 +5970,7 @@
     @GuardedBy("mSettingsLock")
     private boolean updateRingerAndZenModeAffectedStreams() {
         boolean updatedZenModeAffectedStreams = updateZenModeAffectedStreams();
-        int ringerModeAffectedStreams = Settings.System.getIntForUser(mContentResolver,
+        int ringerModeAffectedStreams = mSettings.getSystemIntForUser(mContentResolver,
                 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
                 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
                  (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
@@ -5991,7 +5994,7 @@
         }
 
         if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
-            Settings.System.putIntForUser(mContentResolver,
+            mSettings.putSystemIntForUser(mContentResolver,
                     Settings.System.MODE_RINGER_STREAMS_AFFECTED,
                     ringerModeAffectedStreams,
                     UserHandle.USER_CURRENT);
@@ -6968,7 +6971,7 @@
                         + ", device " + AudioSystem.getOutputDeviceName(device)
                         + " and User=" + ActivityManager.getCurrentUser());
             }
-            boolean success = Settings.System.putIntForUser(mContentResolver,
+            boolean success = mSettings.putSystemIntForUser(mContentResolver,
                     getSettingNameForDevice(device),
                     getIndex(device),
                     UserHandle.USER_CURRENT);
@@ -6994,7 +6997,7 @@
                             ? AudioSystem.DEFAULT_STREAM_VOLUME[mPublicStreamType] : -1;
                     int index;
                     String name = getSettingNameForDevice(device);
-                    index = Settings.System.getIntForUser(
+                    index = mSettings.getSystemIntForUser(
                             mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
                     if (index == -1) {
                         continue;
@@ -7243,7 +7246,7 @@
                         index = defaultIndex;
                     } else {
                         String name = getSettingNameForDevice(device);
-                        index = Settings.System.getIntForUser(
+                        index = mSettings.getSystemIntForUser(
                                 mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
                     }
                     if (index == -1) {
@@ -7780,7 +7783,7 @@
                 return;
             }
             if (streamState.hasValidSettingsName()) {
-                System.putIntForUser(mContentResolver,
+                mSettings.putSystemIntForUser(mContentResolver,
                         streamState.getSettingNameForDevice(device),
                         (streamState.getIndex(device) + 5)/ 10,
                         UserHandle.USER_CURRENT);
@@ -7791,11 +7794,11 @@
             if (mUseFixedVolume) {
                 return;
             }
-            Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
+            mSettings.putGlobalInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
         }
 
         private void onPersistSafeVolumeState(int state) {
-            Settings.Global.putInt(mContentResolver,
+            mSettings.putGlobalInt(mContentResolver,
                     Settings.Global.AUDIO_SAFE_VOLUME_STATE,
                     state);
         }
@@ -7939,7 +7942,7 @@
 
                 case MSG_PERSIST_MUSIC_ACTIVE_MS:
                     final int musicActiveMs = msg.arg1;
-                    Settings.Secure.putIntForUser(mContentResolver,
+                    mSettings.putSecureIntForUser(mContentResolver,
                             Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS, musicActiveMs,
                             UserHandle.USER_CURRENT);
                     break;
@@ -8080,12 +8083,12 @@
             mContentResolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.MASTER_BALANCE), false, this);
 
-            mEncodedSurroundMode = Settings.Global.getInt(
+            mEncodedSurroundMode = mSettings.getGlobalInt(
                     mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT,
                     Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
             mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.ENCODED_SURROUND_OUTPUT), false, this);
-            mEnabledSurroundFormats = Settings.Global.getString(
+            mEnabledSurroundFormats = mSettings.getGlobalString(
                     mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
             mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this);
@@ -8119,7 +8122,7 @@
         }
 
         private void updateEncodedSurroundOutput() {
-            int newSurroundMode = Settings.Global.getInt(
+            int newSurroundMode = mSettings.getGlobalInt(
                 mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT,
                 Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
             // Did it change?
@@ -8662,13 +8665,13 @@
     }
 
     void onPersistSpatialAudioEnabled(boolean enabled) {
-        Settings.Secure.putIntForUser(mContentResolver,
+        mSettings.putSecureIntForUser(mContentResolver,
                 Settings.Secure.SPATIAL_AUDIO_ENABLED, enabled ? 1 : 0,
                 UserHandle.USER_CURRENT);
     }
 
     boolean isSpatialAudioEnabled() {
-        return Settings.Secure.getIntForUser(mContentResolver,
+        return mSettings.getSecureIntForUser(mContentResolver,
                 Settings.Secure.SPATIAL_AUDIO_ENABLED, SPATIAL_AUDIO_ENABLED_DEFAULT ? 1 : 0,
                 UserHandle.USER_CURRENT) == 1;
     }
@@ -9793,7 +9796,7 @@
         }
 
         public void loadSettings(ContentResolver cr) {
-            mLongPressTimeout = Settings.Secure.getIntForUser(cr,
+            mLongPressTimeout = mSettings.getSecureIntForUser(cr,
                     Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
         }
 
@@ -11417,7 +11420,7 @@
         }
         final long callingIdentity = Binder.clearCallingIdentity();
         try {
-            System.putIntForUser(mContentResolver,
+            mSettings.putSystemIntForUser(mContentResolver,
                     getSettingsNameForDeviceVolumeBehavior(deviceType),
                     deviceVolumeBehavior,
                     UserHandle.USER_CURRENT);
@@ -11428,7 +11431,7 @@
 
     @AudioManager.DeviceVolumeBehaviorState
     private int retrieveStoredDeviceVolumeBehavior(int deviceType) {
-        return System.getIntForUser(mContentResolver,
+        return mSettings.getSystemIntForUser(mContentResolver,
                 getSettingsNameForDeviceVolumeBehavior(deviceType),
                 AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET,
                 UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/audio/SettingsAdapter.java b/services/core/java/com/android/server/audio/SettingsAdapter.java
new file mode 100644
index 0000000..dbc4d6d
--- /dev/null
+++ b/services/core/java/com/android/server/audio/SettingsAdapter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+
+/**
+ * Adapter for methods that read and write settings in android.provider.Settings.
+ */
+public class SettingsAdapter {
+    public static SettingsAdapter getDefaultAdapter() {
+        return new SettingsAdapter();
+    }
+
+    /**
+     * Wrapper methods for Settings.Global
+     */
+
+    /** Wraps {@link Settings.Global#getInt(ContentResolver, String, int)} */
+    public int getGlobalInt(ContentResolver cr, String name, int def) {
+        return Settings.Global.getInt(cr, name, def);
+    }
+
+    /** Wraps {@link Settings.Global#getString(ContentResolver, String)} */
+    public String getGlobalString(ContentResolver resolver, String name) {
+        return Settings.Global.getString(resolver, name);
+    }
+
+    /** Wraps {@link Settings.Global#putInt(ContentResolver, String, int)} */
+    public boolean putGlobalInt(ContentResolver cr, String name, int value) {
+        return Settings.Global.putInt(cr, name, value);
+    }
+
+    /** Wraps {@link Settings.Global#putString(ContentResolver, String, String)} */
+    public boolean putGlobalString(ContentResolver resolver, String name, String value) {
+        return Settings.Global.putString(resolver, name, value);
+    }
+
+    /**
+     * Wrapper methods for Settings.System
+     */
+
+    /** Wraps {@link Settings.System#getIntForUser(ContentResolver, String, int, int)} */
+    public int getSystemIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+        return Settings.System.getIntForUser(cr, name, def, userHandle);
+    }
+
+    /** Wraps {@link Settings.System#putIntForUser(ContentResolver, String, int, int)} */
+    public boolean putSystemIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+        return Settings.System.putIntForUser(cr, name, value, userHandle);
+    }
+
+    /**
+     * Wrapper methods for Settings.Secure
+     */
+
+    /** Wraps {@link Settings.Secure#getIntForUser(ContentResolver, String, int, int)} */
+    public int getSecureIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+        return Settings.Secure.getIntForUser(cr, name, def, userHandle);
+    }
+
+    /** Wraps {@link Settings.Secure#getStringForUser(ContentResolver, String, int)} */
+    public String getSecureStringForUser(ContentResolver resolver, String name, int userHandle) {
+        return Settings.Secure.getStringForUser(resolver, name, userHandle);
+    }
+
+    /** Wraps {@link Settings.Secure#putIntForUser(ContentResolver, String, int, int)} */
+    public boolean putSecureIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+        return Settings.Secure.putIntForUser(cr, name, value, userHandle);
+    }
+
+    /** Wraps {@link Settings.Secure#putStringForUser(ContentResolver, String, String, int)} */
+    public boolean putSecureStringForUser(ContentResolver cr, String name, String value,
+            int userHandle) {
+        return Settings.Secure.putStringForUser(cr, name, value, userHandle);
+    }
+}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index a6194df..449177e 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -100,7 +100,7 @@
         android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
     <uses-permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY" />
     <uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" />
-
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
     <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
 
     <queries>
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index c0ba3c7..5b6aebc 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -45,6 +45,7 @@
     private Context mContext;
     private AudioSystemAdapter mAudioSystem;
     @Spy private SystemServerAdapter mSpySystemServer;
+    private SettingsAdapter mSettingsAdapter;
     // the class being unit-tested here
     private AudioService mAudioService;
 
@@ -59,7 +60,9 @@
         mContext = InstrumentationRegistry.getTargetContext();
         mAudioSystem = new NoOpAudioSystemAdapter();
         mSpySystemServer = spy(new NoOpSystemServerAdapter());
-        mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer);
+        mSettingsAdapter = new NoOpSettingsAdapter();
+        mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer,
+                mSettingsAdapter);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpSettingsAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpSettingsAdapter.java
new file mode 100644
index 0000000..bf80f27b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpSettingsAdapter.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import android.content.ContentResolver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class NoOpSettingsAdapter extends SettingsAdapter {
+
+    /**
+     * No-op methods for Settings.Global
+     */
+
+    private Map<String, Integer> mGlobalIntSettings = new HashMap<>();
+    private Map<String, String> mGlobalStringSettings = new HashMap<>();
+
+    @Override
+    public int getGlobalInt(ContentResolver cr, String name, int def) {
+        return mGlobalIntSettings.getOrDefault(name, def);
+    }
+
+    @Override
+    public String getGlobalString(ContentResolver resolver, String name) {
+        return mGlobalStringSettings.getOrDefault(name, null);
+    }
+
+    @Override
+    public boolean putGlobalInt(ContentResolver cr, String name, int value) {
+        mGlobalIntSettings.put(name, value);
+        return true;
+    }
+
+    @Override
+    public boolean putGlobalString(ContentResolver resolver, String name, String value) {
+        mGlobalStringSettings.put(name, value);
+        return true;
+    }
+
+    /**
+     * No-op methods for Settings.System
+     */
+
+    private Map<String, Integer> mSystemIntSettings = new HashMap<>();
+
+    @Override
+    public int getSystemIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+        return mSystemIntSettings.getOrDefault(name, def);
+    }
+
+    @Override
+    public boolean putSystemIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+        mSystemIntSettings.put(name, value);
+        return true;
+    }
+
+    /**
+     * No-op methods for Settings.Secure
+     */
+
+    private Map<String, Integer> mSecureIntSettings = new HashMap<>();
+    private Map<String, String> mSecureStringSettings = new HashMap<>();
+
+    @Override
+    public int getSecureIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+        return mSecureIntSettings.getOrDefault(name, def);
+    }
+
+    @Override
+    public String getSecureStringForUser(ContentResolver resolver, String name, int userHandle) {
+        return mSecureStringSettings.getOrDefault(name, null);
+    }
+
+    @Override
+    public boolean putSecureIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+        mSecureIntSettings.put(name, value);
+        return true;
+    }
+
+    @Override
+    public boolean putSecureStringForUser(ContentResolver cr, String name, String value,
+            int userHandle) {
+        mSecureStringSettings.put(name, value);
+        return true;
+    }
+}
+